From b2243f480cfa3a0e1bae80e6503fc25c113aeef8 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Thu, 13 Feb 2020 21:13:48 -0800 Subject: [PATCH] Add option to lazy load cards (#4857) * Add option to lazy load cards * Lazy load header/footer elements * Lazy load rows * Clean up params * Rename last var --- .../create-element/create-badge-element.ts | 15 +++- .../create-element/create-card-element.ts | 55 +++++++------ .../create-element/create-element-base.ts | 78 ++++++++++++------- .../create-header-footer-element.ts | 16 +++- .../create-element/create-hui-element.ts | 4 +- .../create-element/create-row-element.ts | 56 +++++++------ 6 files changed, 134 insertions(+), 90 deletions(-) diff --git a/src/panels/lovelace/create-element/create-badge-element.ts b/src/panels/lovelace/create-element/create-badge-element.ts index 9fb4bc3b21..120a51b98e 100644 --- a/src/panels/lovelace/create-element/create-badge-element.ts +++ b/src/panels/lovelace/create-element/create-badge-element.ts @@ -1,10 +1,19 @@ -import "../badges/hui-entity-filter-badge"; import "../badges/hui-state-label-badge"; import { LovelaceBadgeConfig } from "../../../data/lovelace"; import { createLovelaceElement } from "./create-element-base"; -const BADGE_TYPES = new Set(["entity-filter", "error", "state-label"]); +const ALWAYS_LOADED_TYPES = new Set(["error", "state-label"]); +const LAZY_LOAD_TYPES = { + "entity-filter": () => import("../badges/hui-entity-filter-badge"), +}; export const createBadgeElement = (config: LovelaceBadgeConfig) => - createLovelaceElement("badge", config, BADGE_TYPES, undefined, "state-label"); + createLovelaceElement( + "badge", + config, + ALWAYS_LOADED_TYPES, + LAZY_LOAD_TYPES, + undefined, + "state-label" + ); diff --git a/src/panels/lovelace/create-element/create-card-element.ts b/src/panels/lovelace/create-element/create-card-element.ts index acfd982613..6692a1ea3d 100644 --- a/src/panels/lovelace/create-element/create-card-element.ts +++ b/src/panels/lovelace/create-element/create-card-element.ts @@ -1,57 +1,54 @@ -import "../cards/hui-alarm-panel-card"; -import "../cards/hui-conditional-card"; import "../cards/hui-entities-card"; import "../cards/hui-entity-button-card"; -import "../cards/hui-entity-filter-card"; import "../cards/hui-glance-card"; import "../cards/hui-history-graph-card"; import "../cards/hui-horizontal-stack-card"; -import "../cards/hui-iframe-card"; import "../cards/hui-light-card"; -import "../cards/hui-map-card"; -import "../cards/hui-markdown-card"; import "../cards/hui-media-control-card"; -import "../cards/hui-picture-card"; -import "../cards/hui-picture-elements-card"; -import "../cards/hui-picture-entity-card"; -import "../cards/hui-picture-glance-card"; -import "../cards/hui-plant-status-card"; import "../cards/hui-sensor-card"; -import "../cards/hui-vertical-stack-card"; -import "../cards/hui-shopping-list-card"; import "../cards/hui-thermostat-card"; +import "../cards/hui-vertical-stack-card"; import "../cards/hui-weather-forecast-card"; -import "../cards/hui-gauge-card"; import { LovelaceCardConfig } from "../../../data/lovelace"; import { createLovelaceElement } from "./create-element-base"; -const CARD_TYPES = new Set([ - "alarm-panel", - "conditional", +const ALWAYS_LOADED_TYPES = new Set([ "entities", "entity-button", - "entity-filter", "error", - "gauge", "glance", "history-graph", "horizontal-stack", - "iframe", "light", - "map", - "markdown", "media-control", - "picture", - "picture-elements", - "picture-entity", - "picture-glance", - "plant-status", "sensor", - "shopping-list", "thermostat", "vertical-stack", "weather-forecast", ]); +const LAZY_LOAD_TYPES = { + "alarm-panel": () => import("../cards/hui-alarm-panel-card"), + "entity-filter": () => import("../cards/hui-entity-filter-card"), + "picture-elements": () => import("../cards/hui-picture-elements-card"), + "picture-entity": () => import("../cards/hui-picture-entity-card"), + "picture-glance": () => import("../cards/hui-picture-glance-card"), + "plant-status": () => import("../cards/hui-plant-status-card"), + "shopping-list": () => import("../cards/hui-shopping-list-card"), + conditional: () => import("../cards/hui-conditional-card"), + gauge: () => import("../cards/hui-gauge-card"), + iframe: () => import("../cards/hui-iframe-card"), + map: () => import("../cards/hui-map-card"), + markdown: () => import("../cards/hui-markdown-card"), + picture: () => import("../cards/hui-picture-card"), +}; + export const createCardElement = (config: LovelaceCardConfig) => - createLovelaceElement("card", config, CARD_TYPES); + createLovelaceElement( + "card", + config, + ALWAYS_LOADED_TYPES, + LAZY_LOAD_TYPES, + undefined, + undefined + ); diff --git a/src/panels/lovelace/create-element/create-element-base.ts b/src/panels/lovelace/create-element/create-element-base.ts index 0650e0a450..61f1b6a432 100644 --- a/src/panels/lovelace/create-element/create-element-base.ts +++ b/src/panels/lovelace/create-element/create-element-base.ts @@ -50,10 +50,36 @@ const _createErrorElement = ( config: CreateElementConfigTypes[T]["config"] ): HuiErrorCard => createErrorCardElement(createErrorCardConfig(error, config)); +const _maybeCreate = ( + tag: string, + config: CreateElementConfigTypes[T]["config"] +) => { + if (customElements.get(tag)) { + return _createElement(tag, config); + } + + const element = _createErrorElement( + `Custom element doesn't exist: ${tag}.`, + config + ); + element.style.display = "None"; + const timer = window.setTimeout(() => { + element.style.display = ""; + }, TIMEOUT); + + customElements.whenDefined(tag).then(() => { + clearTimeout(timer); + fireEvent(element, "ll-rebuild"); + }); + + return element; +}; + export const createLovelaceElement = ( tagSuffix: T, config: CreateElementConfigTypes[T]["config"], - elementTypes: Set, + alwaysLoadTypes?: Set, + lazyLoadTypes?: { [domain: string]: () => unknown }, // Allow looking at "entity" in config and mapping that to a type domainTypes?: { _domain_not_found: string; [domain: string]: string }, // Default type if no type given. If given, entity types will not work. @@ -73,43 +99,35 @@ export const createLovelaceElement = ( } if (config.type && config.type.startsWith(CUSTOM_TYPE_PREFIX)) { - const tag = config.type.substr(CUSTOM_TYPE_PREFIX.length); - - if (customElements.get(tag)) { - return _createElement(tag, config); - } - const element = _createErrorElement( - `Custom element doesn't exist: ${tag}.`, - config - ); - element.style.display = "None"; - const timer = window.setTimeout(() => { - element.style.display = ""; - }, TIMEOUT); - - customElements.whenDefined(tag).then(() => { - clearTimeout(timer); - fireEvent(element, "ll-rebuild"); - }); - - return element; + return _maybeCreate(config.type.substr(CUSTOM_TYPE_PREFIX.length), config); } + let type: string | undefined; + // config.type has priority over config.entity, but defaultType has not. // @ts-ignore if (domainTypes && !config.type && config.entity) { // @ts-ignore const domain = config.entity.split(".", 1)[0]; - return _createElement( - `hui-${domainTypes![domain] || - domainTypes!._domain_not_found}-entity-${tagSuffix}`, - config - ); + type = `${domainTypes![domain] || domainTypes!._domain_not_found}-entity`; + } else { + type = config.type || defaultType; } - const type = config.type || defaultType; + if (type === undefined) { + return _createErrorElement(`No type specified`, config); + } - return type !== undefined && elementTypes.has(type) - ? _createElement(`hui-${type}-${tagSuffix}`, config) - : _createErrorElement(`Unknown type encountered: ${type}.`, config); + const tag = `hui-${type}-${tagSuffix}`; + + if (lazyLoadTypes && type in lazyLoadTypes) { + lazyLoadTypes[type](); + return _maybeCreate(tag, config); + } + + if (alwaysLoadTypes && alwaysLoadTypes.has(type)) { + return _createElement(tag, config); + } + + return _createErrorElement(`Unknown type encountered: ${type}.`, config); }; diff --git a/src/panels/lovelace/create-element/create-header-footer-element.ts b/src/panels/lovelace/create-element/create-header-footer-element.ts index 28d74d148b..f2e2859f3c 100644 --- a/src/panels/lovelace/create-element/create-header-footer-element.ts +++ b/src/panels/lovelace/create-element/create-header-footer-element.ts @@ -1,9 +1,17 @@ -import "../header-footer/hui-picture-header-footer"; -import "../header-footer/hui-buttons-header-footer"; import { LovelaceHeaderFooterConfig } from "../header-footer/types"; import { createLovelaceElement } from "./create-element-base"; -const SPECIAL_TYPES = new Set(["picture", "buttons"]); +const LAZY_LOAD_TYPES = { + picture: () => import("../header-footer/hui-picture-header-footer"), + buttons: () => import("../header-footer/hui-buttons-header-footer"), +}; export const createHeaderFooterElement = (config: LovelaceHeaderFooterConfig) => - createLovelaceElement("header-footer", config, SPECIAL_TYPES); + createLovelaceElement( + "header-footer", + config, + undefined, + LAZY_LOAD_TYPES, + undefined, + undefined + ); diff --git a/src/panels/lovelace/create-element/create-hui-element.ts b/src/panels/lovelace/create-element/create-hui-element.ts index 1645737bbb..d61546046a 100644 --- a/src/panels/lovelace/create-element/create-hui-element.ts +++ b/src/panels/lovelace/create-element/create-hui-element.ts @@ -9,7 +9,7 @@ import "../elements/hui-state-label-element"; import { LovelaceElementConfig } from "../elements/types"; import { createLovelaceElement } from "./create-element-base"; -const ELEMENT_TYPES = new Set([ +const ALWAYS_LOADED_TYPES = new Set([ "conditional", "icon", "image", @@ -20,4 +20,4 @@ const ELEMENT_TYPES = new Set([ ]); export const createHuiElement = (config: LovelaceElementConfig) => - createLovelaceElement("element", config, ELEMENT_TYPES); + createLovelaceElement("element", config, ALWAYS_LOADED_TYPES); diff --git a/src/panels/lovelace/create-element/create-row-element.ts b/src/panels/lovelace/create-element/create-row-element.ts index 207c250c2e..b33b419667 100644 --- a/src/panels/lovelace/create-element/create-row-element.ts +++ b/src/panels/lovelace/create-element/create-row-element.ts @@ -1,36 +1,41 @@ -import "../entity-rows/hui-climate-entity-row"; -import "../entity-rows/hui-cover-entity-row"; -import "../entity-rows/hui-group-entity-row"; -import "../entity-rows/hui-input-datetime-entity-row"; -import "../entity-rows/hui-input-number-entity-row"; -import "../entity-rows/hui-input-select-entity-row"; -import "../entity-rows/hui-input-text-entity-row"; -import "../entity-rows/hui-lock-entity-row"; import "../entity-rows/hui-media-player-entity-row"; import "../entity-rows/hui-scene-entity-row"; import "../entity-rows/hui-script-entity-row"; import "../entity-rows/hui-sensor-entity-row"; import "../entity-rows/hui-text-entity-row"; -import "../entity-rows/hui-timer-entity-row"; import "../entity-rows/hui-toggle-entity-row"; import "../special-rows/hui-call-service-row"; -import "../special-rows/hui-conditional-row"; -import "../special-rows/hui-divider-row"; -import "../special-rows/hui-section-row"; -import "../special-rows/hui-weblink-row"; -import "../special-rows/hui-cast-row"; import { EntityConfig } from "../entity-rows/types"; import { createLovelaceElement } from "./create-element-base"; -const SPECIAL_TYPES = new Set([ +const ALWAYS_LOADED_TYPES = new Set([ + "media-player-entity", + "scene-entity", + "script-entity", + "sensor-entity", + "text-entity", + "toggle-entity", "call-service", - "cast", - "conditional", - "divider", - "section", - "select", - "weblink", ]); +const LAZY_LOAD_TYPES = { + "climate-entity": () => import("../entity-rows/hui-climate-entity-row"), + "cover-entity": () => import("../entity-rows/hui-cover-entity-row"), + "group-entity": () => import("../entity-rows/hui-group-entity-row"), + "input-datetime-entity": () => + import("../entity-rows/hui-input-datetime-entity-row"), + "input-number-entity": () => + import("../entity-rows/hui-input-number-entity-row"), + "input-select-entity": () => + import("../entity-rows/hui-input-select-entity-row"), + "input-text-entity": () => import("../entity-rows/hui-input-text-entity-row"), + "lock-entity": () => import("../entity-rows/hui-lock-entity-row"), + "timer-entity": () => import("../entity-rows/hui-timer-entity-row"), + conditional: () => import("../special-rows/hui-conditional-row"), + divider: () => import("../special-rows/hui-divider-row"), + section: () => import("../special-rows/hui-section-row"), + weblink: () => import("../special-rows/hui-weblink-row"), + cast: () => import("../special-rows/hui-cast-row"), +}; const DOMAIN_TO_ELEMENT_TYPE = { _domain_not_found: "text", alert: "toggle", @@ -60,4 +65,11 @@ const DOMAIN_TO_ELEMENT_TYPE = { }; export const createRowElement = (config: EntityConfig) => - createLovelaceElement("row", config, SPECIAL_TYPES, DOMAIN_TO_ELEMENT_TYPE); + createLovelaceElement( + "row", + config, + ALWAYS_LOADED_TYPES, + LAZY_LOAD_TYPES, + DOMAIN_TO_ELEMENT_TYPE, + undefined + );