Conditional element support for use in picture-elements (#2865)

* Conditional element support for use in picture-elements

* Refactored

* Refactor to HTMLElement, separated files

* New file

* Added disconnected callback, handled multiple config calls.

* Added update method

* Refactored and simplified - elements contained internally

* Removed excess if

* Refactored also picture elements
This commit is contained in:
yosilevy 2019-03-03 21:27:34 +02:00 committed by Paulus Schoutsen
parent 241d7345d0
commit 19c44f7c7b
6 changed files with 160 additions and 43 deletions

View File

@ -1,15 +1,14 @@
import { createCardElement } from "../common/create-card-element"; import { createCardElement } from "../common/create-card-element";
import { computeCardSize } from "../common/compute-card-size"; import { computeCardSize } from "../common/compute-card-size";
import {
Condition,
checkConditionsMet,
validateConditionalConfig,
} from "../../lovelace/common/validate-condition";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { LovelaceCard } from "../types"; import { LovelaceCard } from "../types";
import { LovelaceCardConfig } from "../../../data/lovelace"; import { LovelaceCardConfig } from "../../../data/lovelace";
interface Condition {
entity: string;
state?: string;
state_not?: string;
}
interface Config extends LovelaceCardConfig { interface Config extends LovelaceCardConfig {
card: LovelaceCardConfig; card: LovelaceCardConfig;
conditions: Condition[]; conditions: Condition[];
@ -25,7 +24,7 @@ class HuiConditionalCard extends HTMLElement implements LovelaceCard {
!config.card || !config.card ||
!config.conditions || !config.conditions ||
!Array.isArray(config.conditions) || !Array.isArray(config.conditions) ||
!config.conditions.every((c) => c.entity && (c.state || c.state_not)) !validateConditionalConfig(config.conditions)
) { ) {
throw new Error("Error in card configuration."); throw new Error("Error in card configuration.");
} }
@ -36,32 +35,30 @@ class HuiConditionalCard extends HTMLElement implements LovelaceCard {
this._config = config; this._config = config;
this._card = createCardElement(config.card); this._card = createCardElement(config.card);
if (this._hass) {
this.hass = this._hass; this.update();
}
} }
set hass(hass: HomeAssistant) { set hass(hass: HomeAssistant) {
this._hass = hass; this._hass = hass;
if (!this._card) { this.update();
}
public getCardSize() {
return computeCardSize(this._card!);
}
private update() {
if (!this._card || !this._hass) {
return; return;
} }
const visible = const visible =
this._config && this._config && checkConditionsMet(this._config.conditions, this._hass);
this._config.conditions.every((c) => {
if (!(c.entity in hass.states)) {
return false;
}
if (c.state) {
return hass.states[c.entity].state === c.state;
}
return hass.states[c.entity].state !== c.state_not;
});
if (visible) { if (visible) {
this._card.hass = hass; this._card.hass = this._hass;
if (!this._card.parentElement) { if (!this._card.parentElement) {
this.appendChild(this._card); this.appendChild(this._card);
} }
@ -71,10 +68,6 @@ class HuiConditionalCard extends HTMLElement implements LovelaceCard {
// This will hide the complete card so it won't get styled by parent // This will hide the complete card so it won't get styled by parent
this.style.setProperty("display", visible ? "" : "none"); this.style.setProperty("display", visible ? "" : "none");
} }
public getCardSize() {
return computeCardSize(this._card!);
}
} }
declare global { declare global {

View File

@ -1,6 +1,6 @@
import { html, LitElement, TemplateResult } from "lit-element"; import { html, LitElement, TemplateResult } from "lit-element";
import { createHuiElement } from "../common/create-hui-element"; import { createStyledHuiElement } from "./picture-elements/create-styled-hui-element";
import { LovelaceCard } from "../types"; import { LovelaceCard } from "../types";
import { LovelaceCardConfig } from "../../../data/lovelace"; import { LovelaceCardConfig } from "../../../data/lovelace";
@ -71,8 +71,13 @@ class HuiPictureElementsCard extends LitElement implements LovelaceCard {
.entity="${this._config.entity}" .entity="${this._config.entity}"
.aspectRatio="${this._config.aspect_ratio}" .aspectRatio="${this._config.aspect_ratio}"
></hui-image> ></hui-image>
${this._config.elements.map((elementConfig: LovelaceElementConfig) => ${this._config.elements.map(
this._createHuiElement(elementConfig) (elementConfig: LovelaceElementConfig) => {
const element = createStyledHuiElement(elementConfig);
element.hass = this._hass;
return element;
}
)} )}
</div> </div>
</ha-card> </ha-card>
@ -95,20 +100,6 @@ class HuiPictureElementsCard extends LitElement implements LovelaceCard {
</style> </style>
`; `;
} }
private _createHuiElement(
elementConfig: LovelaceElementConfig
): LovelaceElement {
const element = createHuiElement(elementConfig) as LovelaceElement;
element.hass = this._hass;
element.classList.add("element");
Object.keys(elementConfig.style).forEach((prop) => {
element.style.setProperty(prop, elementConfig.style[prop]);
});
return element;
}
} }
declare global { declare global {

View File

@ -0,0 +1,20 @@
import { LovelaceElement, LovelaceElementConfig } from "../../elements/types";
import { createHuiElement } from "../../common/create-hui-element";
export function createStyledHuiElement(
elementConfig: LovelaceElementConfig
): LovelaceElement {
const element = createHuiElement(elementConfig) as LovelaceElement;
// keep conditional card as a transparent container so let its position remain static
if (element.tagName !== "HUI-CONDITIONAL-ELEMENT") {
element.classList.add("element");
}
if (elementConfig.style) {
Object.keys(elementConfig.style).forEach((prop) => {
element.style.setProperty(prop, elementConfig.style[prop]);
});
}
return element;
}

View File

@ -1,5 +1,6 @@
import deepClone from "deep-clone-simple"; import deepClone from "deep-clone-simple";
import "../elements/hui-conditional-element";
import "../elements/hui-icon-element"; import "../elements/hui-icon-element";
import "../elements/hui-image-element"; import "../elements/hui-image-element";
import "../elements/hui-service-button-element"; import "../elements/hui-service-button-element";
@ -17,6 +18,7 @@ import { LovelaceElementConfig, LovelaceElement } from "../elements/types";
const CUSTOM_TYPE_PREFIX = "custom:"; const CUSTOM_TYPE_PREFIX = "custom:";
const ELEMENT_TYPES = new Set([ const ELEMENT_TYPES = new Set([
"conditional",
"icon", "icon",
"image", "image",
"service-button", "service-button",

View File

@ -0,0 +1,28 @@
import { HomeAssistant } from "../../../types";
export interface Condition {
entity: string;
state?: string;
state_not?: string;
}
export function checkConditionsMet(
conditions: Condition[],
hass: HomeAssistant
): boolean {
return conditions.every((c) => {
if (!(c.entity in hass.states)) {
return false;
}
if (c.state) {
return hass.states[c.entity].state === c.state;
}
return hass!.states[c.entity].state !== c.state_not;
});
}
export function validateConditionalConfig(conditions: Condition[]): boolean {
return conditions.every(
(c) => ((c.entity && (c.state || c.state_not)) as unknown) as boolean
);
}

View File

@ -0,0 +1,83 @@
import {
Condition,
checkConditionsMet,
validateConditionalConfig,
} from "../../lovelace/common/validate-condition";
import { createStyledHuiElement } from "../cards/picture-elements/create-styled-hui-element";
import { LovelaceElement, LovelaceElementConfig } from "./types";
import { HomeAssistant } from "../../../types";
interface Config extends LovelaceElementConfig {
conditions: Condition[];
elements: LovelaceElementConfig[];
}
class HuiConditionalElement extends HTMLElement implements LovelaceElement {
public _hass?: HomeAssistant;
private _config?: Config;
private _elements: LovelaceElement[] = [];
public setConfig(config: Config): void {
if (
!config.conditions ||
!Array.isArray(config.conditions) ||
!config.elements ||
!Array.isArray(config.elements) ||
!validateConditionalConfig(config.conditions)
) {
throw new Error("Error in card configuration.");
}
if (this._elements.length > 0) {
this._elements.map((el: LovelaceElement) => {
if (el.parentElement) {
el.parentElement.removeChild(el);
}
});
this._elements = [];
}
this._config = config;
this._config.elements.map((elementConfig: LovelaceElementConfig) => {
this._elements.push(createStyledHuiElement(elementConfig));
});
this.updateElements();
}
set hass(hass: HomeAssistant) {
this._hass = hass;
this.updateElements();
}
private updateElements() {
if (!this._hass || !this._config) {
return;
}
const visible = checkConditionsMet(this._config.conditions, this._hass);
this._elements.map((el: LovelaceElement) => {
if (visible) {
el.hass = this._hass;
if (!el.parentElement) {
this.appendChild(el);
}
} else if (el.parentElement) {
el.parentElement.removeChild(el);
}
});
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-conditional-element": HuiConditionalElement;
}
}
customElements.define("hui-conditional-element", HuiConditionalElement);