mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-17 14:26:35 +00:00
Add rebuild support to editor preview (#4932)
* Add rebuild support to editor preview * getLovelaceCardClass function added * Use error class * Tiny cleanup * Misplaced comment
This commit is contained in:
parent
6d54496187
commit
52609dded9
@ -1,7 +0,0 @@
|
|||||||
const CUSTOM_TYPE_PREFIX = "custom:";
|
|
||||||
|
|
||||||
export function getCardElementTag(type: string): string {
|
|
||||||
return type.startsWith(CUSTOM_TYPE_PREFIX)
|
|
||||||
? type.substr(CUSTOM_TYPE_PREFIX.length)
|
|
||||||
: `hui-${type}-card`;
|
|
||||||
}
|
|
@ -11,7 +11,10 @@ import "../cards/hui-thermostat-card";
|
|||||||
import "../cards/hui-vertical-stack-card";
|
import "../cards/hui-vertical-stack-card";
|
||||||
import "../cards/hui-weather-forecast-card";
|
import "../cards/hui-weather-forecast-card";
|
||||||
import { LovelaceCardConfig } from "../../../data/lovelace";
|
import { LovelaceCardConfig } from "../../../data/lovelace";
|
||||||
import { createLovelaceElement } from "./create-element-base";
|
import {
|
||||||
|
createLovelaceElement,
|
||||||
|
getLovelaceElementClass,
|
||||||
|
} from "./create-element-base";
|
||||||
|
|
||||||
const ALWAYS_LOADED_TYPES = new Set([
|
const ALWAYS_LOADED_TYPES = new Set([
|
||||||
"entities",
|
"entities",
|
||||||
@ -56,3 +59,6 @@ export const createCardElement = (config: LovelaceCardConfig) =>
|
|||||||
undefined,
|
undefined,
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getCardElementClass = (type: string) =>
|
||||||
|
getLovelaceElementClass(type, "card", ALWAYS_LOADED_TYPES, LAZY_LOAD_TYPES);
|
||||||
|
@ -7,7 +7,12 @@ import {
|
|||||||
createErrorCardElement,
|
createErrorCardElement,
|
||||||
createErrorCardConfig,
|
createErrorCardConfig,
|
||||||
} from "../cards/hui-error-card";
|
} from "../cards/hui-error-card";
|
||||||
import { LovelaceCard, LovelaceBadge, LovelaceHeaderFooter } from "../types";
|
import {
|
||||||
|
LovelaceCard,
|
||||||
|
LovelaceBadge,
|
||||||
|
LovelaceHeaderFooter,
|
||||||
|
LovelaceCardConstructor,
|
||||||
|
} from "../types";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { LovelaceElementConfig, LovelaceElement } from "../elements/types";
|
import { LovelaceElementConfig, LovelaceElement } from "../elements/types";
|
||||||
import { LovelaceRow, LovelaceRowConfig } from "../entity-rows/types";
|
import { LovelaceRow, LovelaceRowConfig } from "../entity-rows/types";
|
||||||
@ -17,13 +22,30 @@ const CUSTOM_TYPE_PREFIX = "custom:";
|
|||||||
const TIMEOUT = 2000;
|
const TIMEOUT = 2000;
|
||||||
|
|
||||||
interface CreateElementConfigTypes {
|
interface CreateElementConfigTypes {
|
||||||
card: { config: LovelaceCardConfig; element: LovelaceCard };
|
card: {
|
||||||
badge: { config: LovelaceBadgeConfig; element: LovelaceBadge };
|
config: LovelaceCardConfig;
|
||||||
element: { config: LovelaceElementConfig; element: LovelaceElement };
|
element: LovelaceCard;
|
||||||
row: { config: LovelaceRowConfig; element: LovelaceRow };
|
constructor: LovelaceCardConstructor;
|
||||||
|
};
|
||||||
|
badge: {
|
||||||
|
config: LovelaceBadgeConfig;
|
||||||
|
element: LovelaceBadge;
|
||||||
|
constructor: unknown;
|
||||||
|
};
|
||||||
|
element: {
|
||||||
|
config: LovelaceElementConfig;
|
||||||
|
element: LovelaceElement;
|
||||||
|
constructor: unknown;
|
||||||
|
};
|
||||||
|
row: {
|
||||||
|
config: LovelaceRowConfig;
|
||||||
|
element: LovelaceRow;
|
||||||
|
constructor: unknown;
|
||||||
|
};
|
||||||
"header-footer": {
|
"header-footer": {
|
||||||
config: LovelaceHeaderFooterConfig;
|
config: LovelaceHeaderFooterConfig;
|
||||||
element: LovelaceHeaderFooter;
|
element: LovelaceHeaderFooter;
|
||||||
|
constructor: unknown;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,11 +97,16 @@ const _maybeCreate = <T extends keyof CreateElementConfigTypes>(
|
|||||||
return element;
|
return element;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _getCustomTag = (type: string) =>
|
||||||
|
type.startsWith(CUSTOM_TYPE_PREFIX)
|
||||||
|
? type.substr(CUSTOM_TYPE_PREFIX.length)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
export const createLovelaceElement = <T extends keyof CreateElementConfigTypes>(
|
export const createLovelaceElement = <T extends keyof CreateElementConfigTypes>(
|
||||||
tagSuffix: T,
|
tagSuffix: T,
|
||||||
config: CreateElementConfigTypes[T]["config"],
|
config: CreateElementConfigTypes[T]["config"],
|
||||||
alwaysLoadTypes?: Set<string>,
|
alwaysLoadTypes?: Set<string>,
|
||||||
lazyLoadTypes?: { [domain: string]: () => unknown },
|
lazyLoadTypes?: { [domain: string]: () => Promise<unknown> },
|
||||||
// Allow looking at "entity" in config and mapping that to a type
|
// Allow looking at "entity" in config and mapping that to a type
|
||||||
domainTypes?: { _domain_not_found: string; [domain: string]: string },
|
domainTypes?: { _domain_not_found: string; [domain: string]: string },
|
||||||
// Default type if no type given. If given, entity types will not work.
|
// Default type if no type given. If given, entity types will not work.
|
||||||
@ -98,8 +125,10 @@ export const createLovelaceElement = <T extends keyof CreateElementConfigTypes>(
|
|||||||
return _createErrorElement("No card type configured.", config);
|
return _createErrorElement("No card type configured.", config);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.type && config.type.startsWith(CUSTOM_TYPE_PREFIX)) {
|
const customTag = config.type ? _getCustomTag(config.type) : undefined;
|
||||||
return _maybeCreate(config.type.substr(CUSTOM_TYPE_PREFIX.length), config);
|
|
||||||
|
if (customTag) {
|
||||||
|
return _maybeCreate(customTag, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
let type: string | undefined;
|
let type: string | undefined;
|
||||||
@ -131,3 +160,44 @@ export const createLovelaceElement = <T extends keyof CreateElementConfigTypes>(
|
|||||||
|
|
||||||
return _createErrorElement(`Unknown type encountered: ${type}.`, config);
|
return _createErrorElement(`Unknown type encountered: ${type}.`, config);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getLovelaceElementClass = async <
|
||||||
|
T extends keyof CreateElementConfigTypes
|
||||||
|
>(
|
||||||
|
type: string,
|
||||||
|
tagSuffix: T,
|
||||||
|
alwaysLoadTypes?: Set<string>,
|
||||||
|
lazyLoadTypes?: { [domain: string]: () => Promise<unknown> }
|
||||||
|
): Promise<CreateElementConfigTypes[T]["constructor"]> => {
|
||||||
|
const customTag = _getCustomTag(type);
|
||||||
|
|
||||||
|
if (customTag) {
|
||||||
|
const customCls = customElements.get(customTag);
|
||||||
|
return customCls
|
||||||
|
? customCls
|
||||||
|
: new Promise((resolve, reject) => {
|
||||||
|
// We will give custom components up to TIMEOUT seconds to get defined
|
||||||
|
setTimeout(
|
||||||
|
() => reject(new Error(`Custom element not found: ${customTag}`)),
|
||||||
|
TIMEOUT
|
||||||
|
);
|
||||||
|
|
||||||
|
customElements
|
||||||
|
.whenDefined(customTag)
|
||||||
|
.then(() => resolve(customElements.get(customTag)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const tag = `hui-${type}-${tagSuffix}`;
|
||||||
|
const cls = customElements.get(tag);
|
||||||
|
|
||||||
|
if (alwaysLoadTypes && type in alwaysLoadTypes) {
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lazyLoadTypes && type in lazyLoadTypes) {
|
||||||
|
return cls || lazyLoadTypes[type]().then(() => customElements.get(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Unknown type: ${type}`);
|
||||||
|
};
|
||||||
|
@ -14,7 +14,6 @@ import "@material/mwc-button";
|
|||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||||
import { LovelaceCardEditor } from "../../types";
|
import { LovelaceCardEditor } from "../../types";
|
||||||
import { getCardElementTag } from "../../common/get-card-element-tag";
|
|
||||||
import { computeRTL } from "../../../../common/util/compute_rtl";
|
import { computeRTL } from "../../../../common/util/compute_rtl";
|
||||||
|
|
||||||
import "../../../../components/ha-code-editor";
|
import "../../../../components/ha-code-editor";
|
||||||
@ -23,6 +22,7 @@ import "../../../../components/ha-code-editor";
|
|||||||
import { HaCodeEditor } from "../../../../components/ha-code-editor";
|
import { HaCodeEditor } from "../../../../components/ha-code-editor";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { EntityConfig } from "../../entity-rows/types";
|
import { EntityConfig } from "../../entity-rows/types";
|
||||||
|
import { getCardElementClass } from "../../create-element/create-card-element";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HASSDomEvents {
|
interface HASSDomEvents {
|
||||||
@ -215,13 +215,7 @@ export class HuiCardEditor extends LitElement {
|
|||||||
throw new Error("No card type defined");
|
throw new Error("No card type defined");
|
||||||
}
|
}
|
||||||
|
|
||||||
const tag = getCardElementTag(cardType);
|
const elClass = await getCardElementClass(cardType);
|
||||||
|
|
||||||
// Check if the card type exists
|
|
||||||
const elClass = customElements.get(tag);
|
|
||||||
if (!elClass) {
|
|
||||||
throw new Error(`Unknown card type encountered: ${cardType}.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._loading = true;
|
this._loading = true;
|
||||||
// Check if a GUI editor exists
|
// Check if a GUI editor exists
|
||||||
|
@ -10,9 +10,9 @@ import "@material/mwc-button";
|
|||||||
|
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||||
import { getCardElementTag } from "../../common/get-card-element-tag";
|
|
||||||
import { CardPickTarget } from "../types";
|
import { CardPickTarget } from "../types";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { getCardElementClass } from "../../create-element/create-card-element";
|
||||||
|
|
||||||
const cards: string[] = [
|
const cards: string[] = [
|
||||||
"alarm-panel",
|
"alarm-panel",
|
||||||
@ -94,15 +94,14 @@ export class HuiCardPicker extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _cardPicked(ev: Event): void {
|
private async _cardPicked(ev: Event): Promise<void> {
|
||||||
const type = (ev.currentTarget! as CardPickTarget).type;
|
const type = (ev.currentTarget! as CardPickTarget).type;
|
||||||
const tag = getCardElementTag(type);
|
|
||||||
|
|
||||||
const elClass = customElements.get(tag);
|
const elClass = await getCardElementClass(type);
|
||||||
let config: LovelaceCardConfig = { type };
|
let config: LovelaceCardConfig = { type };
|
||||||
|
|
||||||
if (elClass && elClass.getStubConfig) {
|
if (elClass && elClass.getStubConfig) {
|
||||||
const cardConfig = elClass.getStubConfig(this.hass);
|
const cardConfig = elClass.getStubConfig(this.hass!);
|
||||||
config = { ...config, ...cardConfig };
|
config = { ...config, ...cardConfig };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,13 +7,23 @@ import { HomeAssistant } from "../../../../types";
|
|||||||
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||||
import { LovelaceCard } from "../../types";
|
import { LovelaceCard } from "../../types";
|
||||||
import { ConfigError } from "../types";
|
import { ConfigError } from "../types";
|
||||||
import { getCardElementTag } from "../../common/get-card-element-tag";
|
|
||||||
import { createErrorCardConfig } from "../../cards/hui-error-card";
|
import { createErrorCardConfig } from "../../cards/hui-error-card";
|
||||||
import { computeRTL } from "../../../../common/util/compute_rtl";
|
import { computeRTL } from "../../../../common/util/compute_rtl";
|
||||||
|
|
||||||
export class HuiCardPreview extends HTMLElement {
|
export class HuiCardPreview extends HTMLElement {
|
||||||
private _hass?: HomeAssistant;
|
private _hass?: HomeAssistant;
|
||||||
private _element?: LovelaceCard;
|
private _element?: LovelaceCard;
|
||||||
|
private _config?: LovelaceCardConfig;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.addEventListener("ll-rebuild", () => {
|
||||||
|
this._cleanup();
|
||||||
|
if (this._config) {
|
||||||
|
this.config = this._config;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
set hass(hass: HomeAssistant) {
|
set hass(hass: HomeAssistant) {
|
||||||
if (!this._hass || this._hass.language !== hass.language) {
|
if (!this._hass || this._hass.language !== hass.language) {
|
||||||
@ -36,6 +46,9 @@ export class HuiCardPreview extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set config(configValue: LovelaceCardConfig) {
|
set config(configValue: LovelaceCardConfig) {
|
||||||
|
const curConfig = this._config;
|
||||||
|
this._config = configValue;
|
||||||
|
|
||||||
if (!configValue) {
|
if (!configValue) {
|
||||||
this._cleanup();
|
this._cleanup();
|
||||||
return;
|
return;
|
||||||
@ -53,9 +66,7 @@ export class HuiCardPreview extends HTMLElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tag = getCardElementTag(configValue.type);
|
if (curConfig && configValue.type === curConfig.type) {
|
||||||
|
|
||||||
if (tag.toUpperCase() === this._element.tagName) {
|
|
||||||
try {
|
try {
|
||||||
this._element.setConfig(deepClone(configValue));
|
this._element.setConfig(deepClone(configValue));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant, Constructor } from "../../types";
|
||||||
import {
|
import {
|
||||||
LovelaceCardConfig,
|
LovelaceCardConfig,
|
||||||
LovelaceConfig,
|
LovelaceConfig,
|
||||||
@ -37,6 +37,11 @@ export interface LovelaceCard extends HTMLElement {
|
|||||||
setConfig(config: LovelaceCardConfig): void;
|
setConfig(config: LovelaceCardConfig): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface LovelaceCardConstructor extends Constructor<LovelaceCard> {
|
||||||
|
getStubConfig?: (hass: HomeAssistant) => LovelaceCardConfig;
|
||||||
|
getConfigElement?: () => LovelaceCardEditor;
|
||||||
|
}
|
||||||
|
|
||||||
export interface LovelaceHeaderFooter extends HTMLElement {
|
export interface LovelaceHeaderFooter extends HTMLElement {
|
||||||
hass?: HomeAssistant;
|
hass?: HomeAssistant;
|
||||||
setConfig(config: LovelaceHeaderFooterConfig): void;
|
setConfig(config: LovelaceHeaderFooterConfig): void;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user