diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts index 2386fa8d1a..a40acfda49 100644 --- a/src/panels/lovelace/components/hui-card-options.ts +++ b/src/panels/lovelace/components/hui-card-options.ts @@ -87,7 +87,7 @@ export class HuiCardOptions extends hassLocalizeLitMixin(LitElement) { } return html` ${view.title}${view.title || "Unnamed view"} `; }) diff --git a/src/panels/lovelace/editor/lovelace-editor/hui-dialog-edit-lovelace.ts b/src/panels/lovelace/editor/lovelace-editor/hui-dialog-edit-lovelace.ts new file mode 100644 index 0000000000..2cb86b013b --- /dev/null +++ b/src/panels/lovelace/editor/lovelace-editor/hui-dialog-edit-lovelace.ts @@ -0,0 +1,152 @@ +import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; +import { TemplateResult } from "lit-html"; +import "@polymer/paper-spinner/paper-spinner"; +import "@polymer/paper-dialog/paper-dialog"; +// This is not a duplicate import, one is for types, one is for element. +// tslint:disable-next-line +import { PaperDialogElement } from "@polymer/paper-dialog/paper-dialog"; +import "@polymer/paper-button/paper-button"; +import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable"; +import "./hui-lovelace-editor"; +import { HomeAssistant } from "../../../../types"; +import { LovelaceConfig } from "../../../../data/lovelace"; +import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin"; +import { Lovelace } from "../../types"; + +export class HuiDialogEditLovelace extends hassLocalizeLitMixin(LitElement) { + public hass?: HomeAssistant; + private _lovelace?: Lovelace; + private _config?: LovelaceConfig; + private _saving: boolean; + + static get properties(): PropertyDeclarations { + return { + hass: {}, + _lovelace: {}, + }; + } + + protected constructor() { + super(); + this._saving = false; + } + + public async showDialog(lovelace: Lovelace): Promise { + this._lovelace = lovelace; + if (this._dialog == null) { + await this.updateComplete; + } + + const { views, ...lovelaceConfig } = this._lovelace!.config; + this._config = lovelaceConfig as LovelaceConfig; + + this._dialog.open(); + } + + private get _dialog(): PaperDialogElement { + return this.shadowRoot!.querySelector("paper-dialog")!; + } + + protected render(): TemplateResult { + return html` + ${this.renderStyle()} + +

Edit Lovelace

+ + +
+ ${this.localize("ui.common.cancel")} + + + ${this.localize("ui.common.save")} +
+
+ `; + } + + private _closeDialog(): void { + this._config = undefined; + this._dialog.close(); + } + + private async _save(): Promise { + if (!this._config) { + return; + } + if (!this._isConfigChanged()) { + this._closeDialog(); + return; + } + + this._saving = true; + const lovelace = this._lovelace!; + + const config: LovelaceConfig = { + ...lovelace.config, + ...this._config, + }; + + try { + await lovelace.saveConfig(config); + this._closeDialog(); + } catch (err) { + alert(`Saving failed: ${err.message}`); + } finally { + this._saving = false; + } + } + + private _ConfigChanged(ev: CustomEvent): void { + if (ev.detail && ev.detail.config) { + this._config = ev.detail.config; + } + } + + private _isConfigChanged(): boolean { + const { views, ...lovelaceConfig } = this._lovelace!.config; + return JSON.stringify(this._config) !== JSON.stringify(lovelaceConfig); + } + + private renderStyle(): TemplateResult { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-dialog-edit-lovelace": HuiDialogEditLovelace; + } +} + +customElements.define("hui-dialog-edit-lovelace", HuiDialogEditLovelace); diff --git a/src/panels/lovelace/editor/lovelace-editor/hui-lovelace-editor.ts b/src/panels/lovelace/editor/lovelace-editor/hui-lovelace-editor.ts new file mode 100644 index 0000000000..68761be409 --- /dev/null +++ b/src/panels/lovelace/editor/lovelace-editor/hui-lovelace-editor.ts @@ -0,0 +1,80 @@ +import { html, LitElement, PropertyDeclarations } from "@polymer/lit-element"; +import { TemplateResult } from "lit-html"; +import "@polymer/paper-input/paper-input"; + +import { EditorTarget } from "../types"; +import { hassLocalizeLitMixin } from "../../../../mixins/lit-localize-mixin"; +import { HomeAssistant } from "../../../../types"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import { configElementStyle } from "../config-elements/config-elements-style"; + +import { LovelaceConfig } from "../../../../data/lovelace"; + +declare global { + interface HASSDomEvents { + "lovelace-config-changed": { + config: LovelaceConfig; + }; + } +} + +export class HuiLovelaceEditor extends hassLocalizeLitMixin(LitElement) { + static get properties(): PropertyDeclarations { + return { hass: {}, config: {} }; + } + + public hass?: HomeAssistant; + public config?: LovelaceConfig; + + get _title(): string { + if (!this.config) { + return ""; + } + return this.config.title || ""; + } + + protected render(): TemplateResult { + return html` + ${configElementStyle} +
+ +
+ `; + } + + private _valueChanged(ev: Event): void { + if (!this.config) { + return; + } + + const target = ev.currentTarget! as EditorTarget; + + if (this[`_${target.configValue}`] === target.value) { + return; + } + + let newConfig; + + if (target.configValue) { + newConfig = { + ...this.config, + [target.configValue]: target.value, + }; + } + + fireEvent(this, "lovelace-config-changed", { config: newConfig }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-lovelace-editor": HuiLovelaceEditor; + } +} + +customElements.define("hui-lovelace-editor", HuiLovelaceEditor); diff --git a/src/panels/lovelace/editor/lovelace-editor/show-edit-lovelace-dialog.ts b/src/panels/lovelace/editor/lovelace-editor/show-edit-lovelace-dialog.ts new file mode 100644 index 0000000000..bc042dfa15 --- /dev/null +++ b/src/panels/lovelace/editor/lovelace-editor/show-edit-lovelace-dialog.ts @@ -0,0 +1,32 @@ +import { fireEvent } from "../../../../common/dom/fire_event"; +import { Lovelace } from "../../types"; + +declare global { + // for fire event + interface HASSDomEvents { + "show-edit-lovelace": Lovelace; + } +} + +let registeredDialog = false; +const dialogShowEvent = "show-edit-lovelace"; +const dialogTag = "hui-dialog-edit-lovelace"; + +const registerEditLovelaceDialog = (element: HTMLElement) => + fireEvent(element, "register-dialog", { + dialogShowEvent, + dialogTag, + dialogImport: () => + import(/* webpackChunkName: "hui-dialog-edit-lovelace" */ "./hui-dialog-edit-lovelace"), + }); + +export const showEditLovelaceDialog = ( + element: HTMLElement, + lovelace: Lovelace +) => { + if (!registeredDialog) { + registeredDialog = true; + registerEditLovelaceDialog(element); + } + fireEvent(element, dialogShowEvent, lovelace); +}; diff --git a/src/panels/lovelace/hui-editor.ts b/src/panels/lovelace/hui-editor.ts index 42b65dbc79..7ebe72d96c 100644 --- a/src/panels/lovelace/hui-editor.ts +++ b/src/panels/lovelace/hui-editor.ts @@ -90,7 +90,9 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) { app-header-layout { height: 100vh; } - + app-toolbar { + background-color: #455a64; + } paper-button { font-size: 16px; } diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index 04621e7e5c..066c9536b6 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -43,6 +43,7 @@ import "./hui-view"; import { HUIView } from "./hui-view"; import createCardElement from "./common/create-card-element"; import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog"; +import { showEditLovelaceDialog } from "./editor/lovelace-editor/show-edit-lovelace-dialog"; import { Lovelace } from "./types"; import { afterNextRender } from "../../common/util/render-status"; @@ -121,18 +122,51 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) { .narrow="${this.narrow}" > - + ${ this._editMode ? html` - +
- ${this.localize("ui.panel.lovelace.editor.header")} + ${ + this.config.title || + this.localize("ui.panel.lovelace.editor.header") + } +
+ + + + + Raw config editor + +
` : html` @@ -180,16 +214,6 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) { this.localize("ui.panel.lovelace.editor.configure_ui") } - ${ - this._storageMode - ? html` - Raw config editor - ` - : "" - } Help @@ -224,7 +248,7 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) { this._editMode ? html` @@ -293,11 +317,20 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) { --paper-tabs-selection-bar-color: var(--text-primary-color, #fff); text-transform: uppercase; } - paper-tab.iron-selected .edit-view-icon { + .edit-mode { + background-color: #455a64; + } + .edit-mode div[main-title] { + pointer-events: auto; + } + paper-tab.iron-selected .edit-icon { display: inline-flex; } - .edit-view-icon { + .edit-icon { + color: var(--accent-color); padding-left: 8px; + } + .edit-icon.view { display: none; } #add-view { @@ -412,10 +445,6 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) { return this.lovelace!.mode === "yaml"; } - private get _storageMode(): boolean { - return this.lovelace!.mode === "storage"; - } - private get _editMode() { return this.lovelace!.editMode; } @@ -478,6 +507,10 @@ class HUIRoot extends hassLocalizeLitMixin(LitElement) { } } + private _editLovelace() { + showEditLovelaceDialog(this, this.lovelace!); + } + private _editView() { showEditViewDialog(this, { lovelace: this.lovelace!,