From adefc7a4e23cda62c3e1607c8480c86b3b75fa60 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 4 Mar 2022 23:15:10 +0100 Subject: [PATCH] Convert lovelace config dialogs to ha-form (#11910) --- .../dialog-lovelace-dashboard-detail.ts | 290 ++++++++---------- .../dialog-lovelace-resource-detail.ts | 227 +++++++------- 2 files changed, 240 insertions(+), 277 deletions(-) diff --git a/src/panels/config/lovelace/dashboards/dialog-lovelace-dashboard-detail.ts b/src/panels/config/lovelace/dashboards/dialog-lovelace-dashboard-detail.ts index 39b449d3d7..75db9e031c 100644 --- a/src/panels/config/lovelace/dashboards/dialog-lovelace-dashboard-detail.ts +++ b/src/panels/config/lovelace/dashboards/dialog-lovelace-dashboard-detail.ts @@ -1,20 +1,18 @@ import "@material/mwc-button/mwc-button"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../../../../common/dom/fire_event"; import { slugify } from "../../../../common/string/slugify"; -import { computeRTLDirection } from "../../../../common/util/compute_rtl"; import { createCloseHeading } from "../../../../components/ha-dialog"; -import "../../../../components/ha-formfield"; -import "../../../../components/ha-icon-picker"; -import "../../../../components/ha-switch"; -import type { HaSwitch } from "../../../../components/ha-switch"; +import "../../../../components/ha-form/ha-form"; +import { HaFormSchema } from "../../../../components/ha-form/types"; +import { CoreFrontendUserData } from "../../../../data/frontend"; import { LovelaceDashboard, LovelaceDashboardCreateParams, - LovelaceDashboardMutableParams, } from "../../../../data/lovelace"; import { DEFAULT_PANEL, setDefaultPanel } from "../../../../data/panel"; -import { PolymerChangedEvent } from "../../../../polymer-types"; import { haStyleDialog } from "../../../../resources/styles"; import { HomeAssistant } from "../../../../types"; import { LovelaceDashboardDetailsDialogParams } from "./show-dialog-lovelace-dashboard-detail"; @@ -25,62 +23,54 @@ export class DialogLovelaceDashboardDetail extends LitElement { @state() private _params?: LovelaceDashboardDetailsDialogParams; - @state() private _urlPath!: LovelaceDashboard["url_path"]; + @state() private _urlPathChanged = false; - @state() private _showInSidebar!: boolean; + @state() private _data?: Partial; - @state() private _icon!: string; - - @state() private _title!: string; - - @state() - private _requireAdmin!: LovelaceDashboard["require_admin"]; - - @state() private _error?: string; + @state() private _error?: Record; @state() private _submitting = false; - public async showDialog( - params: LovelaceDashboardDetailsDialogParams - ): Promise { + public showDialog(params: LovelaceDashboardDetailsDialogParams): void { this._params = params; this._error = undefined; - this._urlPath = this._params.urlPath || ""; + this._urlPathChanged = false; if (this._params.dashboard) { - this._showInSidebar = !!this._params.dashboard.show_in_sidebar; - this._icon = this._params.dashboard.icon || ""; - this._title = this._params.dashboard.title || ""; - this._requireAdmin = this._params.dashboard.require_admin || false; + this._data = this._params.dashboard; } else { - this._showInSidebar = true; - this._icon = ""; - this._title = ""; - this._requireAdmin = false; + this._data = { + show_in_sidebar: true, + icon: "", + title: "", + require_admin: false, + mode: "storage", + }; } - await this.updateComplete; + } + + public closeDialog(): void { + this._params = undefined; + this._data = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); } protected render(): TemplateResult { - if (!this._params) { + if (!this._params || !this._data) { return html``; } const defaultPanelUrlPath = this.hass.defaultPanel; - const urlInvalid = - this._params.urlPath !== "lovelace" && - !/^[a-zA-Z0-9_-]+-[a-zA-Z0-9_-]+$/.test(this._urlPath); - const titleInvalid = !this._title.trim(); - const dir = computeRTLDirection(this.hass); + const titleInvalid = !this._data.title || !this._data.title.trim(); return html` ${this._error} ` - : ""} -
- - - ${!this._params.dashboard && this.hass.userData?.showAdvanced - ? html` - - ` - : ""} -
- - - - -
-
- - - - -
-
+ `} ${this._params.urlPath @@ -206,7 +134,9 @@ export class DialogLovelaceDashboardDetail extends LitElement { ${this._params.urlPath @@ -223,41 +153,97 @@ export class DialogLovelaceDashboardDetail extends LitElement { `; } - private _urlChanged(ev: PolymerChangedEvent) { - this._error = undefined; - this._urlPath = ev.detail.value; - } + private _schema = memoizeOne( + ( + params: LovelaceDashboardDetailsDialogParams, + userData: CoreFrontendUserData | null | undefined + ) => + [ + { + name: "title", + required: true, + selector: { + text: {}, + }, + }, + { + name: "icon", + required: true, + selector: { + icon: {}, + }, + }, + !params.dashboard && + userData?.showAdvanced && { + name: "url_path", + required: true, + selector: { text: {} }, + }, + { + name: "require_admin", + required: true, + selector: { + boolean: {}, + }, + }, + { + name: "show_in_sidebar", + required: true, + selector: { + boolean: {}, + }, + }, + ].filter(Boolean) + ); - private _iconChanged(ev: PolymerChangedEvent) { - this._error = undefined; - this._icon = ev.detail.value; - } + private _computeLabel = (entry: HaFormSchema): string => + this.hass.localize( + `ui.panel.config.lovelace.dashboards.detail.${ + entry.name === "show_in_sidebar" + ? "show_sidebar" + : entry.name === "url_path" + ? "url" + : entry.name + }` + ); - private _titleChanged(ev: PolymerChangedEvent) { + private _valueChanged(ev: CustomEvent) { this._error = undefined; - this._title = ev.detail.value; - if (!this.hass.userData?.showAdvanced) { - this._fillUrlPath(); + const value = ev.detail.value; + if (value.url_path !== this._data?.url_path) { + this._urlPathChanged = true; + if ( + !value.url_path || + value.url_path === "lovelace" || + !/^[a-zA-Z0-9_-]+-[a-zA-Z0-9_-]+$/.test(value.url_path) + ) { + this._error = { + url_path: this.hass.localize( + "ui.panel.config.lovelace.dashboards.detail.url_error_msg" + ), + }; + } + } + if (value.title !== this._data?.title) { + this._data = value; + this._fillUrlPath(value.title); + } else { + this._data = value; } } - private _fillUrlPath() { - if ((this.hass.userData?.showAdvanced && this._urlPath) || !this._title) { + private _fillUrlPath(title: string) { + if ((this.hass.userData?.showAdvanced && this._urlPathChanged) || !title) { return; } - const slugifyTitle = slugify(this._title, "-"); - this._urlPath = slugifyTitle.includes("-") - ? slugifyTitle - : `lovelace-${slugifyTitle}`; - } - - private _showSidebarChanged(ev: Event) { - this._showInSidebar = (ev.target as HaSwitch).checked; - } - - private _requireAdminChanged(ev: Event) { - this._requireAdmin = (ev.target as HaSwitch).checked; + const slugifyTitle = slugify(title, "-"); + this._data = { + ...this._data, + url_path: slugifyTitle.includes("-") + ? slugifyTitle + : `lovelace-${slugifyTitle}`, + }; } private _toggleDefault() { @@ -273,29 +259,20 @@ export class DialogLovelaceDashboardDetail extends LitElement { private async _updateDashboard() { if (this._params?.urlPath && !this._params.dashboard?.id) { - this._close(); + this.closeDialog(); } this._submitting = true; try { - const values: Partial = { - require_admin: this._requireAdmin, - show_in_sidebar: this._showInSidebar, - icon: this._icon || undefined, - title: this._title, - }; if (this._params!.dashboard) { - await this._params!.updateDashboard(values); + await this._params!.updateDashboard(this._data as LovelaceDashboard); } else { - (values as LovelaceDashboardCreateParams).url_path = - this._urlPath.trim(); - (values as LovelaceDashboardCreateParams).mode = "storage"; await this._params!.createDashboard( - values as LovelaceDashboardCreateParams + this._data as LovelaceDashboardCreateParams ); } - this._close(); + this.closeDialog(); } catch (err: any) { - this._error = err?.message || "Unknown error"; + this._error = { base: err?.message || "Unknown error" }; } finally { this._submitting = false; } @@ -305,26 +282,15 @@ export class DialogLovelaceDashboardDetail extends LitElement { this._submitting = true; try { if (await this._params!.removeDashboard()) { - this._close(); + this.closeDialog(); } } finally { this._submitting = false; } } - private _close(): void { - this._params = undefined; - } - static get styles(): CSSResultGroup { - return [ - haStyleDialog, - css` - ha-switch { - padding: 16px 0; - } - `, - ]; + return [haStyleDialog, css``]; } } diff --git a/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts b/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts index eb2ebe92d3..283d1f2986 100644 --- a/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts +++ b/src/panels/config/lovelace/resources/dialog-lovelace-resource-detail.ts @@ -1,21 +1,20 @@ import "@material/mwc-button/mwc-button"; -import "@material/mwc-list/mwc-list-item"; -import "@polymer/paper-input/paper-input"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; -import { stopPropagation } from "../../../../common/dom/stop_propagation"; +import memoizeOne from "memoize-one"; +import { fireEvent } from "../../../../common/dom/fire_event"; import { createCloseHeading } from "../../../../components/ha-dialog"; -import "../../../../components/ha-select"; -import { - LovelaceResource, - LovelaceResourcesMutableParams, -} from "../../../../data/lovelace"; -import { PolymerChangedEvent } from "../../../../polymer-types"; +import "../../../../components/ha-form/ha-form"; +import { HaFormSchema } from "../../../../components/ha-form/types"; +import { LovelaceResourcesMutableParams } from "../../../../data/lovelace"; import { haStyleDialog } from "../../../../resources/styles"; import { HomeAssistant } from "../../../../types"; import { LovelaceResourceDetailsDialogParams } from "./show-dialog-lovelace-resource-detail"; -const detectResourceType = (url: string) => { +const detectResourceType = (url?: string) => { + if (!url) { + return undefined; + } const ext = url.split(".").pop() || ""; if (ext === "css") { @@ -35,38 +34,41 @@ export class DialogLovelaceResourceDetail extends LitElement { @state() private _params?: LovelaceResourceDetailsDialogParams; - @state() private _url!: LovelaceResource["url"]; + @state() private _data?: Partial; - @state() private _type?: LovelaceResource["type"]; - - @state() private _error?: string; + @state() private _error?: Record; @state() private _submitting = false; - public async showDialog( - params: LovelaceResourceDetailsDialogParams - ): Promise { + public showDialog(params: LovelaceResourceDetailsDialogParams): void { this._params = params; this._error = undefined; if (this._params.resource) { - this._url = this._params.resource.url || ""; - this._type = this._params.resource.type || undefined; + this._data = { + url: this._params.resource.url, + res_type: this._params.resource.type, + }; } else { - this._url = ""; - this._type = undefined; + this._data = { + url: "", + }; } - await this.updateComplete; + } + + public closeDialog(): void { + this._params = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); } protected render(): TemplateResult { if (!this._params) { return html``; } - const urlInvalid = this._url.trim() === ""; + const urlInvalid = !this._data?.url || this._data.url.trim() === ""; return html`
- ${this._error ? html`
${this._error}
` : ""} -
-

- ${this.hass!.localize( - "ui.panel.config.lovelace.resources.detail.warning_header" - )} -

+ ${this.hass!.localize( "ui.panel.config.lovelace.resources.detail.warning_text" )} - -
- - - ${this.hass!.localize( - "ui.panel.config.lovelace.resources.types.module" - )} - - ${this._type === "js" - ? html` - - ${this.hass!.localize( - "ui.panel.config.lovelace.resources.types.js" - )} - - ` - : ""} - - ${this.hass!.localize( - "ui.panel.config.lovelace.resources.types.css" - )} - - ${this._type === "html" - ? html` - - ${this.hass!.localize( - "ui.panel.config.lovelace.resources.types.html" - )} - - ` - : ""} - -
+ + +
${this._params.resource ? html` @@ -159,7 +118,7 @@ export class DialogLovelaceResourceDetail extends LitElement { ${this._params.resource ? this.hass!.localize( @@ -173,37 +132,86 @@ export class DialogLovelaceResourceDetail extends LitElement { `; } - private _urlChanged(ev: PolymerChangedEvent) { - this._error = undefined; - this._url = ev.detail.value; - if (!this._type) { - this._type = detectResourceType(this._url); + private _schema = memoizeOne((data) => [ + { + name: "url", + required: true, + selector: { + text: {}, + }, + }, + { + name: "res_type", + required: true, + selector: { + select: { + options: [ + { + value: "module", + label: this.hass!.localize( + "ui.panel.config.lovelace.resources.types.module" + ), + }, + { + value: "css", + label: this.hass!.localize( + "ui.panel.config.lovelace.resources.types.css" + ), + }, + data.type === "js" && { + value: "js", + label: this.hass!.localize( + "ui.panel.config.lovelace.resources.types.js" + ), + }, + data.type === "html" && { + value: "html", + label: this.hass!.localize( + "ui.panel.config.lovelace.resources.types.html" + ), + }, + ].filter(Boolean), + }, + }, + }, + ]); + + private _computeLabel = (entry: HaFormSchema): string => + this.hass.localize( + `ui.panel.config.lovelace.resources.detail.${entry.name}` + ); + + private _valueChanged(ev: CustomEvent) { + this._data = ev.detail.value; + if (!this._data!.res_type) { + const type = detectResourceType(this._data!.url); + if (!type) { + return; + } + this._data = { + ...this._data, + res_type: type, + }; } } - private _typeChanged(ev) { - this._type = ev.target.value; - } - private async _updateResource() { - if (!this._type) { + if (!this._data?.res_type) { return; } this._submitting = true; try { - const values: LovelaceResourcesMutableParams = { - url: this._url.trim(), - res_type: this._type, - }; if (this._params!.resource) { - await this._params!.updateResource(values); + await this._params!.updateResource(this._data!); } else { - await this._params!.createResource(values); + await this._params!.createResource( + this._data! as LovelaceResourcesMutableParams + ); } this._params = undefined; } catch (err: any) { - this._error = err?.message || "Unknown error"; + this._error = { base: err?.message || "Unknown error" }; } finally { this._submitting = false; } @@ -213,26 +221,15 @@ export class DialogLovelaceResourceDetail extends LitElement { this._submitting = true; try { if (await this._params!.removeResource()) { - this._close(); + this.closeDialog(); } } finally { this._submitting = false; } } - private _close(): void { - this._params = undefined; - } - static get styles(): CSSResultGroup { - return [ - haStyleDialog, - css` - .warning { - color: var(--error-color); - } - `, - ]; + return haStyleDialog; } }