mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-18 23:06:40 +00:00
Fix router (#2943)
* Fix router * Fix demo * Extract update routes * Lint
This commit is contained in:
parent
2d75e797c7
commit
23ca1b972d
@ -96,7 +96,8 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<ha-demo><div id="ha-init-skeleton"></div></ha-demo>
|
||||
<div id="ha-init-skeleton"></div>
|
||||
<ha-demo></ha-demo>
|
||||
<script>
|
||||
var _gaq = [["_setAccount", "UA-57927901-5"], ["_trackPageview"]];
|
||||
(function(d, t) {
|
||||
|
@ -40,8 +40,8 @@
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<home-assistant>
|
||||
<div id='ha-init-skeleton'></div>
|
||||
<home-assistant>
|
||||
</home-assistant>
|
||||
<% if (!latestBuild) { %>
|
||||
<script src="/static/custom-elements-es5-adapter.js"></script>
|
||||
|
@ -54,7 +54,7 @@ export class HomeAssistantAppEl extends ext(HassBaseMixin(LitElement), [
|
||||
></app-location>
|
||||
${this._panelUrl === undefined || this._route === undefined
|
||||
? ""
|
||||
: hass && hass.states && hass.config && hass.panels && hass.services
|
||||
: hass && hass.states && hass.config && hass.services
|
||||
? html`
|
||||
<home-assistant-main
|
||||
.hass=${this.hass}
|
||||
|
@ -15,12 +15,12 @@ import { haStyle } from "../resources/styles";
|
||||
|
||||
@customElement("hass-loading-screen")
|
||||
class HassLoadingScreen extends LitElement {
|
||||
@property() public isRoot? = false;
|
||||
@property({ type: Boolean }) public rootnav? = false;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
return html`
|
||||
<app-toolbar>
|
||||
${this.isRoot
|
||||
${this.rootnav
|
||||
? html`
|
||||
<ha-menu-button></ha-menu-button>
|
||||
`
|
||||
|
@ -13,14 +13,13 @@ const extractPage = (path: string, defaultPage: string) => {
|
||||
: path.substr(1, subpathStart - 1);
|
||||
};
|
||||
|
||||
interface RouteOptions {
|
||||
export interface RouteOptions {
|
||||
tag: string;
|
||||
load: () => Promise<unknown>;
|
||||
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<void>;
|
||||
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,13 +124,12 @@ 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(() => {
|
||||
this._currentLoadProm = loadProm.then(
|
||||
() => {
|
||||
this._currentLoadProm = undefined;
|
||||
// Check if we're still trying to show the same page.
|
||||
if (this._currentPage !== newPage) {
|
||||
return;
|
||||
@ -139,21 +137,56 @@ export class HassRouterPage extends UpdatingElement {
|
||||
|
||||
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<void> {
|
||||
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<void> {
|
||||
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) {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -85,7 +85,7 @@ class LovelacePanel extends LitElement {
|
||||
}
|
||||
|
||||
return html`
|
||||
<hass-loading-screen></hass-loading-screen>
|
||||
<hass-loading-screen rootnav></hass-loading-screen>
|
||||
`;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user