From 1b44c7ce640339b9727a3bb8832a52409f111efb Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Wed, 13 Sep 2023 12:05:53 +0200 Subject: [PATCH] Add strategy editor rename clean function Fix types --- .../hui-dashboard-strategy-element-editor.ts | 30 ++++ ...iginal-states-dashboard-strategy-editor.ts | 75 ++++++++++ .../lovelace/editor/hui-element-editor.ts | 8 +- src/panels/lovelace/hui-root.ts | 17 ++- .../dialog-dashboard-strategy-editor.ts | 129 ++++++++++++++++++ .../show-dialog-dashboard-strategy-editor.ts | 21 +++ .../lovelace/strategies/get-strategy.ts | 11 +- .../lovelace/strategies/legacy-strategy.ts | 14 ++ .../original-states-dashboard-strategy.ts | 23 +++- .../original-states-view-strategy.ts | 9 +- src/panels/lovelace/strategies/types.ts | 6 + 11 files changed, 323 insertions(+), 20 deletions(-) create mode 100644 src/panels/lovelace/editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor.ts create mode 100644 src/panels/lovelace/editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor.ts create mode 100644 src/panels/lovelace/strategies/device-registry-detail/dialog-dashboard-strategy-editor.ts create mode 100644 src/panels/lovelace/strategies/device-registry-detail/show-dialog-dashboard-strategy-editor.ts diff --git a/src/panels/lovelace/editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor.ts b/src/panels/lovelace/editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor.ts new file mode 100644 index 000000000000..69984ec1096d --- /dev/null +++ b/src/panels/lovelace/editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor.ts @@ -0,0 +1,30 @@ +import { customElement } from "lit/decorators"; +import { LovelaceDashboardStrategyConfig } from "../../../../data/lovelace/config/types"; +import { getLovelaceStrategy } from "../../strategies/get-strategy"; +import { LovelaceStrategyEditor } from "../../strategies/types"; +import { HuiElementEditor } from "../hui-element-editor"; + +@customElement("hui-dashboard-strategy-element-editor") +export class HuiDashboardStrategyElementEditor extends HuiElementEditor { + protected async getConfigElement(): Promise< + LovelaceStrategyEditor | undefined + > { + const elClass = await getLovelaceStrategy( + "dashboard", + this.configElementType! + ); + + // Check if a GUI editor exists + if (elClass && elClass.getConfigElement) { + return elClass.getConfigElement(); + } + + return undefined; + } +} + +declare global { + interface HTMLElementTagNameMap { + "hui-dashboard-strategy-element-editor": HuiDashboardStrategyElementEditor; + } +} diff --git a/src/panels/lovelace/editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor.ts b/src/panels/lovelace/editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor.ts new file mode 100644 index 000000000000..d411ce5af9c2 --- /dev/null +++ b/src/panels/lovelace/editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor.ts @@ -0,0 +1,75 @@ +import { html, LitElement, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { fireEvent } from "../../../../common/dom/fire_event"; +import "../../../../components/ha-form/ha-form"; +import type { + HaFormSchema, + SchemaUnion, +} from "../../../../components/ha-form/types"; +import type { HomeAssistant } from "../../../../types"; +import { OriginalStatesDashboardStrategyConfig } from "../../strategies/original-states-dashboard-strategy"; +import { LovelaceStrategyEditor } from "../../strategies/types"; + +const SCHEMA = [ + { + name: "no_area_group", + selector: { + boolean: {}, + }, + }, +] as const satisfies readonly HaFormSchema[]; + +@customElement("hui-original-states-dashboard-strategy-editor") +export class HuiOriginalStatesDashboarStrategyEditor + extends LitElement + implements LovelaceStrategyEditor +{ + @property({ attribute: false }) public hass?: HomeAssistant; + + @state() + private _config?: OriginalStatesDashboardStrategyConfig; + + public setConfig(config: OriginalStatesDashboardStrategyConfig): void { + this._config = config; + } + + protected render() { + if (!this.hass || !this._config) { + return nothing; + } + + const data = this._config; + + return html` + + `; + } + + private _valueChanged(ev: CustomEvent): void { + const config = ev.detail.value; + fireEvent(this, "config-changed", { config }); + } + + private _computeLabelCallback = (schema: SchemaUnion) => { + switch (schema.name) { + case "no_area_group": + return "Do not group by area"; + default: + return this.hass!.localize( + `ui.panel.lovelace.editor.card.generic.${schema.name}` + ); + } + }; +} + +declare global { + interface HTMLElementTagNameMap { + "hui-original-states-dashboard-strategy-editor": HuiOriginalStatesDashboarStrategyEditor; + } +} diff --git a/src/panels/lovelace/editor/hui-element-editor.ts b/src/panels/lovelace/editor/hui-element-editor.ts index 56486d66ec79..916a9e0eeb2b 100644 --- a/src/panels/lovelace/editor/hui-element-editor.ts +++ b/src/panels/lovelace/editor/hui-element-editor.ts @@ -16,8 +16,9 @@ import "../../../components/ha-alert"; import "../../../components/ha-circular-progress"; import "../../../components/ha-code-editor"; import type { HaCodeEditor } from "../../../components/ha-code-editor"; -import type { LovelaceCardConfig } from "../../../data/lovelace/config/card"; -import type { LovelaceConfig } from "../../../data/lovelace/config/types"; +import { LovelaceCardConfig } from "../../../data/lovelace/config/card"; +import { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy"; +import { LovelaceConfig } from "../../../data/lovelace/config/types"; import type { HomeAssistant } from "../../../types"; import type { LovelaceRowConfig } from "../entity-rows/types"; import { LovelaceHeaderFooterConfig } from "../header-footer/types"; @@ -36,7 +37,8 @@ export interface ConfigChangedEvent { | LovelaceCardConfig | LovelaceRowConfig | LovelaceHeaderFooterConfig - | LovelaceTileFeatureConfig; + | LovelaceTileFeatureConfig + | LovelaceStrategyConfig; error?: string; guiModeAvailable?: boolean; } diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index f2dc50d40067..dcb49ebc274f 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -19,12 +19,12 @@ import { import "@polymer/paper-tabs/paper-tab"; import "@polymer/paper-tabs/paper-tabs"; import { - css, CSSResultGroup, - html, LitElement, PropertyValues, TemplateResult, + css, + html, } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; @@ -64,11 +64,15 @@ import { documentationUrl } from "../../util/documentation-url"; import { swapView } from "./editor/config-util"; import { showEditLovelaceDialog } from "./editor/lovelace-editor/show-edit-lovelace-dialog"; import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog"; +import { showDashboardStrategyEditorDialog } from "./strategies/device-registry-detail/show-dialog-dashboard-strategy-editor"; import type { Lovelace } from "./types"; import "./views/hui-view"; import type { HUIView } from "./views/hui-view"; import { LovelaceViewConfig } from "../../data/lovelace/config/view"; -import { LovelaceConfig } from "../../data/lovelace/config/types"; +import { + LovelaceConfig, + isStrategyDashboard, +} from "../../data/lovelace/config/types"; @customElement("hui-root") class HUIRoot extends LitElement { @@ -804,6 +808,13 @@ class HUIRoot extends LitElement { }); return; } + if (isStrategyDashboard(this.lovelace!.rawConfig)) { + showDashboardStrategyEditorDialog(this, { + config: this.lovelace!.rawConfig, + saveConfig: this.lovelace!.saveConfig, + }); + return; + } this.lovelace!.setEditMode(true); } diff --git a/src/panels/lovelace/strategies/device-registry-detail/dialog-dashboard-strategy-editor.ts b/src/panels/lovelace/strategies/device-registry-detail/dialog-dashboard-strategy-editor.ts new file mode 100644 index 000000000000..507e6c340533 --- /dev/null +++ b/src/panels/lovelace/strategies/device-registry-detail/dialog-dashboard-strategy-editor.ts @@ -0,0 +1,129 @@ +import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { customElement, property, query, state } from "lit/decorators"; +import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event"; +import "../../../../components/ha-button"; +import { createCloseHeading } from "../../../../components/ha-dialog"; +import { LovelaceStrategyConfig } from "../../../../data/lovelace/config/strategy"; +import { haStyle, haStyleDialog } from "../../../../resources/styles"; +import type { HomeAssistant } from "../../../../types"; +import { showSaveSuccessToast } from "../../../../util/toast-saved-success"; +import "../../editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor"; +import type { HuiDashboardStrategyElementEditor } from "../../editor/dashboard-strategy-editor/hui-dashboard-strategy-element-editor"; +import { ConfigChangedEvent } from "../../editor/hui-element-editor"; +import { GUIModeChangedEvent } from "../../editor/types"; +import { cleanLegacyStrategyConfig } from "../legacy-strategy"; +import type { DashboardStrategyEditorDialogParams } from "./show-dialog-dashboard-strategy-editor"; + +@customElement("dialog-dashboard-strategy-editor") +class DialogDashboardStrategyEditor extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @state() private _params?: DashboardStrategyEditorDialogParams; + + @state() private _strategyConfig?: LovelaceStrategyConfig; + + @state() private _GUImode = true; + + @state() private _guiModeAvailable? = true; + + @query("hui-dashboard-strategy-element-editor") + private _strategyEditorEl?: HuiDashboardStrategyElementEditor; + + public async showDialog( + params: DashboardStrategyEditorDialogParams + ): Promise { + this._params = params; + this._strategyConfig = params.config.strategy; + await this.updateComplete; + } + + public closeDialog(): void { + this._params = undefined; + this._strategyConfig = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + private _handleConfigChanged(ev: HASSDomEvent) { + ev.stopPropagation(); + this._guiModeAvailable = ev.detail.guiModeAvailable; + this._strategyConfig = ev.detail.config as LovelaceStrategyConfig; + } + + private _handleGUIModeChanged(ev: HASSDomEvent): void { + ev.stopPropagation(); + this._GUImode = ev.detail.guiMode; + this._guiModeAvailable = ev.detail.guiModeAvailable; + } + + private _toggleMode(): void { + this._strategyEditorEl?.toggleMode(); + } + + private _opened() { + this._strategyEditorEl?.focusYamlEditor(); + } + + private async _save(): Promise { + await this._params!.saveConfig({ + ...this._params!.config, + strategy: this._strategyConfig!, + }); + showSaveSuccessToast(this, this.hass); + this.closeDialog(); + } + + protected render() { + if (!this._params || !this._strategyConfig) { + return nothing; + } + + const config = cleanLegacyStrategyConfig(this._strategyConfig); + + return html` + + + ${this._strategyConfig !== undefined + ? html` + + ${this.hass!.localize( + !this._strategyEditorEl || this._GUImode + ? "ui.panel.lovelace.editor.edit_card.show_code_editor" + : "ui.panel.lovelace.editor.edit_card.show_visual_editor" + )} + + + ${this.hass!.localize("ui.common.save")} + + ` + : nothing} + + `; + } + + static get styles(): CSSResultGroup { + return [haStyle, haStyleDialog, css``]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "dialog-dashboard-strategy-editor": DialogDashboardStrategyEditor; + } +} diff --git a/src/panels/lovelace/strategies/device-registry-detail/show-dialog-dashboard-strategy-editor.ts b/src/panels/lovelace/strategies/device-registry-detail/show-dialog-dashboard-strategy-editor.ts new file mode 100644 index 000000000000..822952cd0dd1 --- /dev/null +++ b/src/panels/lovelace/strategies/device-registry-detail/show-dialog-dashboard-strategy-editor.ts @@ -0,0 +1,21 @@ +import { fireEvent } from "../../../../common/dom/fire_event"; +import { LovelaceDashboardStrategyConfig } from "../../../../data/lovelace/config/types"; + +export interface DashboardStrategyEditorDialogParams { + config: LovelaceDashboardStrategyConfig; + saveConfig: (config: LovelaceDashboardStrategyConfig) => void; +} + +export const loadDashboardStrategyEditorDialog = () => + import("./dialog-dashboard-strategy-editor"); + +export const showDashboardStrategyEditorDialog = ( + element: HTMLElement, + params: DashboardStrategyEditorDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-dashboard-strategy-editor", + dialogImport: loadDashboardStrategyEditorDialog, + dialogParams: params, + }); +}; diff --git a/src/panels/lovelace/strategies/get-strategy.ts b/src/panels/lovelace/strategies/get-strategy.ts index cf507c78ac94..ee62115275df 100644 --- a/src/panels/lovelace/strategies/get-strategy.ts +++ b/src/panels/lovelace/strategies/get-strategy.ts @@ -9,7 +9,7 @@ import { isStrategyView, } from "../../../data/lovelace/config/view"; import { AsyncReturnType, HomeAssistant } from "../../../types"; -import { isLegacyStrategy } from "./legacy-strategy"; +import { cleanLegacyStrategyConfig, isLegacyStrategy } from "./legacy-strategy"; import { LovelaceDashboardStrategy, LovelaceStrategy, @@ -40,7 +40,7 @@ type StrategyConfig = AsyncReturnType< Strategies[T]["generate"] >; -const getLovelaceStrategy = async ( +export const getLovelaceStrategy = async ( configType: T, strategyType: string ): Promise => { @@ -109,12 +109,7 @@ const generateStrategy = async ( } } - const config = { - ...strategyConfig, - ...strategyConfig.options, - }; - - delete config.options; + const config = cleanLegacyStrategyConfig(strategyConfig); return await strategy.generate(config, hass); } catch (err: any) { diff --git a/src/panels/lovelace/strategies/legacy-strategy.ts b/src/panels/lovelace/strategies/legacy-strategy.ts index 7ce729f0cd8b..98c786ad053f 100644 --- a/src/panels/lovelace/strategies/legacy-strategy.ts +++ b/src/panels/lovelace/strategies/legacy-strategy.ts @@ -1,3 +1,4 @@ +import { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy"; import { LovelaceConfig, LovelaceRawConfig, @@ -27,3 +28,16 @@ export interface LovelaceViewStrategy { hass: HomeAssistant; }): Promise; } + +export const cleanLegacyStrategyConfig = (config: LovelaceStrategyConfig) => { + if (!(Object.keys(config).length === 2 && "options" in config)) { + return config; + } + const cleanedConfig = { + ...config, + ...config.options, + }; + + delete cleanedConfig.options; + return cleanedConfig; +}; diff --git a/src/panels/lovelace/strategies/original-states-dashboard-strategy.ts b/src/panels/lovelace/strategies/original-states-dashboard-strategy.ts index 7dfaec41a819..a7491091b155 100644 --- a/src/panels/lovelace/strategies/original-states-dashboard-strategy.ts +++ b/src/panels/lovelace/strategies/original-states-dashboard-strategy.ts @@ -1,22 +1,39 @@ import { ReactiveElement } from "lit"; import { customElement } from "lit/decorators"; -import { HomeAssistant } from "../../../types"; import { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy"; import { LovelaceConfig } from "../../../data/lovelace/config/types"; +import { HomeAssistant } from "../../../types"; +import { LovelaceStrategyEditor } from "./types"; + +export type OriginalStatesDashboardStrategyConfig = LovelaceStrategyConfig & { + no_area_group?: boolean; +}; @customElement("original-states-dashboard-strategy") export class OriginalStatesDashboardStrategy extends ReactiveElement { static async generate( - _config: LovelaceStrategyConfig, + config: OriginalStatesDashboardStrategyConfig, hass: HomeAssistant ): Promise { return { title: hass.config.location_name, views: [ { - strategy: { type: "original-states" }, + strategy: { + type: "original-states", + no_area_group: config.no_area_group, + }, }, ], }; } + + public static async getConfigElement(): Promise { + await import( + "../editor/dashboard-strategy-editor/hui-original-states-dashboard-strategy-editor" + ); + return document.createElement( + "hui-original-states-dashboard-strategy-editor" + ); + } } diff --git a/src/panels/lovelace/strategies/original-states-view-strategy.ts b/src/panels/lovelace/strategies/original-states-view-strategy.ts index 05cb1ecd2b2e..3f8d20f66736 100644 --- a/src/panels/lovelace/strategies/original-states-view-strategy.ts +++ b/src/panels/lovelace/strategies/original-states-view-strategy.ts @@ -3,15 +3,18 @@ import { ReactiveElement } from "lit"; import { customElement } from "lit/decorators"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { getEnergyPreferences } from "../../../data/energy"; -import { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy"; import { LovelaceViewConfig } from "../../../data/lovelace/config/view"; import { HomeAssistant } from "../../../types"; import { generateDefaultViewConfig } from "../common/generate-lovelace-config"; +export type OriginalStatesViewStrategyConfig = { + no_area_group?: boolean; +}; + @customElement("original-states-view-strategy") export class OriginalStatesViewStrategy extends ReactiveElement { static async generate( - _config: LovelaceStrategyConfig, + config: OriginalStatesViewStrategyConfig, hass: HomeAssistant ): Promise { if (hass.config.state === STATE_NOT_RUNNING) { @@ -37,7 +40,7 @@ export class OriginalStatesViewStrategy extends ReactiveElement { // User can override default view. If they didn't, we will add one // that contains all entities. const view = generateDefaultViewConfig( - hass.areas, + config.no_area_group ? {} : hass.areas, hass.devices, hass.entities, hass.states, diff --git a/src/panels/lovelace/strategies/types.ts b/src/panels/lovelace/strategies/types.ts index ea4946a8006e..da2ab8ee3c00 100644 --- a/src/panels/lovelace/strategies/types.ts +++ b/src/panels/lovelace/strategies/types.ts @@ -2,9 +2,11 @@ import { LovelaceConfig } from "../../../data/lovelace/config/types"; import { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy"; import { LovelaceViewConfig } from "../../../data/lovelace/config/view"; import { HomeAssistant } from "../../../types"; +import { LovelaceGenericElementEditor } from "../types"; export type LovelaceStrategy = { generate(config: LovelaceStrategyConfig, hass: HomeAssistant): Promise; + getConfigElement?: () => LovelaceStrategyEditor; }; export interface LovelaceDashboardStrategy @@ -12,3 +14,7 @@ export interface LovelaceDashboardStrategy export interface LovelaceViewStrategy extends LovelaceStrategy {} + +export interface LovelaceStrategyEditor extends LovelaceGenericElementEditor { + setConfig(config: LovelaceStrategyConfig): void; +}