Convert HUI-ROOT to Lit Element (#2264)

* Convert HUI-ROOT to Lit Element

* Update src/panels/lovelace/hui-root.ts

Co-Authored-By: balloob <paulus@home-assistant.io>

* Update src/panels/lovelace/hui-root.ts

Co-Authored-By: balloob <paulus@home-assistant.io>

* Update src/panels/lovelace/hui-root.ts

Co-Authored-By: balloob <paulus@home-assistant.io>

* Update src/panels/lovelace/hui-root.ts

Co-Authored-By: balloob <paulus@home-assistant.io>

* Update src/panels/lovelace/hui-root.ts

Co-Authored-By: balloob <paulus@home-assistant.io>

* Update src/panels/lovelace/hui-root.ts

Co-Authored-By: balloob <paulus@home-assistant.io>

* Update src/panels/lovelace/hui-root.ts

Co-Authored-By: balloob <paulus@home-assistant.io>

* Update src/panels/lovelace/hui-root.ts

Co-Authored-By: balloob <paulus@home-assistant.io>

* Update src/panels/lovelace/hui-root.ts

Co-Authored-By: balloob <paulus@home-assistant.io>

* Update src/panels/lovelace/hui-root.ts

Co-Authored-By: balloob <paulus@home-assistant.io>

* Apply suggestions from code review

Co-Authored-By: balloob <paulus@home-assistant.io>

* Address comments
This commit is contained in:
Paulus Schoutsen 2018-12-11 19:39:40 +01:00 committed by GitHub
commit ccc6262026
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 594 additions and 454 deletions

View File

@ -3,6 +3,8 @@ import { HomeAssistant } from "../types";
export interface LovelaceConfig {
title?: string;
views: LovelaceViewConfig[];
background?: string;
resources?: Array<{ type: "css" | "js" | "module" | "html"; url: string }>;
}
export interface LovelaceViewConfig {
@ -13,6 +15,8 @@ export interface LovelaceViewConfig {
path?: string;
icon?: string;
theme?: string;
panel?: boolean;
background?: string;
}
export interface LovelaceCardConfig {

View File

@ -131,4 +131,10 @@ class LovelaceFullConfigEditor extends hassLocalizeLitMixin(LitElement) {
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-editor": LovelaceFullConfigEditor;
}
}
customElements.define("hui-editor", LovelaceFullConfigEditor);

View File

@ -1,454 +0,0 @@
import "@polymer/app-layout/app-header-layout/app-header-layout";
import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-scroll-effects/effects/waterfall";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import "@polymer/app-route/app-route";
import "@polymer/paper-icon-button/paper-icon-button";
import "@polymer/paper-button/paper-button";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import "@polymer/paper-menu-button/paper-menu-button";
import "@polymer/paper-tabs/paper-tab";
import "@polymer/paper-tabs/paper-tabs";
import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element";
import scrollToTarget from "../../common/dom/scroll-to-target";
import EventsMixin from "../../mixins/events-mixin";
import localizeMixin from "../../mixins/localize-mixin";
import NavigateMixin from "../../mixins/navigate-mixin";
import "../../layouts/ha-app-layout";
import "../../components/ha-start-voice-button";
import "../../components/ha-icon";
import { loadModule, loadCSS, loadJS } from "../../common/dom/load_resource";
import { subscribeNotifications } from "../../data/ws-notifications";
import { computeNotifications } from "./common/compute-notifications";
import "./components/notifications/hui-notification-drawer";
import "./components/notifications/hui-notifications-button";
import "./hui-unused-entities";
import "./hui-view";
import debounce from "../../common/util/debounce";
import createCardElement from "./common/create-card-element";
import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog";
// CSS and JS should only be imported once. Modules and HTML are safe.
const CSS_CACHE = {};
const JS_CACHE = {};
class HUIRoot extends NavigateMixin(
EventsMixin(localizeMixin(PolymerElement))
) {
static get template() {
return html`
<style include='ha-style'>
:host {
-ms-user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
}
ha-app-layout {
min-height: 100%;
}
paper-tabs {
margin-left: 12px;
--paper-tabs-selection-bar-color: var(--text-primary-color, #FFF);
text-transform: uppercase;
}
paper-tab.iron-selected .edit-view-icon{
display: inline-flex;
}
.edit-view-icon {
padding-left: 8px;
display: none;
}
#add-view {
position: absolute;
height: 44px;
}
#add-view ha-icon {
background-color: var(--accent-color);
border-radius: 5px;
margin-top: 4px;
}
app-toolbar a {
color: var(--text-primary-color, white);
}
paper-button.warning:not([disabled]) {
color: var(--google-red-500);
}
#view {
min-height: calc(100vh - 112px);
/**
* Since we only set min-height, if child nodes need percentage
* heights they must use absolute positioning so we need relative
* positioning here.
*
* https://www.w3.org/TR/CSS2/visudet.html#the-height-property
*/
position: relative;
}
#view.tabs-hidden {
min-height: calc(100vh - 64px);
}
paper-item {
cursor: pointer;
}
</style>
<app-route route="[[route]]" pattern="/:view" data="{{routeData}}"></app-route>
<hui-notification-drawer
hass="[[hass]]"
notifications="[[_notifications]]"
open="{{notificationsOpen}}"
narrow="[[narrow]]"
></hui-notification-drawer>
<ha-app-layout id="layout">
<app-header slot="header" effects="waterfall" fixed condenses>
<template is='dom-if' if="[[!_editMode]]">
<app-toolbar>
<ha-menu-button narrow='[[narrow]]' show-menu='[[showMenu]]'></ha-menu-button>
<div main-title>[[_computeTitle(config)]]</div>
<hui-notifications-button
hass="[[hass]]"
notifications-open="{{notificationsOpen}}"
notifications="[[_notifications]]"
></hui-notifications-button>
<ha-start-voice-button hass="[[hass]]"></ha-start-voice-button>
<paper-menu-button
no-animations
horizontal-align="right"
horizontal-offset="-5"
>
<paper-icon-button icon="hass:dots-vertical" slot="dropdown-trigger"></paper-icon-button>
<paper-listbox on-iron-select="_deselect" slot="dropdown-content">
<template is='dom-if' if="[[_yamlMode]]">
<paper-item on-click="_handleRefresh">Refresh</paper-item>
</template>
<paper-item on-click="_handleUnusedEntities">Unused entities</paper-item>
<paper-item on-click="_editModeEnable">[[localize("ui.panel.lovelace.editor.configure_ui")]]</paper-item>
<template is='dom-if' if="[[_storageMode]]">
<paper-item on-click="_handleFullEditor">Raw config editor</paper-item>
</template>
<paper-item on-click="_handleHelp">Help</paper-item>
</paper-listbox>
</paper-menu-button>
</app-toolbar>
</template>
<template is='dom-if' if="[[_editMode]]">
<app-toolbar>
<paper-icon-button
icon='hass:close'
on-click='_editModeDisable'
></paper-icon-button>
<div main-title>[[localize("ui.panel.lovelace.editor.header")]]</div>
</app-toolbar>
</template>
<div sticky hidden$="[[_computeTabsHidden(config.views, _editMode)]]">
<paper-tabs scrollable selected="[[_curView]]" on-iron-activate="_handleViewSelected">
<template is="dom-repeat" items="[[config.views]]">
<paper-tab>
<template is="dom-if" if="[[item.icon]]">
<ha-icon title$="[[item.title]]" icon="[[item.icon]]"></ha-icon>
</template>
<template is="dom-if" if="[[!item.icon]]">
[[_computeTabTitle(item.title)]]
</template>
<template is='dom-if' if="[[_editMode]]">
<ha-icon class="edit-view-icon" on-click="_editView" icon="hass:pencil"></ha-icon>
</template>
</paper-tab>
</template>
<template is='dom-if' if="[[_editMode]]">
<paper-button id="add-view" on-click="_addView">
<ha-icon title=[[localize("ui.panel.lovelace.editor.edit_view.add")]] icon="hass:plus"></ha-icon>
</paper-button>
</template>
</paper-tabs>
</div>
</app-header>
<div id='view' on-rebuild-view='_debouncedConfigChanged'></div>
</app-header-layout>
`;
}
static get properties() {
return {
narrow: Boolean,
showMenu: Boolean,
hass: { type: Object, observer: "_hassChanged" },
config: {
type: Object,
computed: "_computeConfig(lovelace)",
observer: "_configChanged",
},
lovelace: { type: Object },
columns: { type: Number, observer: "_columnsChanged" },
_curView: { type: Number, value: 0 },
route: { type: Object, observer: "_routeChanged" },
notificationsOpen: { type: Boolean, value: false },
_persistentNotifications: { type: Array, value: [] },
_notifications: {
type: Array,
computed: "_updateNotifications(hass.states, _persistentNotifications)",
},
_yamlMode: {
type: Boolean,
computed: "_computeYamlMode(lovelace)",
},
_storageMode: {
type: Boolean,
computed: "_computeStorageMode(lovelace)",
},
_editMode: {
type: Boolean,
value: false,
computed: "_computeEditMode(lovelace)",
observer: "_editModeChanged",
},
routeData: Object,
};
}
constructor() {
super();
this._debouncedConfigChanged = debounce(
() => this._selectView(this._curView),
100
);
}
connectedCallback() {
super.connectedCallback();
this._unsubNotifications = subscribeNotifications(
this.hass.connection,
(notifications) => {
this._persistentNotifications = notifications;
}
);
}
disconnectedCallback() {
super.disconnectedCallback();
if (typeof this._unsubNotifications === "function") {
this._unsubNotifications();
}
}
_updateNotifications(states, persistent) {
if (!states) return persistent;
const configurator = computeNotifications(states);
return persistent.concat(configurator);
}
_routeChanged(route) {
const views = this.config && this.config.views;
if (route.path === "" && route.prefix === "/lovelace" && views) {
this.navigate(`/lovelace/${views[0].path || 0}`, true);
} else if (this.routeData.view) {
const view = this.routeData.view;
let index = 0;
for (let i = 0; i < views.length; i++) {
if (views[i].path === view || i === parseInt(view)) {
index = i;
break;
}
}
if (index !== this._curView) this._selectView(index);
}
}
_computeViewPath(path, index) {
return path || index;
}
_computeTitle(config) {
return config.title || "Home Assistant";
}
_computeTabsHidden(views, editMode) {
return views.length < 2 && !editMode;
}
_computeTabTitle(title) {
return title || "Unnamed view";
}
_handleRefresh() {
this.fire("config-refresh");
}
_handleUnusedEntities() {
this._selectView("unused");
}
_deselect(ev) {
ev.target.selected = null;
}
_handleHelp() {
window.open("https://www.home-assistant.io/lovelace/", "_blank");
}
_handleFullEditor() {
this.lovelace.enableFullEditMode();
}
_editModeEnable() {
if (this._yamlMode) {
window.alert("The edit UI is not available when in YAML mode.");
return;
}
this.lovelace.setEditMode(true);
if (this.config.views.length < 2) {
this.$.view.classList.remove("tabs-hidden");
this.fire("iron-resize");
}
}
_editModeDisable() {
this.lovelace.setEditMode(false);
if (this.config.views.length < 2) {
this.$.view.classList.add("tabs-hidden");
this.fire("iron-resize");
}
}
_editModeChanged() {
this._selectView(this._curView);
}
_editView() {
showEditViewDialog(this, {
lovelace: this.lovelace,
viewIndex: this._curView,
});
}
_addView() {
showEditViewDialog(this, {
lovelace: this.lovelace,
});
}
_handleViewSelected(ev) {
const index = ev.detail.selected;
this._navigateView(index);
}
_navigateView(viewIndex) {
if (viewIndex !== this._curView) {
const path = this.config.views[viewIndex].path || viewIndex;
this.navigate(`/lovelace/${path}`);
}
scrollToTarget(this, this.$.layout.header.scrollTarget);
}
_selectView(viewIndex) {
this._curView = viewIndex;
// Recreate a new element to clear the applied themes.
const root = this.$.view;
if (root.lastChild) {
root.removeChild(root.lastChild);
}
let view;
let background = this.config.background || "";
if (viewIndex === "unused") {
view = document.createElement("hui-unused-entities");
view.setConfig(this.config);
} else {
const viewConfig = this.config.views[this._curView];
if (!viewConfig) {
this._editModeEnable();
return;
}
if (viewConfig.panel) {
view = createCardElement(viewConfig.cards[0]);
view.isPanel = true;
} else {
view = document.createElement("hui-view");
view.lovelace = this.lovelace;
view.config = viewConfig;
view.columns = this.columns;
view.index = viewIndex;
}
if (viewConfig.background) background = viewConfig.background;
}
this.$.view.style.background = background;
view.hass = this.hass;
root.appendChild(view);
}
_hassChanged(hass) {
if (!this.$.view.lastChild) return;
this.$.view.lastChild.hass = hass;
}
_configChanged(config) {
this._loadResources(config.resources || []);
// On config change, recreate the view from scratch.
this._selectView(this._curView);
this.$.view.classList.toggle("tabs-hidden", config.views.length < 2);
}
_columnsChanged(columns) {
if (!this.$.view.lastChild) return;
this.$.view.lastChild.columns = columns;
}
_loadResources(resources) {
resources.forEach((resource) => {
switch (resource.type) {
case "css":
if (resource.url in CSS_CACHE) break;
CSS_CACHE[resource.url] = loadCSS(resource.url);
break;
case "js":
if (resource.url in JS_CACHE) break;
JS_CACHE[resource.url] = loadJS(resource.url);
break;
case "module":
loadModule(resource.url);
break;
case "html":
import(/* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href").then(
({ importHref }) => importHref(resource.url)
);
break;
default:
// eslint-disable-next-line
console.warn("Unknown resource type specified: ${resource.type}");
}
});
}
_computeConfig(lovelace) {
return lovelace ? lovelace.config : null;
}
_computeYamlMode(lovelace) {
return lovelace ? lovelace.mode === "yaml" : false;
}
_computeStorageMode(lovelace) {
return lovelace ? lovelace.mode === "storage" : false;
}
_computeEditMode(lovelace) {
return lovelace ? lovelace.editMode : false;
}
}
customElements.define("hui-root", HUIRoot);

View File

@ -0,0 +1,584 @@
import {
html,
LitElement,
PropertyDeclarations,
PropertyValues,
} from "@polymer/lit-element";
import { TemplateResult } from "lit-html";
import { classMap } from "lit-html/directives/classMap";
import "@polymer/app-layout/app-header-layout/app-header-layout";
import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-scroll-effects/effects/waterfall";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import "@polymer/app-route/app-route";
import "@polymer/paper-icon-button/paper-icon-button";
import "@polymer/paper-button/paper-button";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import "@polymer/paper-menu-button/paper-menu-button";
import "@polymer/paper-tabs/paper-tab";
import "@polymer/paper-tabs/paper-tabs";
import { HassEntities } from "home-assistant-js-websocket";
import scrollToTarget from "../../common/dom/scroll-to-target";
import "../../layouts/ha-app-layout";
import "../../components/ha-start-voice-button";
import "../../components/ha-icon";
import { loadModule, loadCSS, loadJS } from "../../common/dom/load_resource";
import { subscribeNotifications } from "../../data/ws-notifications";
import debounce from "../../common/util/debounce";
import { hassLocalizeLitMixin } from "../../mixins/lit-localize-mixin";
import { HomeAssistant } from "../../types";
import { LovelaceConfig } from "../../data/lovelace";
import { navigate } from "../../common/navigate";
import { fireEvent } from "../../common/dom/fire_event";
import { computeNotifications } from "./common/compute-notifications";
import "./components/notifications/hui-notification-drawer";
import "./components/notifications/hui-notifications-button";
import "./hui-unused-entities";
import "./hui-view";
import createCardElement from "./common/create-card-element";
import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog";
import { Lovelace } from "./types";
// CSS and JS should only be imported once. Modules and HTML are safe.
const CSS_CACHE = {};
const JS_CACHE = {};
class HUIRoot extends hassLocalizeLitMixin(LitElement) {
public narrow?: boolean;
public showMenu?: boolean;
public hass?: HomeAssistant;
public lovelace?: Lovelace;
public columns?: number;
public route?: { path: string; prefix: string };
private _routeData?: { view: string };
private _curView: number | "unused";
private notificationsOpen?: boolean;
private _persistentNotifications?: Notification[];
private _haStyle?: DocumentFragment;
private _debouncedConfigChanged: () => void;
private _unsubNotifications?: () => void;
static get properties(): PropertyDeclarations {
return {
narrow: {},
showMenu: {},
hass: {},
lovelace: {},
columns: {},
route: {},
_routeData: {},
_curView: {},
notificationsOpen: {},
_persistentNotifications: {},
};
}
constructor() {
super();
this._curView = 0;
this._debouncedConfigChanged = debounce(
() => this._selectView(this._curView),
100
);
}
public connectedCallback(): void {
super.connectedCallback();
this._unsubNotifications = subscribeNotifications(
this.hass!.connection,
(notifications) => {
this._persistentNotifications = notifications;
}
);
}
public disconnectedCallback(): void {
super.disconnectedCallback();
if (this._unsubNotifications) {
this._unsubNotifications();
}
}
protected render(): TemplateResult {
return html`
${this.renderStyle()}
<app-route .route="${this.route}" pattern="/:view" data="${
this._routeData
}" @data-changed="${this._routeDataChanged}"></app-route>
<hui-notification-drawer
.hass="${this.hass}"
.notifications="${this._notifications}"
.open="${this.notificationsOpen}"
@open-changed="${this._handleNotificationsOpenChanged}"
.narrow="${this.narrow}"
></hui-notification-drawer>
<ha-app-layout id="layout">
<app-header slot="header" effects="waterfall" fixed condenses>
${
this._editMode
? html`
<app-toolbar>
<paper-icon-button
icon="hass:close"
@click="${this._editModeDisable}"
></paper-icon-button>
<div main-title>
${this.localize("ui.panel.lovelace.editor.header")}
</div>
</app-toolbar>
`
: html`
<app-toolbar>
<ha-menu-button
.narrow="${this.narrow}"
.showMenu="${this.showMenu}"
></ha-menu-button>
<div main-title>${this.config.title || "Home Assistant"}</div>
<hui-notifications-button
.hass="${this.hass}"
.notificationsOpen="{{notificationsOpen}}"
.notifications="${this._notifications}"
></hui-notifications-button>
<ha-start-voice-button
.hass="${this.hass}"
></ha-start-voice-button>
<paper-menu-button
no-animations
horizontal-align="right"
horizontal-offset="-5"
>
<paper-icon-button
icon="hass:dots-vertical"
slot="dropdown-trigger"
></paper-icon-button>
<paper-listbox
@iron-select="${this._deselect}"
slot="dropdown-content"
>
${
this._yamlMode
? html`
<paper-item @click="${this._handleRefresh}"
>Refresh</paper-item
>
`
: ""
}
<paper-item @click="${this._handleUnusedEntities}"
>Unused entities</paper-item
>
<paper-item @click="${this._editModeEnable}"
>${
this.localize("ui.panel.lovelace.editor.configure_ui")
}</paper-item
>
${
this._storageMode
? html`
<paper-item
@click="${this.lovelace!.enableFullEditMode}"
>Raw config editor</paper-item
>
`
: ""
}
<paper-item @click="${this._handleHelp}">Help</paper-item>
</paper-listbox>
</paper-menu-button>
</app-toolbar>
`
}
${
this.lovelace!.config.views.length > 1 || this._editMode
? html`
<div sticky>
<paper-tabs
scrollable
.selected="${this._curView}"
@iron-activate="${this._handleViewSelected}"
>
${
this.lovelace!.config.views.map(
(view) => html`
<paper-tab>
${
view.icon
? html`
<ha-icon
title="${view.title}"
.icon="${view.icon}"
></ha-icon>
`
: view.title || "Unnamed view"
}
${
this._editMode
? html`
<ha-icon
class="edit-view-icon"
@click="${this._editView}"
icon="hass:pencil"
></ha-icon>
`
: ""
}
</paper-tab>
`
)
}
${
this._editMode
? html`
<paper-button
id="add-view"
@click="${this._addView}"
>
<ha-icon
title="${
this.localize(
"ui.panel.lovelace.editor.edit_view.add"
)
}"
icon="hass:plus"
></ha-icon>
</paper-button>
`
: ""
}
</paper-tabs>
</div>
`
: ""
}
</app-header>
<div id='view' class="${classMap({
"tabs-hidden": this.lovelace!.config.views.length < 2,
})}" @rebuild-view='${this._debouncedConfigChanged}'></div>
</app-header-layout>
`;
}
protected renderStyle(): TemplateResult {
if (!this._haStyle) {
this._haStyle = document.importNode(
(document.getElementById("ha-style")!
.children[0] as HTMLTemplateElement).content,
true
);
}
return html`
${this._haStyle}
<style include="ha-style">
:host {
-ms-user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
}
ha-app-layout {
min-height: 100%;
}
paper-tabs {
margin-left: 12px;
--paper-tabs-selection-bar-color: var(--text-primary-color, #fff);
text-transform: uppercase;
}
paper-tab.iron-selected .edit-view-icon {
display: inline-flex;
}
.edit-view-icon {
padding-left: 8px;
display: none;
}
#add-view {
position: absolute;
height: 44px;
}
#add-view ha-icon {
background-color: var(--accent-color);
border-radius: 5px;
margin-top: 4px;
}
app-toolbar a {
color: var(--text-primary-color, white);
}
paper-button.warning:not([disabled]) {
color: var(--google-red-500);
}
#view {
min-height: calc(100vh - 112px);
/**
* Since we only set min-height, if child nodes need percentage
* heights they must use absolute positioning so we need relative
* positioning here.
*
* https://www.w3.org/TR/CSS2/visudet.html#the-height-property
*/
position: relative;
}
#view.tabs-hidden {
min-height: calc(100vh - 64px);
}
paper-item {
cursor: pointer;
}
</style>
`;
}
protected updated(changedProperties: PropertyValues): void {
super.updated(changedProperties);
const view = this._view;
const huiView = view.lastChild as any;
if (changedProperties.has("columns") && huiView) {
(this._view.lastChild as any).columns = this.columns;
}
if (changedProperties.has("hass") && huiView) {
huiView.hass = this.hass;
}
if (changedProperties.has("route")) {
const views = this.config && this.config.views;
if (
this.route!.path === "" &&
this.route!.prefix === "/lovelace" &&
views
) {
navigate(this, `/lovelace/${views[0].path || 0}`, true);
} else if (this._routeData!.view) {
const selectedView = this._routeData!.view;
const selectedViewInt = parseInt(selectedView, 10);
let index = 0;
for (let i = 0; i < views.length; i++) {
if (views[i].path === selectedView || i === selectedViewInt) {
index = i;
break;
}
}
if (index !== this._curView) {
this._selectView(index);
}
}
}
if (changedProperties.has("lovelace")) {
const oldLovelace = changedProperties.get("lovelace") as
| Lovelace
| undefined;
if (!oldLovelace || oldLovelace.config !== this.lovelace!.config) {
this._loadResources(this.lovelace!.config.resources || []);
// On config change, recreate the view from scratch.
this._selectView(this._curView);
}
if (!oldLovelace || oldLovelace.editMode !== this.lovelace!.editMode) {
this._editModeChanged();
}
}
}
private get _notifications() {
return this._updateNotifications(
this.hass!.states,
this._persistentNotifications! || []
);
}
private get config(): LovelaceConfig {
return this.lovelace!.config;
}
private get _yamlMode(): boolean {
return this.lovelace!.mode === "yaml";
}
private get _storageMode(): boolean {
return this.lovelace!.mode === "storage";
}
private get _editMode() {
return this.lovelace!.editMode;
}
private get _layout(): any {
return this.shadowRoot!.getElementById("layout");
}
private get _view(): HTMLDivElement {
return this.shadowRoot!.getElementById("view") as HTMLDivElement;
}
private _routeDataChanged(ev): void {
this._routeData = ev.detail.value;
}
private _handleNotificationsOpenChanged(ev): void {
this.notificationsOpen = ev.detail.value;
}
private _updateNotifications(
states: HassEntities,
persistent: Array<unknown>
): Array<unknown> {
const configurator = computeNotifications(states);
return persistent.concat(configurator);
}
private _handleRefresh(): void {
fireEvent(this, "config-refresh");
}
private _handleUnusedEntities(): void {
this._selectView("unused");
}
private _deselect(ev): void {
ev.target.selected = null;
}
private _handleHelp(): void {
window.open("https://www.home-assistant.io/lovelace/", "_blank");
}
private _editModeEnable(): void {
if (this._yamlMode) {
window.alert("The edit UI is not available when in YAML mode.");
return;
}
this.lovelace!.setEditMode(true);
if (this.config.views.length < 2) {
fireEvent(this, "iron-resize");
}
}
private _editModeDisable(): void {
this.lovelace!.setEditMode(false);
if (this.config.views.length < 2) {
fireEvent(this, "iron-resize");
}
}
private _editModeChanged(): void {
this._selectView(this._curView);
}
private _editView() {
showEditViewDialog(this, {
lovelace: this.lovelace!,
viewIndex: this._curView as number,
});
}
private _addView() {
showEditViewDialog(this, {
lovelace: this.lovelace!,
});
}
private _handleViewSelected(ev) {
const index = ev.detail.selected;
this._navigateView(index);
}
private _navigateView(viewIndex: number): void {
if (viewIndex !== this._curView) {
const path = this.config.views[viewIndex].path || viewIndex;
navigate(this, `/lovelace/${path}`);
}
scrollToTarget(this, this._layout.header.scrollTarget);
}
private _selectView(viewIndex: HUIRoot["_curView"]): void {
this._curView = viewIndex;
// Recreate a new element to clear the applied themes.
const root = this._view;
if (root.lastChild) {
root.removeChild(root.lastChild);
}
let view;
let background = this.config.background || "";
if (viewIndex === "unused") {
view = document.createElement("hui-unused-entities");
view.setConfig(this.config);
} else {
const viewConfig = this.config.views[this._curView];
if (!viewConfig) {
this._editModeEnable();
return;
}
if (viewConfig.panel && viewConfig.cards && viewConfig.cards.length > 0) {
view = createCardElement(viewConfig.cards[0]);
view.isPanel = true;
} else {
view = document.createElement("hui-view");
view.lovelace = this.lovelace;
view.config = viewConfig;
view.columns = this.columns;
view.index = viewIndex;
}
if (viewConfig.background) {
background = viewConfig.background;
}
}
this._view.style.background = background;
view.hass = this.hass;
root.appendChild(view);
}
private _loadResources(resources) {
resources.forEach((resource) => {
switch (resource.type) {
case "css":
if (resource.url in CSS_CACHE) {
break;
}
CSS_CACHE[resource.url] = loadCSS(resource.url);
break;
case "js":
if (resource.url in JS_CACHE) {
break;
}
JS_CACHE[resource.url] = loadJS(resource.url);
break;
case "module":
loadModule(resource.url);
break;
case "html":
import(/* webpackChunkName: "import-href-polyfill" */ "../../resources/html-import/import-href").then(
({ importHref }) => importHref(resource.url)
);
break;
default:
// tslint:disable-next-line
console.warn(`Unknown resource type specified: ${resource.type}`);
}
});
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-root": HUIRoot;
}
}
customElements.define("hui-root", HUIRoot);