mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 15:26:36 +00:00
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:
parent
241d7345d0
commit
19c44f7c7b
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
}
|
@ -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",
|
||||||
|
28
src/panels/lovelace/common/validate-condition.ts
Normal file
28
src/panels/lovelace/common/validate-condition.ts
Normal 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
|
||||||
|
);
|
||||||
|
}
|
83
src/panels/lovelace/elements/hui-conditional-element.ts
Normal file
83
src/panels/lovelace/elements/hui-conditional-element.ts
Normal 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);
|
Loading…
x
Reference in New Issue
Block a user