ha-frontend/src/panels/lovelace/cards/hui-stack-card.ts

138 lines
3.7 KiB
TypeScript

import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
nothing,
} from "lit";
import { property, state } from "lit/decorators";
import { LovelaceCardConfig } from "../../../data/lovelace/config/card";
import { HomeAssistant } from "../../../types";
import { createCardElement } from "../create-element/create-card-element";
import { LovelaceCard, LovelaceCardEditor } from "../types";
import { StackCardConfig } from "./types";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
export abstract class HuiStackCard<T extends StackCardConfig = StackCardConfig>
extends LitElement
implements LovelaceCard
{
public static async getConfigElement(): Promise<LovelaceCardEditor> {
await import("../editor/config-elements/hui-stack-card-editor");
return document.createElement("hui-stack-card-editor");
}
public static getStubConfig(): Record<string, unknown> {
return { cards: [] };
}
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public editMode = false;
@state() protected _cards?: LovelaceCard[];
@state() protected _config?: T;
public getCardSize(): number | Promise<number> {
return 1;
}
public setConfig(config: T): void {
if (!config || !config.cards || !Array.isArray(config.cards)) {
throw new Error("Invalid configuration");
}
this._config = config;
this._cards = config.cards.map((card) => {
const element = this._createCardElement(card) as LovelaceCard;
return element;
});
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (
!this._cards ||
(!changedProps.has("hass") && !changedProps.has("editMode"))
) {
return;
}
for (const element of this._cards) {
if (this.hass) {
element.hass = this.hass;
}
if (this.editMode !== undefined) {
element.editMode = this.editMode;
}
}
}
protected render() {
if (!this._config || !this._cards) {
return nothing;
}
return html`
${this._config.title
? html`<h1 class="card-header">${this._config.title}</h1>`
: ""}
<div id="root" dir=${this.hass ? computeRTLDirection(this.hass) : "ltr"}>
${this._cards}
</div>
`;
}
static get sharedStyles(): CSSResultGroup {
return css`
.card-header {
color: var(--ha-card-header-color, var(--primary-text-color));
font-family: var(--ha-card-header-font-family, inherit);
font-size: var(--ha-card-header-font-size, 24px);
font-weight: normal;
margin-block-start: 0px;
margin-block-end: 0px;
letter-spacing: -0.012em;
line-height: 32px;
display: block;
padding: 24px 16px 16px;
}
:host {
--ha-card-border-radius: inherit !important;
--ha-card-border-width: inherit !important;
--ha-card-box-shadow: inherit !important;
}
`;
}
private _createCardElement(cardConfig: LovelaceCardConfig) {
const element = createCardElement(cardConfig) as LovelaceCard;
if (this.hass) {
element.hass = this.hass;
}
element.addEventListener(
"ll-rebuild",
(ev) => {
ev.stopPropagation();
this._rebuildCard(element, cardConfig);
},
{ once: true }
);
return element;
}
private _rebuildCard(
cardElToReplace: LovelaceCard,
config: LovelaceCardConfig
): void {
const newCardEl = this._createCardElement(config);
if (cardElToReplace.parentElement) {
cardElToReplace.parentElement.replaceChild(newCardEl, cardElToReplace);
}
this._cards = this._cards!.map((curCardEl) =>
curCardEl === cardElToReplace ? newCardEl : curCardEl
);
}
}