diff --git a/src/panels/lovelace/hui-root.ts b/src/panels/lovelace/hui-root.ts index 2ccfc6ccc9..2dd95ba627 100644 --- a/src/panels/lovelace/hui-root.ts +++ b/src/panels/lovelace/hui-root.ts @@ -613,7 +613,7 @@ class HUIRoot extends LitElement { } if (!force && huiView) { - huiView.lovelace = this.lovelace; + huiView.lovelace = this.lovelace!; } } diff --git a/src/panels/lovelace/views/hui-panel-view.ts b/src/panels/lovelace/views/hui-panel-view.ts index 7cf3a2dc8d..515673759c 100644 --- a/src/panels/lovelace/views/hui-panel-view.ts +++ b/src/panels/lovelace/views/hui-panel-view.ts @@ -101,6 +101,11 @@ export class PanelView extends LitElement implements LovelaceViewElement { } private _createCard(): void { + if (this.cards.length === 0) { + this._card = undefined; + return; + } + const card: LovelaceCard = this.cards[0]; card.isPanel = true; diff --git a/src/panels/lovelace/views/hui-view.ts b/src/panels/lovelace/views/hui-view.ts index 666e47e1f0..100d5ef1f1 100644 --- a/src/panels/lovelace/views/hui-view.ts +++ b/src/panels/lovelace/views/hui-view.ts @@ -39,13 +39,13 @@ declare global { @customElement("hui-view") export class HUIView extends UpdatingElement { - @property({ attribute: false }) public hass?: HomeAssistant; + @property({ attribute: false }) public hass!: HomeAssistant; - @property({ attribute: false }) public lovelace?: Lovelace; + @property({ attribute: false }) public lovelace!: Lovelace; @property({ type: Boolean }) public narrow!: boolean; - @property({ type: Number }) public index?: number; + @property({ type: Number }) public index!: number; @internalProperty() private _cards: Array = []; @@ -89,127 +89,128 @@ export class HUIView extends UpdatingElement { protected updated(changedProperties: PropertyValues): void { super.updated(changedProperties); - const hass = this.hass!; - const lovelace = this.lovelace!; + /* + We need to handle the following use cases: + - initialization: create layout element, populate + - config changed to view with same layout element + - config changed to view with different layout element + - forwarded properties hass/narrow/lovelace/cards/badges change + - cards/badges change if one is rebuild when it was loaded later + - lovelace changes if edit mode is enabled or config has changed + */ - const hassChanged = changedProperties.has("hass"); - const oldLovelace = changedProperties.get("lovelace") as Lovelace; - - let editModeChanged = false; - let configChanged = false; - - if (changedProperties.has("index")) { - configChanged = true; - } else if (changedProperties.has("lovelace")) { - editModeChanged = - oldLovelace && lovelace.editMode !== oldLovelace.editMode; - configChanged = !oldLovelace || lovelace.config !== oldLovelace.config; - } - - let viewConfig: LovelaceViewConfig | undefined; + const oldLovelace = changedProperties.get("lovelace") as this["lovelace"]; + const configChanged = + changedProperties.has("index") || + (changedProperties.has("lovelace") && + (!oldLovelace || + this.lovelace.config.views[this.index] !== + oldLovelace.config.views[this.index])); + // If config has changed, create element if necessary and set all values. if (configChanged) { - viewConfig = lovelace.config.views[this.index!]; + let viewConfig = this.lovelace.config.views[this.index]; viewConfig = { ...viewConfig, type: viewConfig.panel ? PANEL_VIEW_LAYOUT : viewConfig.type || DEFAULT_VIEW_LAYOUT, }; - } - let replace = false; - - if ( - configChanged && - (!this._layoutElement || this._layoutElementType !== viewConfig!.type) - ) { - replace = true; - this._layoutElement = createViewElement(viewConfig!); - this._layoutElementType = viewConfig!.type; - this._layoutElement.addEventListener("ll-create-card", () => { - showCreateCardDialog(this, { - lovelaceConfig: this.lovelace!.config, - saveConfig: this.lovelace!.saveConfig, - path: [this.index!], - }); - }); - this._layoutElement.addEventListener("ll-edit-card", (ev) => { - showEditCardDialog(this, { - lovelaceConfig: this.lovelace!.config, - saveConfig: this.lovelace!.saveConfig, - path: ev.detail.path, - }); - }); - this._layoutElement.addEventListener("ll-delete-card", (ev) => { - confDeleteCard(this, this.hass!, this.lovelace!, ev.detail.path); - }); - } - - if (configChanged) { this._createBadges(viewConfig!); this._createCards(viewConfig!); + // Create a new layout element if necessary. + let addLayoutElement = false; + + if ( + !this._layoutElement || + this._layoutElementType !== viewConfig!.type + ) { + addLayoutElement = true; + this._createLayoutElement(viewConfig!); + } + this._layoutElement!.hass = this.hass; this._layoutElement!.narrow = this.narrow; - this._layoutElement!.lovelace = lovelace; + this._layoutElement!.lovelace = this.lovelace; this._layoutElement!.index = this.index; - } - - if (hassChanged) { - this._badges.forEach((badge) => { - badge.hass = hass; - }); - - this._cards.forEach((element) => { - element.hass = hass; - }); - - this._layoutElement!.hass = this.hass; - } - - if (changedProperties.has("narrow")) { - this._layoutElement!.narrow = this.narrow; - } - - if (editModeChanged) { - this._layoutElement!.lovelace = lovelace; - } - - if ( - configChanged || - hassChanged || - editModeChanged || - changedProperties.has("_cards") || - changedProperties.has("_badges") - ) { this._layoutElement!.cards = this._cards; this._layoutElement!.badges = this._badges; + + if (addLayoutElement) { + while (this.lastChild) { + this.removeChild(this.lastChild); + } + this.appendChild(this._layoutElement!); + } + } else { + // Config has not changed. Just props + if (changedProperties.has("hass")) { + this._badges.forEach((badge) => { + badge.hass = this.hass; + }); + + this._cards.forEach((element) => { + element.hass = this.hass; + }); + + this._layoutElement!.hass = this.hass; + } + if (changedProperties.has("narrow")) { + this._layoutElement!.narrow = this.narrow; + } + if (changedProperties.has("lovelace")) { + this._layoutElement!.lovelace = this.lovelace; + } + if (changedProperties.has("_cards")) { + this._layoutElement!.cards = this._cards; + } + if (changedProperties.has("_badges")) { + this._layoutElement!.badges = this._badges; + } } const oldHass = changedProperties.get("hass") as this["hass"] | undefined; + // Update theme if necessary: + // - If config changed, the theme could have changed + // - if hass themes preferences have changed if ( configChanged || - editModeChanged || - (hassChanged && - oldHass && - (hass.themes !== oldHass.themes || - hass.selectedTheme !== oldHass.selectedTheme)) + (changedProperties.has("hass") && + (!oldHass || + this.hass.themes !== oldHass.themes || + this.hass.selectedTheme !== oldHass.selectedTheme)) ) { applyThemesOnElement( this, - hass.themes, - lovelace.config.views[this.index!].theme + this.hass.themes, + this.lovelace.config.views[this.index].theme ); } + } - if (this._layoutElement && replace) { - while (this.lastChild) { - this.removeChild(this.lastChild); - } - this.appendChild(this._layoutElement); - } + private _createLayoutElement(config: LovelaceViewConfig): void { + this._layoutElement = createViewElement(config); + this._layoutElementType = config.type; + this._layoutElement.addEventListener("ll-create-card", () => { + showCreateCardDialog(this, { + lovelaceConfig: this.lovelace!.config, + saveConfig: this.lovelace!.saveConfig, + path: [this.index], + }); + }); + this._layoutElement.addEventListener("ll-edit-card", (ev) => { + showEditCardDialog(this, { + lovelaceConfig: this.lovelace!.config, + saveConfig: this.lovelace!.saveConfig, + path: ev.detail.path, + }); + }); + this._layoutElement.addEventListener("ll-delete-card", (ev) => { + confDeleteCard(this, this.hass!, this.lovelace!, ev.detail.path); + }); } private _createBadges(config: LovelaceViewConfig): void {