From 935639e5e0ab828e7e3795095adbff28a14c817e Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Tue, 6 Nov 2018 04:09:28 -0500 Subject: [PATCH] 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 --- src/panels/lovelace/cards/hui-glance-card.ts | 30 ++-- .../lovelace/editor/hui-dialog-edit-card.ts | 152 +++++++++++++++--- .../lovelace/editor/hui-glance-card-editor.ts | 79 +++++++++ .../lovelace/editor/hui-yaml-card-preview.ts | 17 +- src/panels/lovelace/editor/hui-yaml-editor.ts | 15 +- src/panels/lovelace/editor/types.ts | 12 ++ src/panels/lovelace/types.ts | 5 + 7 files changed, 260 insertions(+), 50 deletions(-) create mode 100644 src/panels/lovelace/editor/hui-glance-card-editor.ts create mode 100644 src/panels/lovelace/editor/types.ts diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts index 261ad84dfa..df27398474 100644 --- a/src/panels/lovelace/cards/hui-glance-card.ts +++ b/src/panels/lovelace/cards/hui-glance-card.ts @@ -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 { + await import("../editor/hui-glance-card-editor"); + return document.createElement("hui-glance-card-editor"); + } + public hass?: HomeAssistant; private _config?: Config; private _configEntities?: EntityConfig[]; diff --git a/src/panels/lovelace/editor/hui-dialog-edit-card.ts b/src/panels/lovelace/editor/hui-dialog-edit-card.ts index bc4b2eea0c..c34288d621 100644 --- a/src/panels/lovelace/editor/hui-dialog-edit-card.ts +++ b/src/panels/lovelace/editor/hui-dialog-edit-card.ts @@ -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`

Card Configuration

- + ${ + this._editorToggle && this._configElement !== null + ? html`
${when( + this._configElement, + () => this._configElement, + () => html`Loading...` + )}
` + : html` + ` + }
- Cancel - Save + Toggle Editor + Cancel + Save
`; } - 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 { + const cardConfig = await getCardConfig(this.hass!, this._cardId!); + this._configValue = { + format: "yaml", + value: cardConfig, + }; + this._originalConfigYaml = cardConfig; + } + + private async _loadConfigElement(): Promise { + 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 { + 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) { diff --git a/src/panels/lovelace/editor/hui-glance-card-editor.ts b/src/panels/lovelace/editor/hui-glance-card-editor.ts new file mode 100644 index 0000000000..328e2a7fa5 --- /dev/null +++ b/src/panels/lovelace/editor/hui-glance-card-editor.ts @@ -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` +
+ Show Entity's Name?

+ Show Entity's State Text?
+ `; + } + + 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); diff --git a/src/panels/lovelace/editor/hui-yaml-card-preview.ts b/src/panels/lovelace/editor/hui-yaml-card-preview.ts index 3ca81c2c5f..c187c2c496 100644 --- a/src/panels/lovelace/editor/hui-yaml-card-preview.ts +++ b/src/panels/lovelace/editor/hui-yaml-card-preview.ts @@ -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); diff --git a/src/panels/lovelace/editor/hui-yaml-editor.ts b/src/panels/lovelace/editor/hui-yaml-editor.ts index e1977849c7..3191f8de2b 100644 --- a/src/panels/lovelace/editor/hui-yaml-editor.ts +++ b/src/panels/lovelace/editor/hui-yaml-editor.ts @@ -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` `; } - 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 }); } } diff --git a/src/panels/lovelace/editor/types.ts b/src/panels/lovelace/editor/types.ts new file mode 100644 index 0000000000..c585cad86f --- /dev/null +++ b/src/panels/lovelace/editor/types.ts @@ -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; +} diff --git a/src/panels/lovelace/types.ts b/src/panels/lovelace/types.ts index def084c78b..32905d0534 100644 --- a/src/panels/lovelace/types.ts +++ b/src/panels/lovelace/types.ts @@ -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; +}