mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-21 08:16:36 +00:00
Config panel routing (#2928)
* Config panel routing * Abstract routing * Convert partial-panel-resolver * decorator * Remove showMenu * Tweaks to make it faster * Rename update method * Less aggressive loading
This commit is contained in:
parent
8785b03fd8
commit
2349e2f251
16
src/common/dom/media_query.ts
Normal file
16
src/common/dom/media_query.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Attach a media query. Listener is called right away and when it matches.
|
||||||
|
* @param mediaQuery media query to match.
|
||||||
|
* @param listener listener to call when media query changes between match/unmatch
|
||||||
|
* @returns function to remove the listener.
|
||||||
|
*/
|
||||||
|
export const listenMediaQuery = (
|
||||||
|
mediaQuery: string,
|
||||||
|
matchesChanged: (matches: boolean) => void
|
||||||
|
) => {
|
||||||
|
const mql = matchMedia(mediaQuery);
|
||||||
|
const listener = (e) => matchesChanged(e.matches);
|
||||||
|
mql.addListener(listener);
|
||||||
|
matchesChanged(mql.matches);
|
||||||
|
return () => mql.removeListener(listener);
|
||||||
|
};
|
@ -47,6 +47,9 @@ export interface CloudWebhook {
|
|||||||
managed?: boolean;
|
managed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const fetchCloudStatus = (hass: HomeAssistant) =>
|
||||||
|
hass.callWS<CloudStatus>({ type: "cloud/status" });
|
||||||
|
|
||||||
export const createCloudhook = (hass: HomeAssistant, webhookId: string) =>
|
export const createCloudhook = (hass: HomeAssistant, webhookId: string) =>
|
||||||
hass.callWS<CloudWebhook>({
|
hass.callWS<CloudWebhook>({
|
||||||
type: "cloud/cloudhook/create",
|
type: "cloud/cloudhook/create",
|
||||||
|
@ -7,16 +7,28 @@ import {
|
|||||||
CSSResultArray,
|
CSSResultArray,
|
||||||
css,
|
css,
|
||||||
customElement,
|
customElement,
|
||||||
|
property,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../components/ha-menu-button";
|
import "../components/ha-menu-button";
|
||||||
|
import "../components/ha-paper-icon-button-arrow-prev";
|
||||||
import { haStyle } from "../resources/styles";
|
import { haStyle } from "../resources/styles";
|
||||||
|
|
||||||
@customElement("hass-loading-screen")
|
@customElement("hass-loading-screen")
|
||||||
class HassLoadingScreen extends LitElement {
|
class HassLoadingScreen extends LitElement {
|
||||||
|
@property() public isRoot? = false;
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-menu-button></ha-menu-button>
|
${this.isRoot
|
||||||
|
? html`
|
||||||
|
<ha-menu-button></ha-menu-button>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ha-paper-icon-button-arrow-prev
|
||||||
|
@click=${this._handleBack}
|
||||||
|
></ha-paper-icon-button-arrow-prev>
|
||||||
|
`}
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<paper-spinner-lite active></paper-spinner-lite>
|
<paper-spinner-lite active></paper-spinner-lite>
|
||||||
@ -24,6 +36,10 @@ class HassLoadingScreen extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleBack() {
|
||||||
|
history.back();
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultArray {
|
static get styles(): CSSResultArray {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
|
192
src/layouts/hass-router-page.ts
Normal file
192
src/layouts/hass-router-page.ts
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import { UpdatingElement, property, PropertyValues } from "lit-element";
|
||||||
|
import "./hass-error-screen";
|
||||||
|
import { Route } from "../types";
|
||||||
|
import { navigate } from "../common/navigate";
|
||||||
|
|
||||||
|
const extractPage = (path: string, defaultPage: string) => {
|
||||||
|
if (path === "") {
|
||||||
|
return defaultPage;
|
||||||
|
}
|
||||||
|
const subpathStart = path.indexOf("/", 1);
|
||||||
|
return subpathStart === -1
|
||||||
|
? path.substr(1)
|
||||||
|
: path.substr(1, subpathStart - 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface RouteOptions {
|
||||||
|
tag: string;
|
||||||
|
load: () => Promise<unknown>;
|
||||||
|
cache?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RouterOptions {
|
||||||
|
isRoot?: boolean;
|
||||||
|
defaultPage?: string;
|
||||||
|
preloadAll?: boolean;
|
||||||
|
cacheAll?: boolean;
|
||||||
|
showLoading?: boolean;
|
||||||
|
routes: {
|
||||||
|
[route: string]: RouteOptions;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time to wait for code to load before we show loading screen.
|
||||||
|
const LOADING_SCREEN_THRESHOLD = 400; // ms
|
||||||
|
|
||||||
|
export class HassRouterPage extends UpdatingElement {
|
||||||
|
protected static routerOptions: RouterOptions = { routes: {} };
|
||||||
|
|
||||||
|
protected static finalize() {
|
||||||
|
super.finalize();
|
||||||
|
this._routerOptions = this.routerOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _routerOptions: RouterOptions;
|
||||||
|
|
||||||
|
@property() public route!: Route;
|
||||||
|
private _currentPage = "";
|
||||||
|
private _cache = {};
|
||||||
|
|
||||||
|
protected update(changedProps: PropertyValues) {
|
||||||
|
super.update(changedProps);
|
||||||
|
|
||||||
|
if (!changedProps.has("route")) {
|
||||||
|
if (this.lastChild) {
|
||||||
|
this._updatePageEl(this.lastChild, changedProps);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const route = this.route;
|
||||||
|
|
||||||
|
const routerOptions = (this.constructor as typeof HassRouterPage)
|
||||||
|
._routerOptions;
|
||||||
|
const defaultPage = routerOptions.defaultPage || "";
|
||||||
|
|
||||||
|
if (route && route.path === "") {
|
||||||
|
navigate(this, `${route.prefix}/${defaultPage}`, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPage = route ? extractPage(route.path, defaultPage) : "not_found";
|
||||||
|
|
||||||
|
if (this._currentPage === newPage) {
|
||||||
|
if (this.lastChild) {
|
||||||
|
this._updatePageEl(this.lastChild, changedProps);
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
loadProm.catch(() => {
|
||||||
|
// Verify that we're still trying to show the same page.
|
||||||
|
if (this._currentPage !== newPage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes either loading screen or the panel
|
||||||
|
this.removeChild(this.lastChild!);
|
||||||
|
|
||||||
|
// Show error screen
|
||||||
|
const errorEl = document.createElement("hass-error-screen");
|
||||||
|
errorEl.error = `Error while loading page ${newPage}.`;
|
||||||
|
this.appendChild(errorEl);
|
||||||
|
});
|
||||||
|
|
||||||
|
// If we don't show loading screen, just show the panel.
|
||||||
|
// It will be automatically upgraded when loading done.
|
||||||
|
if (!routerOptions.showLoading) {
|
||||||
|
this._createPanel(routerOptions, newPage, routeOptions);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We are only going to show the loading screen after some time.
|
||||||
|
// That way we won't have a double fast flash on fast connections.
|
||||||
|
let created = false;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (created || this._currentPage !== newPage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show a loading screen.
|
||||||
|
if (this.lastChild) {
|
||||||
|
this.removeChild(this.lastChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadingEl = document.createElement("hass-loading-screen");
|
||||||
|
loadingEl.isRoot = routerOptions.isRoot;
|
||||||
|
this.appendChild(loadingEl);
|
||||||
|
}, LOADING_SCREEN_THRESHOLD);
|
||||||
|
|
||||||
|
loadProm.then(() => {
|
||||||
|
// Check if we're still trying to show the same page.
|
||||||
|
if (this._currentPage !== newPage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
created = true;
|
||||||
|
this._createPanel(routerOptions, newPage, routeOptions);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
|
||||||
|
const options = (this.constructor as typeof HassRouterPage)._routerOptions;
|
||||||
|
|
||||||
|
if (options.preloadAll) {
|
||||||
|
Object.values(options.routes).forEach((route) => route.load());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _updatePageEl(_pageEl, _changedProps?: PropertyValues) {
|
||||||
|
// default we do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createPanel(
|
||||||
|
routerOptions: RouterOptions,
|
||||||
|
page: string,
|
||||||
|
routeOptions: RouteOptions
|
||||||
|
) {
|
||||||
|
if (this.lastChild) {
|
||||||
|
this.removeChild(this.lastChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
const panelEl =
|
||||||
|
this._cache[page] || document.createElement(routeOptions.tag);
|
||||||
|
this._updatePageEl(panelEl);
|
||||||
|
this.appendChild(panelEl);
|
||||||
|
|
||||||
|
if (routerOptions.cacheAll || routeOptions.cache) {
|
||||||
|
this._cache[page] = panelEl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected get routeTail(): Route {
|
||||||
|
const route = this.route!;
|
||||||
|
const dividerPos = route.path.indexOf("/", 1);
|
||||||
|
return dividerPos === -1
|
||||||
|
? {
|
||||||
|
prefix: route.path,
|
||||||
|
path: "",
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
prefix: route.path.substr(0, dividerPos),
|
||||||
|
path: route.path.substr(dividerPos),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,252 +1,139 @@
|
|||||||
import { LitElement, html, PropertyValues, property } from "lit-element";
|
import { property, customElement } from "lit-element";
|
||||||
|
import { PolymerElement } from "@polymer/polymer";
|
||||||
|
|
||||||
import "./hass-loading-screen";
|
import { HomeAssistant } from "../types";
|
||||||
import "./hass-error-screen";
|
import { HassRouterPage, RouterOptions } from "./hass-router-page";
|
||||||
import { HomeAssistant, Panel, PanelElement, Route } from "../types";
|
|
||||||
|
|
||||||
// Cache of panel loading promises.
|
@customElement("partial-panel-resolver")
|
||||||
const LOADED: { [panel: string]: Promise<void> } = {};
|
class PartialPanelResolver extends HassRouterPage {
|
||||||
|
protected static routerOptions: RouterOptions = {
|
||||||
// Which panel elements we will cache.
|
isRoot: true,
|
||||||
// Maybe we can cache them all eventually, but not sure yet about
|
showLoading: true,
|
||||||
// unknown side effects (like history taking a lot of memory, reset needed)
|
routes: {
|
||||||
const CACHED_EL = ["lovelace", "states"];
|
calendar: {
|
||||||
|
tag: "ha-panel-calendar",
|
||||||
function ensureLoaded(panel): Promise<void> | null {
|
load: () =>
|
||||||
if (panel in LOADED) {
|
import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"),
|
||||||
return LOADED[panel];
|
},
|
||||||
}
|
config: {
|
||||||
|
tag: "ha-panel-config",
|
||||||
let imported;
|
load: () =>
|
||||||
// Name each panel we support here, that way Webpack knows about it.
|
import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"),
|
||||||
switch (panel) {
|
},
|
||||||
case "config":
|
custom: {
|
||||||
imported = import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config");
|
tag: "ha-panel-custom",
|
||||||
break;
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"),
|
||||||
case "custom":
|
},
|
||||||
imported = import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom");
|
"dev-event": {
|
||||||
break;
|
tag: "ha-panel-dev-event",
|
||||||
|
load: () =>
|
||||||
case "dev-event":
|
import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event"),
|
||||||
imported = import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event");
|
},
|
||||||
break;
|
"dev-info": {
|
||||||
|
tag: "ha-panel-dev-info",
|
||||||
case "dev-info":
|
load: () =>
|
||||||
imported = import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info");
|
import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info"),
|
||||||
break;
|
},
|
||||||
|
"dev-mqtt": {
|
||||||
case "dev-mqtt":
|
tag: "ha-panel-dev-mqtt",
|
||||||
imported = import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt");
|
load: () =>
|
||||||
break;
|
import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt"),
|
||||||
|
},
|
||||||
case "dev-service":
|
"dev-service": {
|
||||||
imported = import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service");
|
tag: "ha-panel-dev-service",
|
||||||
break;
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service"),
|
||||||
case "dev-state":
|
},
|
||||||
imported = import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state");
|
"dev-state": {
|
||||||
break;
|
tag: "ha-panel-dev-state",
|
||||||
|
load: () =>
|
||||||
case "dev-template":
|
import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state"),
|
||||||
imported = import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template");
|
},
|
||||||
break;
|
"dev-template": {
|
||||||
|
tag: "ha-panel-dev-template",
|
||||||
case "lovelace":
|
load: () =>
|
||||||
imported = import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace");
|
import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template"),
|
||||||
break;
|
},
|
||||||
|
lovelace: {
|
||||||
case "states":
|
cache: true,
|
||||||
imported = import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states");
|
tag: "ha-panel-lovelace",
|
||||||
break;
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"),
|
||||||
case "history":
|
},
|
||||||
imported = import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history");
|
states: {
|
||||||
break;
|
cache: true,
|
||||||
|
tag: "ha-panel-states",
|
||||||
case "iframe":
|
load: () =>
|
||||||
imported = import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe");
|
import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states"),
|
||||||
break;
|
},
|
||||||
|
history: {
|
||||||
case "kiosk":
|
tag: "ha-panel-history",
|
||||||
imported = import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk");
|
load: () =>
|
||||||
break;
|
import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history"),
|
||||||
|
},
|
||||||
case "logbook":
|
iframe: {
|
||||||
imported = import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook");
|
tag: "ha-panel-iframe",
|
||||||
break;
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe"),
|
||||||
case "mailbox":
|
},
|
||||||
imported = import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox");
|
kiosk: {
|
||||||
break;
|
tag: "ha-panel-kiosk",
|
||||||
|
load: () =>
|
||||||
case "map":
|
import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk"),
|
||||||
imported = import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map");
|
},
|
||||||
break;
|
logbook: {
|
||||||
|
tag: "ha-panel-logbook",
|
||||||
case "profile":
|
load: () =>
|
||||||
imported = import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile");
|
import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook"),
|
||||||
break;
|
},
|
||||||
|
mailbox: {
|
||||||
case "shopping-list":
|
tag: "ha-panel-mailbox",
|
||||||
imported = import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list");
|
load: () =>
|
||||||
break;
|
import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox"),
|
||||||
|
},
|
||||||
case "calendar":
|
map: {
|
||||||
imported = import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar");
|
tag: "ha-panel-map",
|
||||||
break;
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map"),
|
||||||
default:
|
},
|
||||||
imported = null;
|
profile: {
|
||||||
}
|
tag: "ha-panel-profile",
|
||||||
|
load: () =>
|
||||||
if (imported != null) {
|
import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile"),
|
||||||
LOADED[panel] = imported;
|
},
|
||||||
}
|
"shopping-list": {
|
||||||
|
tag: "ha-panel-shopping-list",
|
||||||
return imported;
|
load: () =>
|
||||||
}
|
import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list"),
|
||||||
|
},
|
||||||
class PartialPanelResolver extends LitElement {
|
},
|
||||||
|
};
|
||||||
@property() public hass?: HomeAssistant;
|
@property() public hass?: HomeAssistant;
|
||||||
@property() public narrow?: boolean;
|
@property() public narrow?: boolean;
|
||||||
@property() public showMenu?: boolean;
|
|
||||||
@property() public route?: Route | null;
|
|
||||||
|
|
||||||
@property() private _routeTail?: Route | null;
|
protected _updatePageEl(el) {
|
||||||
@property() private _panelEl?: PanelElement;
|
const hass = this.hass!;
|
||||||
@property() private _error?: boolean;
|
|
||||||
private _panel?: Panel;
|
|
||||||
private _cache: { [name: string]: PanelElement } = {};
|
|
||||||
|
|
||||||
protected render() {
|
|
||||||
if (this._error) {
|
|
||||||
return html`
|
|
||||||
<hass-error-screen
|
|
||||||
error="Error while loading this panel."
|
|
||||||
.narrow=${this.narrow}
|
|
||||||
.showMenu=${this.showMenu}
|
|
||||||
></hass-error-screen>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._panelEl) {
|
|
||||||
return html`
|
|
||||||
<hass-loading-screen
|
|
||||||
.narrow=${this.narrow}
|
|
||||||
.showMenu=${this.showMenu}
|
|
||||||
></hass-loading-screen>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return html`
|
|
||||||
${this._panelEl}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues) {
|
|
||||||
super.updated(changedProps);
|
|
||||||
if (!this.hass) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changedProps.has("route")) {
|
|
||||||
// Manual splitting
|
|
||||||
const route = this.route!;
|
|
||||||
const dividerPos = route.path.indexOf("/", 1);
|
|
||||||
this._routeTail =
|
|
||||||
dividerPos === -1
|
|
||||||
? {
|
|
||||||
prefix: route.path,
|
|
||||||
path: "",
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
prefix: route.path.substr(0, dividerPos),
|
|
||||||
path: route.path.substr(dividerPos),
|
|
||||||
};
|
|
||||||
|
|
||||||
// If just route changed, no need to process further.
|
|
||||||
if (changedProps.size === 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changedProps.has("hass")) {
|
|
||||||
const panel = this.hass.panels[this.hass.panelUrl];
|
|
||||||
|
|
||||||
if (panel !== this._panel) {
|
|
||||||
this._panel = panel;
|
|
||||||
this._panelEl = undefined;
|
|
||||||
|
|
||||||
// Found cached one, use that
|
|
||||||
if (panel.component_name in this._cache) {
|
|
||||||
this._panelEl = this._cache[panel.component_name];
|
|
||||||
this._updatePanel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadingProm = ensureLoaded(panel.component_name);
|
|
||||||
|
|
||||||
if (loadingProm === null) {
|
|
||||||
this._error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadingProm.then(
|
|
||||||
() => {
|
|
||||||
// If panel changed while loading.
|
|
||||||
if (this._panel !== panel) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._panelEl = (this._panelEl = document.createElement(
|
|
||||||
`ha-panel-${panel.component_name}`
|
|
||||||
)) as PanelElement;
|
|
||||||
|
|
||||||
if (CACHED_EL.includes(panel.component_name)) {
|
|
||||||
this._cache[panel.component_name] = this._panelEl;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._error = false;
|
|
||||||
this._updatePanel();
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
// tslint:disable-next-line
|
|
||||||
console.error("Error loading panel", err);
|
|
||||||
this._error = true;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._updatePanel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _updatePanel() {
|
|
||||||
const el = this._panelEl;
|
|
||||||
|
|
||||||
if (!el) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("setProperties" in el) {
|
if ("setProperties" in el) {
|
||||||
// As long as we have Polymer panels
|
// As long as we have Polymer panels
|
||||||
(el as any).setProperties({
|
(el as PolymerElement).setProperties({
|
||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
narrow: this.narrow,
|
narrow: this.narrow,
|
||||||
showMenu: this.showMenu,
|
route: this.routeTail,
|
||||||
route: this._routeTail,
|
panel: hass.panels[hass.panelUrl],
|
||||||
panel: this._panel,
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
el.hass = this.hass;
|
el.hass = hass;
|
||||||
el.narrow = this.narrow;
|
el.narrow = this.narrow;
|
||||||
el.showMenu = this.showMenu;
|
el.route = this.routeTail;
|
||||||
el.route = this._routeTail;
|
el.panel = hass.panels[hass.panelUrl];
|
||||||
el.panel = this._panel;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("partial-panel-resolver", PartialPanelResolver);
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"partial-panel-resolver": PartialPanelResolver;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,243 +0,0 @@
|
|||||||
import "@polymer/app-route/app-route";
|
|
||||||
import "@polymer/iron-media-query/iron-media-query";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
|
|
||||||
import isComponentLoaded from "../../common/config/is_component_loaded";
|
|
||||||
import EventsMixin from "../../mixins/events-mixin";
|
|
||||||
import NavigateMixin from "../../mixins/navigate-mixin";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin EventsMixin
|
|
||||||
* @appliesMixin NavigateMixin
|
|
||||||
*/
|
|
||||||
class HaPanelConfig extends EventsMixin(NavigateMixin(PolymerElement)) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<app-route
|
|
||||||
route="[[route]]"
|
|
||||||
pattern="/:page"
|
|
||||||
data="{{_routeData}}"
|
|
||||||
></app-route>
|
|
||||||
|
|
||||||
<iron-media-query query="(min-width: 1040px)" query-matches="{{wide}}">
|
|
||||||
</iron-media-query>
|
|
||||||
<iron-media-query
|
|
||||||
query="(min-width: 1296px)"
|
|
||||||
query-matches="{{wideSidebar}}"
|
|
||||||
>
|
|
||||||
</iron-media-query>
|
|
||||||
|
|
||||||
<template
|
|
||||||
is="dom-if"
|
|
||||||
if='[[_equals(_routeData.page, "area_registry")]]'
|
|
||||||
restamp
|
|
||||||
>
|
|
||||||
<ha-config-area-registry
|
|
||||||
page-name="area_registry"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-area-registry>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "core")]]' restamp>
|
|
||||||
<ha-config-core
|
|
||||||
page-name="core"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-core>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "cloud")]]' restamp>
|
|
||||||
<ha-config-cloud
|
|
||||||
page-name="cloud"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
cloud-status="[[_cloudStatus]]"
|
|
||||||
></ha-config-cloud>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "dashboard")]]'>
|
|
||||||
<ha-config-dashboard
|
|
||||||
page-name="dashboard"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
cloud-status="[[_cloudStatus]]"
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
show-menu="[[showMenu]]"
|
|
||||||
></ha-config-dashboard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template
|
|
||||||
is="dom-if"
|
|
||||||
if='[[_equals(_routeData.page, "automation")]]'
|
|
||||||
restamp
|
|
||||||
>
|
|
||||||
<ha-config-automation
|
|
||||||
page-name="automation"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-automation>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "script")]]' restamp>
|
|
||||||
<ha-config-script
|
|
||||||
page-name="script"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-script>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template
|
|
||||||
is="dom-if"
|
|
||||||
if='[[_equals(_routeData.page, "entity_registry")]]'
|
|
||||||
restamp
|
|
||||||
>
|
|
||||||
<ha-config-entity-registry
|
|
||||||
page-name="entity_registry"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-entity-registry>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "zha")]]' restamp>
|
|
||||||
<ha-config-zha
|
|
||||||
page-name="zha"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-zha>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "zwave")]]' restamp>
|
|
||||||
<ha-config-zwave
|
|
||||||
page-name="zwave"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-zwave>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "person")]]' restamp>
|
|
||||||
<ha-config-person
|
|
||||||
page-name="person"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-person>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template
|
|
||||||
is="dom-if"
|
|
||||||
if='[[_equals(_routeData.page, "customize")]]'
|
|
||||||
restamp
|
|
||||||
>
|
|
||||||
<ha-config-customize
|
|
||||||
page-name="customize"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
></ha-config-customize>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template
|
|
||||||
is="dom-if"
|
|
||||||
if='[[_equals(_routeData.page, "integrations")]]'
|
|
||||||
restamp
|
|
||||||
>
|
|
||||||
<ha-config-entries
|
|
||||||
route="[[route]]"
|
|
||||||
page-name="integrations"
|
|
||||||
hass="[[hass]]"
|
|
||||||
is-wide="[[isWide]]"
|
|
||||||
narrow="[[narrow]]"
|
|
||||||
></ha-config-entries>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "users")]]' restamp>
|
|
||||||
<ha-config-users
|
|
||||||
page-name="users"
|
|
||||||
route="[[route]]"
|
|
||||||
hass="[[hass]]"
|
|
||||||
></ha-config-users>
|
|
||||||
</template>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
narrow: Boolean,
|
|
||||||
showMenu: Boolean,
|
|
||||||
_cloudStatus: {
|
|
||||||
type: Object,
|
|
||||||
value: null,
|
|
||||||
},
|
|
||||||
|
|
||||||
route: {
|
|
||||||
type: Object,
|
|
||||||
observer: "_routeChanged",
|
|
||||||
},
|
|
||||||
|
|
||||||
_routeData: Object,
|
|
||||||
|
|
||||||
wide: Boolean,
|
|
||||||
wideSidebar: Boolean,
|
|
||||||
|
|
||||||
isWide: {
|
|
||||||
type: Boolean,
|
|
||||||
computed: "computeIsWide(showMenu, wideSidebar, wide)",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ready() {
|
|
||||||
super.ready();
|
|
||||||
if (isComponentLoaded(this.hass, "cloud")) {
|
|
||||||
this._updateCloudStatus();
|
|
||||||
}
|
|
||||||
this.addEventListener("ha-refresh-cloud-status", () =>
|
|
||||||
this._updateCloudStatus()
|
|
||||||
);
|
|
||||||
import(/* webpackChunkName: "panel-config-area-registry" */ "./area_registry/ha-config-area-registry");
|
|
||||||
import(/* webpackChunkName: "panel-config-automation" */ "./automation/ha-config-automation");
|
|
||||||
import(/* webpackChunkName: "panel-config-cloud" */ "./cloud/ha-config-cloud");
|
|
||||||
import(/* webpackChunkName: "panel-config-config" */ "./config-entries/ha-config-entries");
|
|
||||||
import(/* webpackChunkName: "panel-config-core" */ "./core/ha-config-core");
|
|
||||||
import(/* webpackChunkName: "panel-config-customize" */ "./customize/ha-config-customize");
|
|
||||||
import(/* webpackChunkName: "panel-config-dashboard" */ "./dashboard/ha-config-dashboard");
|
|
||||||
import(/* webpackChunkName: "panel-config-script" */ "./script/ha-config-script");
|
|
||||||
import(/* webpackChunkName: "panel-config-entity-registry" */ "./entity_registry/ha-config-entity-registry");
|
|
||||||
import(/* webpackChunkName: "panel-config-users" */ "./users/ha-config-users");
|
|
||||||
import(/* webpackChunkName: "panel-config-zha" */ "./zha/ha-config-zha");
|
|
||||||
import(/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave");
|
|
||||||
import(/* webpackChunkName: "panel-config-person" */ "./person/ha-config-person");
|
|
||||||
}
|
|
||||||
|
|
||||||
async _updateCloudStatus() {
|
|
||||||
this._cloudStatus = await this.hass.callWS({ type: "cloud/status" });
|
|
||||||
|
|
||||||
if (this._cloudStatus.cloud === "connecting") {
|
|
||||||
setTimeout(() => this._updateCloudStatus(), 5000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
computeIsWide(showMenu, wideSidebar, wide) {
|
|
||||||
return showMenu ? wideSidebar : wide;
|
|
||||||
}
|
|
||||||
|
|
||||||
_routeChanged(route) {
|
|
||||||
if (route.path === "" && route.prefix === "/config") {
|
|
||||||
this.navigate("/config/dashboard", true);
|
|
||||||
}
|
|
||||||
this.fire("iron-resize");
|
|
||||||
}
|
|
||||||
|
|
||||||
_equals(a, b) {
|
|
||||||
return a === b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-panel-config", HaPanelConfig);
|
|
142
src/panels/config/ha-panel-config.ts
Normal file
142
src/panels/config/ha-panel-config.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { property, PropertyValues, customElement } from "lit-element";
|
||||||
|
import "../../layouts/hass-loading-screen";
|
||||||
|
import isComponentLoaded from "../../common/config/is_component_loaded";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { CloudStatus, fetchCloudStatus } from "../../data/cloud";
|
||||||
|
import { listenMediaQuery } from "../../common/dom/media_query";
|
||||||
|
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
|
||||||
|
|
||||||
|
@customElement("ha-panel-config")
|
||||||
|
class HaPanelConfig extends HassRouterPage {
|
||||||
|
protected static routerOptions: RouterOptions = {
|
||||||
|
defaultPage: "dashboard",
|
||||||
|
cacheAll: true,
|
||||||
|
preloadAll: true,
|
||||||
|
routes: {
|
||||||
|
area_registry: {
|
||||||
|
tag: "ha-config-area-registry",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-area-registry" */ "./area_registry/ha-config-area-registry"),
|
||||||
|
},
|
||||||
|
automation: {
|
||||||
|
tag: "ha-config-automation",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-automation" */ "./automation/ha-config-automation"),
|
||||||
|
},
|
||||||
|
cloud: {
|
||||||
|
tag: "ha-config-cloud",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-cloud" */ "./cloud/ha-config-cloud"),
|
||||||
|
},
|
||||||
|
core: {
|
||||||
|
tag: "ha-config-core",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-core" */ "./core/ha-config-core"),
|
||||||
|
},
|
||||||
|
customize: {
|
||||||
|
tag: "ha-config-customize",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-customize" */ "./customize/ha-config-customize"),
|
||||||
|
},
|
||||||
|
dashboard: {
|
||||||
|
tag: "ha-config-dashboard",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-dashboard" */ "./dashboard/ha-config-dashboard"),
|
||||||
|
},
|
||||||
|
entity_registry: {
|
||||||
|
tag: "ha-config-entity-registry",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-entity-registry" */ "./entity_registry/ha-config-entity-registry"),
|
||||||
|
},
|
||||||
|
integrations: {
|
||||||
|
tag: "ha-config-integrations",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-integrations" */ "./integrations/ha-config-integrations"),
|
||||||
|
},
|
||||||
|
person: {
|
||||||
|
tag: "ha-config-person",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-person" */ "./person/ha-config-person"),
|
||||||
|
},
|
||||||
|
script: {
|
||||||
|
tag: "ha-config-script",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-script" */ "./script/ha-config-script"),
|
||||||
|
},
|
||||||
|
users: {
|
||||||
|
tag: "ha-config-users",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-users" */ "./users/ha-config-users"),
|
||||||
|
},
|
||||||
|
zha: {
|
||||||
|
tag: "ha-config-zha",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-zha" */ "./zha/ha-config-zha"),
|
||||||
|
},
|
||||||
|
zwave: {
|
||||||
|
tag: "ha-config-zwave",
|
||||||
|
load: () =>
|
||||||
|
import(/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public _wideSidebar: boolean = false;
|
||||||
|
@property() public _wide: boolean = false;
|
||||||
|
@property() private _cloudStatus?: CloudStatus;
|
||||||
|
|
||||||
|
private _listeners: Array<() => void> = [];
|
||||||
|
|
||||||
|
public connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this._listeners.push(
|
||||||
|
listenMediaQuery("(min-width: 1040px)", (matches) => {
|
||||||
|
this._wide = matches;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this._listeners.push(
|
||||||
|
listenMediaQuery("(min-width: 1296px)", (matches) => {
|
||||||
|
this._wideSidebar = matches;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
while (this._listeners.length) {
|
||||||
|
this._listeners.pop()!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
if (isComponentLoaded(this.hass, "cloud")) {
|
||||||
|
this._updateCloudStatus();
|
||||||
|
}
|
||||||
|
this.addEventListener("ha-refresh-cloud-status", () =>
|
||||||
|
this._updateCloudStatus()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _updatePageEl(el) {
|
||||||
|
el.route = this.route;
|
||||||
|
el.hass = this.hass;
|
||||||
|
el.isWide = this.hass.dockedSidebar ? this._wideSidebar : this._wide;
|
||||||
|
el.cloudStatus = this._cloudStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _updateCloudStatus() {
|
||||||
|
this._cloudStatus = await fetchCloudStatus(this.hass);
|
||||||
|
|
||||||
|
if (this._cloudStatus.cloud === "connecting") {
|
||||||
|
setTimeout(() => this._updateCloudStatus(), 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-panel-config": HaPanelConfig;
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ import NavigateMixin from "../../../mixins/navigate-mixin";
|
|||||||
import compare from "../../../common/string/compare";
|
import compare from "../../../common/string/compare";
|
||||||
import { fetchAreaRegistry } from "../../../data/area_registry";
|
import { fetchAreaRegistry } from "../../../data/area_registry";
|
||||||
|
|
||||||
class HaConfigEntries extends NavigateMixin(PolymerElement) {
|
class HaConfigIntegrations extends NavigateMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<app-route
|
<app-route
|
||||||
@ -158,4 +158,4 @@ class HaConfigEntries extends NavigateMixin(PolymerElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-config-entries", HaConfigEntries);
|
customElements.define("ha-config-integrations", HaConfigIntegrations);
|
@ -18,6 +18,7 @@ export const haStyle = css`
|
|||||||
}
|
}
|
||||||
|
|
||||||
app-toolbar ha-menu-button + [main-title],
|
app-toolbar ha-menu-button + [main-title],
|
||||||
|
app-toolbar ha-paper-icon-button-arrow-prev + [main-title],
|
||||||
app-toolbar paper-icon-button + [main-title] {
|
app-toolbar paper-icon-button + [main-title] {
|
||||||
margin-left: 24px;
|
margin-left: 24px;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user