mirror of
https://github.com/home-assistant/frontend.git
synced 2025-04-24 13:27:22 +00:00
Add getElementConfig to Glance + Add Form UI for updating YAML (#1944)
* Working version * Working kind of * Some more changes * More review changes * Progress * Review updates * Adding new changes * Remove un-needed code * Adding Types * Updating UI Editor a bit * Updating from missed reviews * Updates from Reviews * Yaml is not update each time. Instead stored as LovelaceConfig. * Update to not pull config from preview but store it each time it changed * Updating from Reviews * Try catch fix * Update hui-dialog-edit-card.ts
This commit is contained in:
parent
cdb2093ea6
commit
935639e5e0
@ -7,24 +7,23 @@ import {
|
||||
import { TemplateResult } from "lit-html";
|
||||
import { classMap } from "lit-html/directives/classMap";
|
||||
|
||||
import computeStateDisplay from "../../../common/entity/compute_state_display";
|
||||
import computeStateName from "../../../common/entity/compute_state_name";
|
||||
import processConfigEntities from "../common/process-config-entities";
|
||||
import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
|
||||
import { fireEvent } from "../../../common/dom/fire_event.js";
|
||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../types.js";
|
||||
import { LovelaceCard, LovelaceConfig, LovelaceCardEditor } from "../types.js";
|
||||
import { longPress } from "../common/directives/long-press-directive";
|
||||
|
||||
import toggleEntity from "../common/entity/toggle-entity";
|
||||
import computeStateDisplay from "../../../common/entity/compute_state_display.js";
|
||||
import computeStateName from "../../../common/entity/compute_state_name.js";
|
||||
import processConfigEntities from "../common/process-config-entities";
|
||||
import applyThemesOnElement from "../../../common/dom/apply_themes_on_element.js";
|
||||
import toggleEntity from "../common/entity/toggle-entity.js";
|
||||
|
||||
import "../../../components/entity/state-badge";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon";
|
||||
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCard, LovelaceConfig } from "../types";
|
||||
import { longPress } from "../common/directives/long-press-directive";
|
||||
|
||||
interface EntityConfig {
|
||||
export interface EntityConfig {
|
||||
name: string;
|
||||
icon: string;
|
||||
entity: string;
|
||||
@ -34,7 +33,7 @@ interface EntityConfig {
|
||||
service_data?: object;
|
||||
}
|
||||
|
||||
interface Config extends LovelaceConfig {
|
||||
export interface Config extends LovelaceConfig {
|
||||
show_name?: boolean;
|
||||
show_state?: boolean;
|
||||
title?: string;
|
||||
@ -45,6 +44,11 @@ interface Config extends LovelaceConfig {
|
||||
|
||||
export class HuiGlanceCard extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCard {
|
||||
public static async getConfigElement(): Promise<LovelaceCardEditor> {
|
||||
await import("../editor/hui-glance-card-editor");
|
||||
return document.createElement("hui-glance-card-editor");
|
||||
}
|
||||
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
private _configEntities?: EntityConfig[];
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import yaml from "js-yaml";
|
||||
import { when } from "lit-html/directives/when";
|
||||
import { TemplateResult } from "lit-html";
|
||||
|
||||
import "@polymer/paper-button/paper-button";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
@ -10,18 +12,26 @@ import "@polymer/paper-dialog/paper-dialog";
|
||||
import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { getCardConfig, updateCardConfig } from "../common/data";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
|
||||
import "./hui-yaml-editor";
|
||||
import "./hui-yaml-card-preview";
|
||||
// This is not a duplicate import, one is for types, one is for element.
|
||||
// tslint:disable-next-line
|
||||
import { HuiYAMLCardPreview } from "./hui-yaml-card-preview";
|
||||
import { LovelaceCardEditor, LovelaceConfig } from "../types";
|
||||
import { YamlChangedEvent, ConfigValue } from "./types";
|
||||
|
||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
||||
|
||||
export class HuiDialogEditCard extends LitElement {
|
||||
protected hass?: HomeAssistant;
|
||||
private _cardId?: string;
|
||||
private _cardConfig?: string;
|
||||
private _originalConfigYaml?: string;
|
||||
private _configElement?: LovelaceCardEditor | null;
|
||||
private _reloadLovelace?: () => void;
|
||||
private _editorToggle?: boolean;
|
||||
private _configValue?: ConfigValue;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
@ -29,8 +39,9 @@ export class HuiDialogEditCard extends LitElement {
|
||||
cardId: {
|
||||
type: Number,
|
||||
},
|
||||
_cardConfig: {},
|
||||
_dialogClosedCallback: {},
|
||||
_configElement: {},
|
||||
_editorToggle: {},
|
||||
};
|
||||
}
|
||||
|
||||
@ -38,8 +49,10 @@ export class HuiDialogEditCard extends LitElement {
|
||||
this.hass = hass;
|
||||
this._cardId = cardId;
|
||||
this._reloadLovelace = reloadLovelace;
|
||||
this._cardConfig = "";
|
||||
this._loadConfig();
|
||||
this._editorToggle = true;
|
||||
this._configElement = undefined;
|
||||
this._configValue = { format: "yaml", value: "" };
|
||||
this._loadConfig().then(() => this._loadConfigElement());
|
||||
// Wait till dialog is rendered.
|
||||
await this.updateComplete;
|
||||
this._dialog.open();
|
||||
@ -53,58 +66,147 @@ export class HuiDialogEditCard extends LitElement {
|
||||
return this.shadowRoot!.querySelector("hui-yaml-card-preview")!;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
paper-dialog {
|
||||
width: 650px;
|
||||
}
|
||||
.element-editor {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
<paper-dialog with-backdrop>
|
||||
<h2>Card Configuration</h2>
|
||||
<paper-dialog-scrollable>
|
||||
<hui-yaml-editor
|
||||
.yaml="${this._cardConfig}"
|
||||
@yaml-changed="${this._handleYamlChanged}"
|
||||
></hui-yaml-editor>
|
||||
${
|
||||
this._editorToggle && this._configElement !== null
|
||||
? html`<div class="element-editor">${when(
|
||||
this._configElement,
|
||||
() => this._configElement,
|
||||
() => html`Loading...`
|
||||
)}</div>`
|
||||
: html`
|
||||
<hui-yaml-editor
|
||||
.yaml="${this._configValue!.value}"
|
||||
@yaml-changed="${this._handleYamlChanged}"
|
||||
></hui-yaml-editor>`
|
||||
}
|
||||
<hui-yaml-card-preview
|
||||
.hass="${this.hass}"
|
||||
.yaml="${this._cardConfig}"
|
||||
.value="${this._configValue}"
|
||||
></hui-yaml-card-preview>
|
||||
</paper-dialog-scrollable>
|
||||
<div class="paper-dialog-buttons">
|
||||
<paper-button @click="${this._closeDialog}">Cancel</paper-button>
|
||||
<paper-button @click="${this._updateConfig}">Save</paper-button>
|
||||
<paper-button
|
||||
@click="${this._toggleEditor}"
|
||||
>Toggle Editor</paper-button>
|
||||
<paper-button
|
||||
@click="${this._closeDialog}"
|
||||
>Cancel</paper-button>
|
||||
<paper-button
|
||||
@click="${this._updateConfigInBackend}"'
|
||||
>Save</paper-button>
|
||||
</div>
|
||||
</paper-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleYamlChanged(ev) {
|
||||
this._previewEl.yaml = ev.detail.yaml;
|
||||
private _handleYamlChanged(ev: YamlChangedEvent): void {
|
||||
this._configValue = { format: "yaml", value: ev.detail.yaml };
|
||||
this._updatePreview(this._configValue);
|
||||
}
|
||||
|
||||
private _closeDialog() {
|
||||
private _handleJSConfigChanged(value: LovelaceConfig): void {
|
||||
this._configElement!.setConfig(value);
|
||||
this._configValue = { format: "js", value };
|
||||
this._updatePreview(this._configValue);
|
||||
}
|
||||
|
||||
private _updatePreview(value: ConfigValue) {
|
||||
if (!this._previewEl) {
|
||||
return;
|
||||
}
|
||||
this._previewEl.value = value;
|
||||
}
|
||||
|
||||
private _closeDialog(): void {
|
||||
this._dialog.close();
|
||||
}
|
||||
|
||||
private async _loadConfig() {
|
||||
this._cardConfig = await getCardConfig(this.hass!, this._cardId!);
|
||||
await this.updateComplete;
|
||||
// This will center the dialog with the updated config
|
||||
private _toggleEditor(): void {
|
||||
if (this._editorToggle && this._configValue!.format === "js") {
|
||||
this._configValue = {
|
||||
format: "yaml",
|
||||
value: yaml.safeDump(this._configValue!.value),
|
||||
};
|
||||
} else if (this._configElement && this._configValue!.format === "yaml") {
|
||||
this._configValue = {
|
||||
format: "js",
|
||||
value: yaml.safeLoad(this._configValue!.value),
|
||||
};
|
||||
this._configElement.setConfig(this._configValue!.value as LovelaceConfig);
|
||||
}
|
||||
this._editorToggle = !this._editorToggle;
|
||||
}
|
||||
|
||||
private async _loadConfig(): Promise<void> {
|
||||
const cardConfig = await getCardConfig(this.hass!, this._cardId!);
|
||||
this._configValue = {
|
||||
format: "yaml",
|
||||
value: cardConfig,
|
||||
};
|
||||
this._originalConfigYaml = cardConfig;
|
||||
}
|
||||
|
||||
private async _loadConfigElement(): Promise<void> {
|
||||
const conf = yaml.safeLoad(this._configValue!.value);
|
||||
|
||||
const tag = conf.type.startsWith(CUSTOM_TYPE_PREFIX)
|
||||
? conf.type.substr(CUSTOM_TYPE_PREFIX.length)
|
||||
: `hui-${conf.type}-card`;
|
||||
|
||||
const elClass = customElements.get(tag);
|
||||
let configElement;
|
||||
|
||||
try {
|
||||
configElement = await elClass.getConfigElement();
|
||||
} catch (err) {
|
||||
this._configElement = null;
|
||||
return;
|
||||
}
|
||||
|
||||
configElement.setConfig(conf);
|
||||
configElement.hass = this.hass;
|
||||
configElement.addEventListener("config-changed", (ev) =>
|
||||
this._handleJSConfigChanged(ev.detail.config)
|
||||
);
|
||||
this._configValue = { format: "js", value: conf };
|
||||
this._configElement = configElement;
|
||||
|
||||
// This will center the dialog with the updated config Element
|
||||
fireEvent(this._dialog, "iron-resize");
|
||||
}
|
||||
|
||||
private async _updateConfig() {
|
||||
const newCardConfig = this.shadowRoot!.querySelector("hui-yaml-editor")!
|
||||
.yaml;
|
||||
private async _updateConfigInBackend(): Promise<void> {
|
||||
if (this._configValue!.format === "js") {
|
||||
this._configValue = {
|
||||
format: "yaml",
|
||||
value: yaml.safeDump(this._configValue!.value),
|
||||
};
|
||||
}
|
||||
|
||||
if (this._cardConfig === newCardConfig) {
|
||||
if (this._configValue!.value === this._originalConfigYaml) {
|
||||
this._dialog.close();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await updateCardConfig(this.hass!, this._cardId!, newCardConfig);
|
||||
await updateCardConfig(
|
||||
this.hass!,
|
||||
this._cardId!,
|
||||
this._configValue!.value
|
||||
);
|
||||
this._dialog.close();
|
||||
this._reloadLovelace!();
|
||||
} catch (err) {
|
||||
|
79
src/panels/lovelace/editor/hui-glance-card-editor.ts
Normal file
79
src/panels/lovelace/editor/hui-glance-card-editor.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-checkbox/paper-checkbox.js";
|
||||
|
||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||
import { HomeAssistant } from "../../../types.js";
|
||||
import { LovelaceCardEditor } from "../types.js";
|
||||
import { fireEvent } from "../../../common/dom/fire_event.js";
|
||||
import { Config } from "../cards/hui-glance-card";
|
||||
|
||||
import "../../../components/entity/state-badge.js";
|
||||
import "../../../components/entity/ha-entity-picker";
|
||||
import "../../../components/ha-card.js";
|
||||
import "../../../components/ha-icon.js";
|
||||
|
||||
export class HuiGlanceCardEditor extends hassLocalizeLitMixin(LitElement)
|
||||
implements LovelaceCardEditor {
|
||||
public hass?: HomeAssistant;
|
||||
private _config?: Config;
|
||||
|
||||
static get properties(): PropertyDeclarations {
|
||||
return {
|
||||
hass: {},
|
||||
_config: {},
|
||||
};
|
||||
}
|
||||
|
||||
public setConfig(config: Config): void {
|
||||
this._config = { type: "glance", ...config };
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.hass) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<paper-input
|
||||
label="Title"
|
||||
value="${this._config!.title}"
|
||||
.configValue=${"title"}
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-input><br>
|
||||
<paper-checkbox
|
||||
?checked="${this._config!.show_name !== false}"
|
||||
.configValue=${"show_name"}
|
||||
@change="${this._valueChanged}"
|
||||
>Show Entity's Name?</paper-checkbox><br><br>
|
||||
<paper-checkbox
|
||||
?checked="${this._config!.show_state !== false}"
|
||||
.configValue=${"show_state"}
|
||||
@change="${this._valueChanged}"
|
||||
>Show Entity's State Text?</paper-checkbox><br>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: MouseEvent): void {
|
||||
if (!this._config || !this.hass) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = ev.target! as any;
|
||||
|
||||
const newValue =
|
||||
target.checked !== undefined ? target.checked : target.value;
|
||||
|
||||
fireEvent(this, "config-changed", {
|
||||
config: { ...this._config, [target.configValue]: newValue },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-glance-card-editor": HuiGlanceCardEditor;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("hui-glance-card-editor", HuiGlanceCardEditor);
|
@ -6,6 +6,7 @@ import createCardElement from "../common/create-card-element";
|
||||
import createErrorCardConfig from "../common/create-error-card-config";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCard } from "../types";
|
||||
import { ConfigValue } from "./types";
|
||||
|
||||
export class HuiYAMLCardPreview extends HTMLElement {
|
||||
private _hass?: HomeAssistant;
|
||||
@ -17,20 +18,24 @@ export class HuiYAMLCardPreview extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
set yaml(value: string) {
|
||||
set value(configValue: ConfigValue) {
|
||||
if (this.lastChild) {
|
||||
this.removeChild(this.lastChild);
|
||||
}
|
||||
|
||||
if (value === "") {
|
||||
if (!configValue.value || configValue.value === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
let conf;
|
||||
try {
|
||||
conf = yaml.safeLoad(value);
|
||||
} catch (err) {
|
||||
conf = createErrorCardConfig(`Invalid YAML: ${err.message}`, undefined);
|
||||
if (configValue.format === "yaml") {
|
||||
try {
|
||||
conf = yaml.safeLoad(configValue.value);
|
||||
} catch (err) {
|
||||
conf = createErrorCardConfig(`Invalid YAML: ${err.message}`, undefined);
|
||||
}
|
||||
} else {
|
||||
conf = configValue.value;
|
||||
}
|
||||
|
||||
const element = createCardElement(conf);
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
|
||||
import { TemplateResult } from "lit-html";
|
||||
import "@polymer/paper-input/paper-textarea";
|
||||
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
|
||||
export class HuiYAMLEditor extends LitElement {
|
||||
public yaml?: string;
|
||||
|
||||
@ -12,7 +13,7 @@ export class HuiYAMLEditor extends LitElement {
|
||||
};
|
||||
}
|
||||
|
||||
protected render() {
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<style>
|
||||
paper-textarea {
|
||||
@ -20,15 +21,17 @@ export class HuiYAMLEditor extends LitElement {
|
||||
}
|
||||
</style>
|
||||
<paper-textarea
|
||||
max-rows=10
|
||||
value="${this.yaml}"
|
||||
@value-changed="${this._valueChanged}"
|
||||
></paper-textarea>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev) {
|
||||
this.yaml = ev.target.value;
|
||||
fireEvent(this, "yaml-changed", { yaml: ev.target.value });
|
||||
private _valueChanged(ev: MouseEvent): void {
|
||||
const target = ev.target! as any;
|
||||
this.yaml = target.value;
|
||||
fireEvent(this, "yaml-changed", { yaml: target.value });
|
||||
}
|
||||
}
|
||||
|
||||
|
12
src/panels/lovelace/editor/types.ts
Normal file
12
src/panels/lovelace/editor/types.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { LovelaceConfig } from "../types";
|
||||
|
||||
export interface YamlChangedEvent extends Event {
|
||||
detail: {
|
||||
yaml: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ConfigValue {
|
||||
format: "js" | "yaml";
|
||||
value: string | LovelaceConfig;
|
||||
}
|
@ -9,3 +9,8 @@ export interface LovelaceCard extends HTMLElement {
|
||||
getCardSize(): number;
|
||||
setConfig(config: LovelaceConfig): void;
|
||||
}
|
||||
|
||||
export interface LovelaceCardEditor extends HTMLElement {
|
||||
hass?: HomeAssistant;
|
||||
setConfig(config: LovelaceConfig): void;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user