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;
|
||||
}
|
||||
|
||||
export const fetchCloudStatus = (hass: HomeAssistant) =>
|
||||
hass.callWS<CloudStatus>({ type: "cloud/status" });
|
||||
|
||||
export const createCloudhook = (hass: HomeAssistant, webhookId: string) =>
|
||||
hass.callWS<CloudWebhook>({
|
||||
type: "cloud/cloudhook/create",
|
||||
|
@ -7,16 +7,28 @@ import {
|
||||
CSSResultArray,
|
||||
css,
|
||||
customElement,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import "../components/ha-menu-button";
|
||||
import "../components/ha-paper-icon-button-arrow-prev";
|
||||
import { haStyle } from "../resources/styles";
|
||||
|
||||
@customElement("hass-loading-screen")
|
||||
class HassLoadingScreen extends LitElement {
|
||||
@property() public isRoot? = false;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
return html`
|
||||
<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>
|
||||
<div class="content">
|
||||
<paper-spinner-lite active></paper-spinner-lite>
|
||||
@ -24,6 +36,10 @@ class HassLoadingScreen extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
private _handleBack() {
|
||||
history.back();
|
||||
}
|
||||
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
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 "./hass-error-screen";
|
||||
import { HomeAssistant, Panel, PanelElement, Route } from "../types";
|
||||
import { HomeAssistant } from "../types";
|
||||
import { HassRouterPage, RouterOptions } from "./hass-router-page";
|
||||
|
||||
// Cache of panel loading promises.
|
||||
const LOADED: { [panel: string]: Promise<void> } = {};
|
||||
|
||||
// Which panel elements we will cache.
|
||||
// Maybe we can cache them all eventually, but not sure yet about
|
||||
// unknown side effects (like history taking a lot of memory, reset needed)
|
||||
const CACHED_EL = ["lovelace", "states"];
|
||||
|
||||
function ensureLoaded(panel): Promise<void> | null {
|
||||
if (panel in LOADED) {
|
||||
return LOADED[panel];
|
||||
}
|
||||
|
||||
let imported;
|
||||
// Name each panel we support here, that way Webpack knows about it.
|
||||
switch (panel) {
|
||||
case "config":
|
||||
imported = import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config");
|
||||
break;
|
||||
|
||||
case "custom":
|
||||
imported = import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom");
|
||||
break;
|
||||
|
||||
case "dev-event":
|
||||
imported = import(/* webpackChunkName: "panel-dev-event" */ "../panels/dev-event/ha-panel-dev-event");
|
||||
break;
|
||||
|
||||
case "dev-info":
|
||||
imported = import(/* webpackChunkName: "panel-dev-info" */ "../panels/dev-info/ha-panel-dev-info");
|
||||
break;
|
||||
|
||||
case "dev-mqtt":
|
||||
imported = import(/* webpackChunkName: "panel-dev-mqtt" */ "../panels/dev-mqtt/ha-panel-dev-mqtt");
|
||||
break;
|
||||
|
||||
case "dev-service":
|
||||
imported = import(/* webpackChunkName: "panel-dev-service" */ "../panels/dev-service/ha-panel-dev-service");
|
||||
break;
|
||||
|
||||
case "dev-state":
|
||||
imported = import(/* webpackChunkName: "panel-dev-state" */ "../panels/dev-state/ha-panel-dev-state");
|
||||
break;
|
||||
|
||||
case "dev-template":
|
||||
imported = import(/* webpackChunkName: "panel-dev-template" */ "../panels/dev-template/ha-panel-dev-template");
|
||||
break;
|
||||
|
||||
case "lovelace":
|
||||
imported = import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace");
|
||||
break;
|
||||
|
||||
case "states":
|
||||
imported = import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states");
|
||||
break;
|
||||
|
||||
case "history":
|
||||
imported = import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history");
|
||||
break;
|
||||
|
||||
case "iframe":
|
||||
imported = import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe");
|
||||
break;
|
||||
|
||||
case "kiosk":
|
||||
imported = import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk");
|
||||
break;
|
||||
|
||||
case "logbook":
|
||||
imported = import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook");
|
||||
break;
|
||||
|
||||
case "mailbox":
|
||||
imported = import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox");
|
||||
break;
|
||||
|
||||
case "map":
|
||||
imported = import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map");
|
||||
break;
|
||||
|
||||
case "profile":
|
||||
imported = import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile");
|
||||
break;
|
||||
|
||||
case "shopping-list":
|
||||
imported = import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list");
|
||||
break;
|
||||
|
||||
case "calendar":
|
||||
imported = import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar");
|
||||
break;
|
||||
|
||||
default:
|
||||
imported = null;
|
||||
}
|
||||
|
||||
if (imported != null) {
|
||||
LOADED[panel] = imported;
|
||||
}
|
||||
|
||||
return imported;
|
||||
}
|
||||
|
||||
class PartialPanelResolver extends LitElement {
|
||||
@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;
|
||||
@property() public showMenu?: boolean;
|
||||
@property() public route?: Route | null;
|
||||
|
||||
@property() private _routeTail?: Route | null;
|
||||
@property() private _panelEl?: PanelElement;
|
||||
@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;
|
||||
}
|
||||
protected _updatePageEl(el) {
|
||||
const hass = this.hass!;
|
||||
|
||||
if ("setProperties" in el) {
|
||||
// As long as we have Polymer panels
|
||||
(el as any).setProperties({
|
||||
(el as PolymerElement).setProperties({
|
||||
hass: this.hass,
|
||||
narrow: this.narrow,
|
||||
showMenu: this.showMenu,
|
||||
route: this._routeTail,
|
||||
panel: this._panel,
|
||||
route: this.routeTail,
|
||||
panel: hass.panels[hass.panelUrl],
|
||||
});
|
||||
} else {
|
||||
el.hass = this.hass;
|
||||
el.hass = hass;
|
||||
el.narrow = this.narrow;
|
||||
el.showMenu = this.showMenu;
|
||||
el.route = this._routeTail;
|
||||
el.panel = this._panel;
|
||||
el.route = this.routeTail;
|
||||
el.panel = hass.panels[hass.panelUrl];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 { fetchAreaRegistry } from "../../../data/area_registry";
|
||||
|
||||
class HaConfigEntries extends NavigateMixin(PolymerElement) {
|
||||
class HaConfigIntegrations extends NavigateMixin(PolymerElement) {
|
||||
static get template() {
|
||||
return html`
|
||||
<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-paper-icon-button-arrow-prev + [main-title],
|
||||
app-toolbar paper-icon-button + [main-title] {
|
||||
margin-left: 24px;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user