diff --git a/demo/public/index.html b/demo/public/index.html
index a685e87125..5a9e3e49a7 100644
--- a/demo/public/index.html
+++ b/demo/public/index.html
@@ -96,7 +96,8 @@
-
+
+
+
-
<% if (!latestBuild) { %>
diff --git a/src/layouts/app/home-assistant.ts b/src/layouts/app/home-assistant.ts
index 62529ef38b..fe7307f8d9 100644
--- a/src/layouts/app/home-assistant.ts
+++ b/src/layouts/app/home-assistant.ts
@@ -54,7 +54,7 @@ export class HomeAssistantAppEl extends ext(HassBaseMixin(LitElement), [
>
${this._panelUrl === undefined || this._route === undefined
? ""
- : hass && hass.states && hass.config && hass.panels && hass.services
+ : hass && hass.states && hass.config && hass.services
? html`
- ${this.isRoot
+ ${this.rootnav
? html`
`
diff --git a/src/layouts/hass-router-page.ts b/src/layouts/hass-router-page.ts
index b46119c0e3..3d5cab0d09 100644
--- a/src/layouts/hass-router-page.ts
+++ b/src/layouts/hass-router-page.ts
@@ -13,14 +13,13 @@ const extractPage = (path: string, defaultPage: string) => {
: path.substr(1, subpathStart - 1);
};
-interface RouteOptions {
+export interface RouteOptions {
tag: string;
load: () => Promise;
cache?: boolean;
}
export interface RouterOptions {
- isRoot?: boolean;
defaultPage?: string;
preloadAll?: boolean;
cacheAll?: boolean;
@@ -34,17 +33,19 @@ export interface RouterOptions {
const LOADING_SCREEN_THRESHOLD = 400; // ms
export class HassRouterPage extends UpdatingElement {
- protected static routerOptions: RouterOptions = { routes: {} };
+ @property() public route?: Route;
- protected static finalize() {
- super.finalize();
- this._routerOptions = this.routerOptions;
- }
+ protected routerOptions!: RouterOptions;
- private static _routerOptions: RouterOptions;
-
- @property() public route!: Route;
+ /**
+ * Optional variable to define extra routes dynamically.
+ * It is preferred to use static routes.
+ */
+ protected extraRoutes?: {
+ [route: string]: RouteOptions;
+ };
private _currentPage = "";
+ private _currentLoadProm?: Promise;
private _cache = {};
protected update(changedProps: PropertyValues) {
@@ -52,15 +53,13 @@ export class HassRouterPage extends UpdatingElement {
if (!changedProps.has("route")) {
if (this.lastChild) {
- this._updatePageEl(this.lastChild, changedProps);
+ this.updatePageEl(this.lastChild, changedProps);
}
return;
}
const route = this.route;
-
- const routerOptions = (this.constructor as typeof HassRouterPage)
- ._routerOptions;
+ const routerOptions = this.routerOptions || { routes: {} };
const defaultPage = routerOptions.defaultPage || "";
if (route && route.path === "") {
@@ -71,22 +70,22 @@ export class HassRouterPage extends UpdatingElement {
if (this._currentPage === newPage) {
if (this.lastChild) {
- this._updatePageEl(this.lastChild, changedProps);
+ this.updatePageEl(this.lastChild, changedProps);
+ }
+ return;
+ }
+
+ const routeOptions = routerOptions.routes[newPage];
+
+ if (!routeOptions) {
+ this._currentPage = "";
+ if (this.lastChild) {
+ this.removeChild(this.lastChild);
}
return;
}
this._currentPage = newPage;
-
- const routeOptions = routerOptions.routes[newPage];
-
- if (!routeOptions) {
- if (this.lastChild) {
- this._updatePageEl(this.lastChild, changedProps);
- }
- return;
- }
-
const loadProm = routeOptions.load();
// Check when loading the page source failed.
@@ -125,35 +124,69 @@ export class HassRouterPage extends UpdatingElement {
if (this.lastChild) {
this.removeChild(this.lastChild);
}
-
- const loadingEl = document.createElement("hass-loading-screen");
- loadingEl.isRoot = routerOptions.isRoot;
- this.appendChild(loadingEl);
+ this.appendChild(this.createLoadingScreen());
}, LOADING_SCREEN_THRESHOLD);
- loadProm.then(() => {
- // Check if we're still trying to show the same page.
- if (this._currentPage !== newPage) {
- return;
- }
+ this._currentLoadProm = loadProm.then(
+ () => {
+ this._currentLoadProm = undefined;
+ // Check if we're still trying to show the same page.
+ if (this._currentPage !== newPage) {
+ return;
+ }
- created = true;
- this._createPanel(routerOptions, newPage, routeOptions);
- });
+ created = true;
+ this._createPanel(routerOptions, newPage, routeOptions);
+ },
+ () => {
+ this._currentLoadProm = undefined;
+ }
+ );
}
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
- const options = (this.constructor as typeof HassRouterPage)._routerOptions;
+ const options = this.routerOptions;
- if (options.preloadAll) {
+ if (options && options.preloadAll) {
Object.values(options.routes).forEach((route) => route.load());
return;
}
}
- protected _updatePageEl(_pageEl, _changedProps?: PropertyValues) {
+ protected createLoadingScreen() {
+ return document.createElement("hass-loading-screen");
+ }
+
+ /**
+ * Rebuild the current panel.
+ *
+ * Promise will resolve when rebuilding is done and DOM updated.
+ */
+ protected async rebuild(): Promise {
+ const oldRoute = this.route;
+
+ if (oldRoute === undefined) {
+ return;
+ }
+
+ this.route = undefined;
+ await this.updateComplete;
+ // Make sure that the parent didn't override this in the meanwhile.
+ if (this.route === undefined) {
+ this.route = oldRoute;
+ }
+ }
+
+ /**
+ * Promise that resolves when the page has rendered.
+ */
+ protected get pageRendered(): Promise {
+ return this.updateComplete.then(() => this._currentLoadProm);
+ }
+
+ protected updatePageEl(_pageEl, _changedProps?: PropertyValues) {
// default we do nothing
}
@@ -168,7 +201,7 @@ export class HassRouterPage extends UpdatingElement {
const panelEl =
this._cache[page] || document.createElement(routeOptions.tag);
- this._updatePageEl(panelEl);
+ this.updatePageEl(panelEl);
this.appendChild(panelEl);
if (routerOptions.cacheAll || routeOptions.cache) {
diff --git a/src/layouts/partial-panel-resolver.ts b/src/layouts/partial-panel-resolver.ts
index 7d5628fe82..0693e4b1f7 100644
--- a/src/layouts/partial-panel-resolver.ts
+++ b/src/layouts/partial-panel-resolver.ts
@@ -1,118 +1,96 @@
-import { property, customElement } from "lit-element";
+import { property, customElement, PropertyValues } from "lit-element";
import { PolymerElement } from "@polymer/polymer";
-import { HomeAssistant } from "../types";
-import { HassRouterPage, RouterOptions } from "./hass-router-page";
+import { HomeAssistant, Panels } from "../types";
+import {
+ HassRouterPage,
+ RouterOptions,
+ RouteOptions,
+} from "./hass-router-page";
+
+const CACHE_COMPONENTS = ["lovelace", "states"];
+const COMPONENTS = {
+ calendar: () =>
+ import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"),
+ config: () =>
+ import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"),
+ custom: () =>
+ import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"),
+ "dev-event": () =>
+ import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event"),
+ "dev-info": () =>
+ import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info"),
+ "dev-mqtt": () =>
+ import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt"),
+ "dev-service": () =>
+ import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service"),
+ "dev-state": () =>
+ import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state"),
+ "dev-template": () =>
+ import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template"),
+ lovelace: () =>
+ import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"),
+ states: () =>
+ import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states"),
+ history: () =>
+ import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history"),
+ iframe: () =>
+ import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe"),
+ kiosk: () =>
+ import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk"),
+ logbook: () =>
+ import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook"),
+ mailbox: () =>
+ import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox"),
+ map: () =>
+ import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map"),
+ profile: () =>
+ import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile"),
+ "shopping-list": () =>
+ import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list"),
+};
+
+const getRoutes = (panels: Panels): RouterOptions => {
+ const routes: { [route: string]: RouteOptions } = {};
+
+ Object.values(panels).forEach((panel) => {
+ routes[panel.url_path] = {
+ load: COMPONENTS[panel.component_name],
+ tag: `ha-panel-${panel.component_name}`,
+ cache: CACHE_COMPONENTS.includes(panel.component_name),
+ };
+ });
+
+ return {
+ showLoading: true,
+ routes,
+ };
+};
@customElement("partial-panel-resolver")
class PartialPanelResolver extends HassRouterPage {
- protected static routerOptions: RouterOptions = {
- isRoot: true,
- showLoading: true,
- routes: {
- calendar: {
- tag: "ha-panel-calendar",
- load: () =>
- import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"),
- },
- config: {
- tag: "ha-panel-config",
- load: () =>
- import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"),
- },
- custom: {
- tag: "ha-panel-custom",
- load: () =>
- import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"),
- },
- "dev-event": {
- tag: "ha-panel-dev-event",
- load: () =>
- import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event"),
- },
- "dev-info": {
- tag: "ha-panel-dev-info",
- load: () =>
- import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info"),
- },
- "dev-mqtt": {
- tag: "ha-panel-dev-mqtt",
- load: () =>
- import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt"),
- },
- "dev-service": {
- tag: "ha-panel-dev-service",
- load: () =>
- import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service"),
- },
- "dev-state": {
- tag: "ha-panel-dev-state",
- load: () =>
- import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state"),
- },
- "dev-template": {
- tag: "ha-panel-dev-template",
- load: () =>
- import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template"),
- },
- lovelace: {
- cache: true,
- tag: "ha-panel-lovelace",
- load: () =>
- import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"),
- },
- states: {
- cache: true,
- tag: "ha-panel-states",
- load: () =>
- import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states"),
- },
- history: {
- tag: "ha-panel-history",
- load: () =>
- import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history"),
- },
- iframe: {
- tag: "ha-panel-iframe",
- load: () =>
- import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe"),
- },
- kiosk: {
- tag: "ha-panel-kiosk",
- load: () =>
- import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk"),
- },
- logbook: {
- tag: "ha-panel-logbook",
- load: () =>
- import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook"),
- },
- mailbox: {
- tag: "ha-panel-mailbox",
- load: () =>
- import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox"),
- },
- map: {
- tag: "ha-panel-map",
- load: () =>
- import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map"),
- },
- profile: {
- tag: "ha-panel-profile",
- load: () =>
- import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile"),
- },
- "shopping-list": {
- tag: "ha-panel-shopping-list",
- load: () =>
- import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list"),
- },
- },
- };
@property() public hass?: HomeAssistant;
@property() public narrow?: boolean;
- protected _updatePageEl(el) {
+ protected updated(changedProps: PropertyValues) {
+ if (!changedProps.has("hass")) {
+ return;
+ }
+
+ const oldHass = changedProps.get("hass") as this["hass"];
+
+ if (!oldHass || oldHass.panels !== this.hass!.panels) {
+ this._updateRoutes();
+ }
+ }
+
+ protected createLoadingScreen() {
+ const el = super.createLoadingScreen();
+ el.rootnav = true;
+ return el;
+ }
+
+ protected updatePageEl(el) {
const hass = this.hass!;
if ("setProperties" in el) {
@@ -130,6 +108,17 @@ class PartialPanelResolver extends HassRouterPage {
el.panel = hass.panels[hass.panelUrl];
}
}
+
+ private async _updateRoutes() {
+ this.routerOptions = getRoutes(this.hass!.panels);
+ await this.rebuild();
+ await this.pageRendered;
+
+ const initEl = document.getElementById("ha-init-skeleton");
+ if (initEl) {
+ initEl.parentElement!.removeChild(initEl);
+ }
+ }
}
declare global {
diff --git a/src/panels/config/ha-panel-config.ts b/src/panels/config/ha-panel-config.ts
index aad87ca75a..8fbb0629b3 100644
--- a/src/panels/config/ha-panel-config.ts
+++ b/src/panels/config/ha-panel-config.ts
@@ -8,7 +8,11 @@ import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
@customElement("ha-panel-config")
class HaPanelConfig extends HassRouterPage {
- protected static routerOptions: RouterOptions = {
+ @property() public hass!: HomeAssistant;
+ @property() public _wideSidebar: boolean = false;
+ @property() public _wide: boolean = false;
+
+ protected routerOptions: RouterOptions = {
defaultPage: "dashboard",
cacheAll: true,
preloadAll: true,
@@ -81,9 +85,6 @@ class HaPanelConfig extends HassRouterPage {
},
};
- @property() public hass!: HomeAssistant;
- @property() public _wideSidebar: boolean = false;
- @property() public _wide: boolean = false;
@property() private _cloudStatus?: CloudStatus;
private _listeners: Array<() => void> = [];
@@ -119,7 +120,7 @@ class HaPanelConfig extends HassRouterPage {
);
}
- protected _updatePageEl(el) {
+ protected updatePageEl(el) {
el.route = this.route;
el.hass = this.hass;
el.isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide;
diff --git a/src/panels/lovelace/ha-panel-lovelace.ts b/src/panels/lovelace/ha-panel-lovelace.ts
index 8bbb0389b9..d59ffaea9f 100644
--- a/src/panels/lovelace/ha-panel-lovelace.ts
+++ b/src/panels/lovelace/ha-panel-lovelace.ts
@@ -85,7 +85,7 @@ class LovelacePanel extends LitElement {
}
return html`
-
+
`;
}