From 675667a4cfad2b9f449d0c692a9e7673bb83e1c8 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 27 Jun 2024 14:40:42 +0200 Subject: [PATCH 1/5] Wait for hass and config before building the card --- src/panels/lovelace/cards/hui-card.ts | 49 +++++++++++++++++++-------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/src/panels/lovelace/cards/hui-card.ts b/src/panels/lovelace/cards/hui-card.ts index a892d4764dcf..0e34efab2288 100644 --- a/src/panels/lovelace/cards/hui-card.ts +++ b/src/panels/lovelace/cards/hui-card.ts @@ -1,4 +1,4 @@ -import { PropertyValueMap, PropertyValues, ReactiveElement } from "lit"; +import { PropertyValues, ReactiveElement } from "lit"; import { customElement, property } from "lit/decorators"; import { fireEvent } from "../../../common/dom/fire_event"; import { MediaQueriesListener } from "../../../common/dom/media_query"; @@ -23,21 +23,17 @@ declare global { @customElement("hui-card") export class HuiCard extends ReactiveElement { - @property({ attribute: false }) public hass?: HomeAssistant; - @property({ type: Boolean }) public preview = false; @property({ type: Boolean }) public isPanel = false; + private _config?: LovelaceCardConfig; + + private _hass?: HomeAssistant; + set config(config: LovelaceCardConfig | undefined) { - if (!config) return; - if (config.type !== this._config?.type) { - this._buildElement(config); - } else if (config !== this.config) { - this._element?.setConfig(config); - fireEvent(this, "card-updated"); - } this._config = config; + this._init(); } @property({ attribute: false }) @@ -45,7 +41,22 @@ export class HuiCard extends ReactiveElement { return this._config; } - private _config?: LovelaceCardConfig; + set hass(hass: HomeAssistant | undefined) { + this._hass = hass; + this._init(); + } + + @property({ attribute: false }) + public get hass() { + return this._hass; + } + + private _init() { + if (!this.config || !this.hass) { + return; + } + this._buildElement(this.config); + } private _element?: LovelaceCard; @@ -135,6 +146,18 @@ export class HuiCard extends ReactiveElement { protected update(changedProps: PropertyValues) { super.update(changedProps); + if (changedProps.has("config") && this.config) { + const oldConfig = changedProps.get("config"); + if (oldConfig?.type !== this.config.type) { + this._buildElement(this.config); + } else if (oldConfig !== this.config) { + if (this._element) { + this._element.setConfig(this.config); + fireEvent(this, "card-updated"); + } + } + } + if (this._element) { if (changedProps.has("hass")) { try { @@ -156,11 +179,7 @@ export class HuiCard extends ReactiveElement { this._element.isPanel = this.isPanel; } } - } - protected willUpdate( - changedProps: PropertyValueMap | Map - ): void { if (changedProps.has("hass") || changedProps.has("preview")) { this._updateVisibility(); } From 191aed57b86730e2fc2bb6d650c97a6a9cf5f3eb Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 27 Jun 2024 15:43:17 +0200 Subject: [PATCH 2/5] Don't use setter --- src/panels/lovelace/cards/hui-card.ts | 55 +++++++------------ .../lovelace/cards/hui-conditional-card.ts | 1 + .../lovelace/cards/hui-entity-filter-card.ts | 1 + src/panels/lovelace/cards/hui-stack-card.ts | 8 ++- .../card-editor/hui-card-layout-editor.ts | 1 + src/panels/lovelace/sections/hui-section.ts | 1 + src/panels/lovelace/views/hui-view.ts | 1 + 7 files changed, 31 insertions(+), 37 deletions(-) diff --git a/src/panels/lovelace/cards/hui-card.ts b/src/panels/lovelace/cards/hui-card.ts index 0e34efab2288..898b5da7e894 100644 --- a/src/panels/lovelace/cards/hui-card.ts +++ b/src/panels/lovelace/cards/hui-card.ts @@ -27,33 +27,13 @@ export class HuiCard extends ReactiveElement { @property({ type: Boolean }) public isPanel = false; - private _config?: LovelaceCardConfig; + @property({ attribute: false }) public config?: LovelaceCardConfig; - private _hass?: HomeAssistant; + @property({ attribute: false }) public hass?: HomeAssistant; - set config(config: LovelaceCardConfig | undefined) { - this._config = config; - this._init(); - } - - @property({ attribute: false }) - public get config() { - return this._config; - } - - set hass(hass: HomeAssistant | undefined) { - this._hass = hass; - this._init(); - } - - @property({ attribute: false }) - public get hass() { - return this._hass; - } - - private _init() { - if (!this.config || !this.hass) { - return; + public build() { + if (!this.config) { + throw new Error("Cannot build card without config"); } this._buildElement(this.config); } @@ -103,7 +83,9 @@ export class HuiCard extends ReactiveElement { private _createElement(config: LovelaceCardConfig) { const element = createCardElement(config); - element.hass = this.hass; + if (this.hass) { + element.hass = this.hass; + } element.preview = this.preview; // For backwards compatibility (element as any).editMode = this.preview; @@ -117,7 +99,6 @@ export class HuiCard extends ReactiveElement { (ev: Event) => { ev.stopPropagation(); element.hass = this.hass; - element.preview = this.preview; fireEvent(this, "card-updated"); }, { once: true } @@ -146,13 +127,17 @@ export class HuiCard extends ReactiveElement { protected update(changedProps: PropertyValues) { super.update(changedProps); - if (changedProps.has("config") && this.config) { + if (!this._element) { + this.build(); + } + + if (changedProps.has("config")) { const oldConfig = changedProps.get("config"); - if (oldConfig?.type !== this.config.type) { - this._buildElement(this.config); - } else if (oldConfig !== this.config) { - if (this._element) { - this._element.setConfig(this.config); + if (this.config && oldConfig && this.config !== oldConfig) { + if (this.config.type !== oldConfig.type) { + this._buildElement(this.config); + } else { + this._element?.setConfig(this.config); fireEvent(this, "card-updated"); } } @@ -161,7 +146,9 @@ export class HuiCard extends ReactiveElement { if (this._element) { if (changedProps.has("hass")) { try { - this._element.hass = this.hass; + if (this.hass) { + this._element.hass = this.hass; + } } catch (e: any) { this._buildElement(createErrorCardConfig(e.message, null)); } diff --git a/src/panels/lovelace/cards/hui-conditional-card.ts b/src/panels/lovelace/cards/hui-conditional-card.ts index 9a57b3e5a343..68ee58b9cb3b 100644 --- a/src/panels/lovelace/cards/hui-conditional-card.ts +++ b/src/panels/lovelace/cards/hui-conditional-card.ts @@ -41,6 +41,7 @@ class HuiConditionalCard extends HuiConditionalBase implements LovelaceCard { element.hass = this.hass; element.preview = this.preview; element.config = cardConfig; + element.build(); return element; } diff --git a/src/panels/lovelace/cards/hui-entity-filter-card.ts b/src/panels/lovelace/cards/hui-entity-filter-card.ts index ca8372b4a7c0..5475fc550448 100644 --- a/src/panels/lovelace/cards/hui-entity-filter-card.ts +++ b/src/panels/lovelace/cards/hui-entity-filter-card.ts @@ -249,6 +249,7 @@ export class HuiEntityFilterCard element.hass = this.hass; element.preview = this.preview; element.config = cardConfig; + element.build(); return element; } } diff --git a/src/panels/lovelace/cards/hui-stack-card.ts b/src/panels/lovelace/cards/hui-stack-card.ts index a308e368eb59..f22dc3351fe1 100644 --- a/src/panels/lovelace/cards/hui-stack-card.ts +++ b/src/panels/lovelace/cards/hui-stack-card.ts @@ -56,9 +56,11 @@ export abstract class HuiStackCard card.hass = this.hass; }); } - if (changedProperties.has("editMode")) { + if (changedProperties.has("preview")) { this._cards.forEach((card) => { - card.preview = this.preview; + if (this.preview !== undefined) { + card.preview = this.preview; + } }); } } @@ -67,8 +69,8 @@ export abstract class HuiStackCard private _createCardElement(cardConfig: LovelaceCardConfig) { const element = document.createElement("hui-card"); element.hass = this.hass; - element.preview = this.preview; element.config = cardConfig; + element.build(); return element; } diff --git a/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts b/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts index f9cda6e38bac..6fba848973b9 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts @@ -146,6 +146,7 @@ export class HuiCardLayoutEditor extends LitElement { this._defaultLayoutOptions = this._cardElement?.getElementLayoutOptions(); }); + this._cardElement.build(); this._defaultLayoutOptions = this._cardElement.getElementLayoutOptions(); } catch (err) { // eslint-disable-next-line no-console diff --git a/src/panels/lovelace/sections/hui-section.ts b/src/panels/lovelace/sections/hui-section.ts index b662072a13e4..61f2eddd41fe 100644 --- a/src/panels/lovelace/sections/hui-section.ts +++ b/src/panels/lovelace/sections/hui-section.ts @@ -64,6 +64,7 @@ export class HuiSection extends ReactiveElement { ev.stopPropagation(); this._cards = [...this._cards]; }); + element.build(); return element; } diff --git a/src/panels/lovelace/views/hui-view.ts b/src/panels/lovelace/views/hui-view.ts index 06df93b7465b..af1307db81c7 100644 --- a/src/panels/lovelace/views/hui-view.ts +++ b/src/panels/lovelace/views/hui-view.ts @@ -82,6 +82,7 @@ export class HUIView extends ReactiveElement { ev.stopPropagation(); this._cards = [...this._cards]; }); + element.build(); return element; } From 43d5daaed18d63dc65036dc7b4eebbb3d80e35fb Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 27 Jun 2024 15:50:31 +0200 Subject: [PATCH 3/5] Improve code readability --- src/panels/lovelace/cards/hui-card.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/panels/lovelace/cards/hui-card.ts b/src/panels/lovelace/cards/hui-card.ts index 898b5da7e894..e2cdc1ff209f 100644 --- a/src/panels/lovelace/cards/hui-card.ts +++ b/src/panels/lovelace/cards/hui-card.ts @@ -134,7 +134,8 @@ export class HuiCard extends ReactiveElement { if (changedProps.has("config")) { const oldConfig = changedProps.get("config"); if (this.config && oldConfig && this.config !== oldConfig) { - if (this.config.type !== oldConfig.type) { + const typeChanged = this.config.type !== oldConfig.type; + if (typeChanged) { this._buildElement(this.config); } else { this._element?.setConfig(this.config); From b71888b877c488f1df98bc5f83172a1d33d09482 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 27 Jun 2024 16:07:22 +0200 Subject: [PATCH 4/5] Use hasupdated --- src/panels/lovelace/cards/hui-card.ts | 31 +++++++++++++++------------ 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/panels/lovelace/cards/hui-card.ts b/src/panels/lovelace/cards/hui-card.ts index e2cdc1ff209f..ee77c8818ec2 100644 --- a/src/panels/lovelace/cards/hui-card.ts +++ b/src/panels/lovelace/cards/hui-card.ts @@ -124,27 +124,30 @@ export class HuiCard extends ReactiveElement { this._updateVisibility(); } - protected update(changedProps: PropertyValues) { - super.update(changedProps); + protected willUpdate(changedProps: PropertyValues): void { + super.willUpdate(changedProps); if (!this._element) { this.build(); } + } - if (changedProps.has("config")) { - const oldConfig = changedProps.get("config"); - if (this.config && oldConfig && this.config !== oldConfig) { - const typeChanged = this.config.type !== oldConfig.type; - if (typeChanged) { - this._buildElement(this.config); - } else { - this._element?.setConfig(this.config); - fireEvent(this, "card-updated"); - } - } - } + protected update(changedProps: PropertyValues) { + super.update(changedProps); if (this._element) { + if (changedProps.has("config") && this.hasUpdated) { + const oldConfig = changedProps.get("config"); + if (this.config !== oldConfig && this.config) { + const typeChanged = this.config?.type !== oldConfig?.type; + if (typeChanged) { + this._buildElement(this.config); + } else { + this._element?.setConfig(this.config); + fireEvent(this, "card-updated"); + } + } + } if (changedProps.has("hass")) { try { if (this.hass) { From f0fb94b7508950903e9e2769877c3aa16a117f4f Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Thu, 27 Jun 2024 16:17:45 +0200 Subject: [PATCH 5/5] Rename build to load --- src/panels/lovelace/cards/hui-card.ts | 40 +++++++++---------- .../lovelace/cards/hui-conditional-card.ts | 2 +- .../lovelace/cards/hui-entity-filter-card.ts | 2 +- src/panels/lovelace/cards/hui-stack-card.ts | 7 ++-- .../card-editor/hui-card-layout-editor.ts | 2 +- src/panels/lovelace/sections/hui-section.ts | 2 +- src/panels/lovelace/views/hui-view.ts | 2 +- 7 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/panels/lovelace/cards/hui-card.ts b/src/panels/lovelace/cards/hui-card.ts index ee77c8818ec2..6249acaba095 100644 --- a/src/panels/lovelace/cards/hui-card.ts +++ b/src/panels/lovelace/cards/hui-card.ts @@ -31,11 +31,11 @@ export class HuiCard extends ReactiveElement { @property({ attribute: false }) public hass?: HomeAssistant; - public build() { + public load() { if (!this.config) { throw new Error("Cannot build card without config"); } - this._buildElement(this.config); + this._loadElement(this.config); } private _element?: LovelaceCard; @@ -81,43 +81,39 @@ export class HuiCard extends ReactiveElement { return this._element?.getLayoutOptions?.() ?? {}; } - private _createElement(config: LovelaceCardConfig) { - const element = createCardElement(config); + private _loadElement(config: LovelaceCardConfig) { + this._element = createCardElement(config); if (this.hass) { - element.hass = this.hass; + this._element.hass = this.hass; } - element.preview = this.preview; + this._element.preview = this.preview; // For backwards compatibility - (element as any).editMode = this.preview; + (this._element as any).editMode = this.preview; // Update element when the visibility of the card changes (e.g. conditional card or filter card) - element.addEventListener("card-visibility-changed", (ev: Event) => { + this._element.addEventListener("card-visibility-changed", (ev: Event) => { ev.stopPropagation(); this._updateVisibility(); }); - element.addEventListener( + this._element.addEventListener( "ll-upgrade", (ev: Event) => { ev.stopPropagation(); - element.hass = this.hass; + if (this.hass) { + this._element!.hass = this.hass; + } fireEvent(this, "card-updated"); }, { once: true } ); - element.addEventListener( + this._element.addEventListener( "ll-rebuild", (ev: Event) => { ev.stopPropagation(); - this._buildElement(config); + this._loadElement(config); fireEvent(this, "card-updated"); }, { once: true } ); - return element; - } - - private _buildElement(config: LovelaceCardConfig) { - this._element = this._createElement(config); - while (this.lastChild) { this.removeChild(this.lastChild); } @@ -128,7 +124,7 @@ export class HuiCard extends ReactiveElement { super.willUpdate(changedProps); if (!this._element) { - this.build(); + this.load(); } } @@ -141,7 +137,7 @@ export class HuiCard extends ReactiveElement { if (this.config !== oldConfig && this.config) { const typeChanged = this.config?.type !== oldConfig?.type; if (typeChanged) { - this._buildElement(this.config); + this._loadElement(this.config); } else { this._element?.setConfig(this.config); fireEvent(this, "card-updated"); @@ -154,7 +150,7 @@ export class HuiCard extends ReactiveElement { this._element.hass = this.hass; } } catch (e: any) { - this._buildElement(createErrorCardConfig(e.message, null)); + this._loadElement(createErrorCardConfig(e.message, null)); } } if (changedProps.has("preview")) { @@ -163,7 +159,7 @@ export class HuiCard extends ReactiveElement { // For backwards compatibility (this._element as any).editMode = this.preview; } catch (e: any) { - this._buildElement(createErrorCardConfig(e.message, null)); + this._loadElement(createErrorCardConfig(e.message, null)); } } if (changedProps.has("isPanel")) { diff --git a/src/panels/lovelace/cards/hui-conditional-card.ts b/src/panels/lovelace/cards/hui-conditional-card.ts index 68ee58b9cb3b..630a7494c9b0 100644 --- a/src/panels/lovelace/cards/hui-conditional-card.ts +++ b/src/panels/lovelace/cards/hui-conditional-card.ts @@ -41,7 +41,7 @@ class HuiConditionalCard extends HuiConditionalBase implements LovelaceCard { element.hass = this.hass; element.preview = this.preview; element.config = cardConfig; - element.build(); + element.load(); return element; } diff --git a/src/panels/lovelace/cards/hui-entity-filter-card.ts b/src/panels/lovelace/cards/hui-entity-filter-card.ts index 5475fc550448..d79e88ad0daf 100644 --- a/src/panels/lovelace/cards/hui-entity-filter-card.ts +++ b/src/panels/lovelace/cards/hui-entity-filter-card.ts @@ -249,7 +249,7 @@ export class HuiEntityFilterCard element.hass = this.hass; element.preview = this.preview; element.config = cardConfig; - element.build(); + element.load(); return element; } } diff --git a/src/panels/lovelace/cards/hui-stack-card.ts b/src/panels/lovelace/cards/hui-stack-card.ts index f22dc3351fe1..93bc5c4931f9 100644 --- a/src/panels/lovelace/cards/hui-stack-card.ts +++ b/src/panels/lovelace/cards/hui-stack-card.ts @@ -58,9 +58,7 @@ export abstract class HuiStackCard } if (changedProperties.has("preview")) { this._cards.forEach((card) => { - if (this.preview !== undefined) { - card.preview = this.preview; - } + card.preview = this.preview; }); } } @@ -69,8 +67,9 @@ export abstract class HuiStackCard private _createCardElement(cardConfig: LovelaceCardConfig) { const element = document.createElement("hui-card"); element.hass = this.hass; + element.preview = this.preview; element.config = cardConfig; - element.build(); + element.load(); return element; } diff --git a/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts b/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts index 6fba848973b9..68c0487beee4 100644 --- a/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts +++ b/src/panels/lovelace/editor/card-editor/hui-card-layout-editor.ts @@ -146,7 +146,7 @@ export class HuiCardLayoutEditor extends LitElement { this._defaultLayoutOptions = this._cardElement?.getElementLayoutOptions(); }); - this._cardElement.build(); + this._cardElement.load(); this._defaultLayoutOptions = this._cardElement.getElementLayoutOptions(); } catch (err) { // eslint-disable-next-line no-console diff --git a/src/panels/lovelace/sections/hui-section.ts b/src/panels/lovelace/sections/hui-section.ts index 61f2eddd41fe..07dc7a78f8af 100644 --- a/src/panels/lovelace/sections/hui-section.ts +++ b/src/panels/lovelace/sections/hui-section.ts @@ -64,7 +64,7 @@ export class HuiSection extends ReactiveElement { ev.stopPropagation(); this._cards = [...this._cards]; }); - element.build(); + element.load(); return element; } diff --git a/src/panels/lovelace/views/hui-view.ts b/src/panels/lovelace/views/hui-view.ts index af1307db81c7..ea0bb5a89512 100644 --- a/src/panels/lovelace/views/hui-view.ts +++ b/src/panels/lovelace/views/hui-view.ts @@ -82,7 +82,7 @@ export class HUIView extends ReactiveElement { ev.stopPropagation(); this._cards = [...this._cards]; }); - element.build(); + element.load(); return element; }