Change config navigation to tabs (#4630)

* Change config navigation to tabs

* Update ha-menu-button.ts

* Icons

* update

* Review comments

* configSections -> object instead of array
This commit is contained in:
Bram Kragten 2020-01-28 21:48:21 +01:00 committed by GitHub
parent 6e624b394b
commit 1c9eab7ca0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1322 additions and 948 deletions

View File

@ -8,7 +8,7 @@ import { DEFAULT_DOMAIN_ICON } from "../const";
const fixedIcons = { const fixedIcons = {
alert: "hass:alert", alert: "hass:alert",
alexa: "hass:amazon-alexa", alexa: "hass:amazon-alexa",
automation: "hass:playlist-play", automation: "hass:robot",
calendar: "hass:calendar", calendar: "hass:calendar",
camera: "hass:video", camera: "hass:video",
climate: "hass:thermostat", climate: "hass:thermostat",
@ -36,8 +36,8 @@ const fixedIcons = {
plant: "hass:flower", plant: "hass:flower",
proximity: "hass:apple-safari", proximity: "hass:apple-safari",
remote: "hass:remote", remote: "hass:remote",
scene: "hass:google-pages", scene: "hass:palette",
script: "hass:file-document", script: "hass:script-text",
sensor: "hass:eye", sensor: "hass:eye",
simple_alarm: "hass:bell", simple_alarm: "hass:bell",
sun: "hass:white-balance-sunny", sun: "hass:white-balance-sunny",
@ -48,7 +48,7 @@ const fixedIcons = {
water_heater: "hass:thermometer", water_heater: "hass:thermometer",
weather: "hass:weather-cloudy", weather: "hass:weather-cloudy",
weblink: "hass:open-in-new", weblink: "hass:open-in-new",
zone: "hass:map-marker", zone: "hass:map-marker-radius",
}; };
export const domainIcon = (domain: string, state?: string): string => { export const domainIcon = (domain: string, state?: string): string => {

View File

@ -135,7 +135,7 @@ class HaMenuButton extends LitElement {
top: 5px; top: 5px;
right: 2px; right: 2px;
border-radius: 50%; border-radius: 50%;
border: 2px solid var(--primary-color); border: 2px solid var(--app-header-background-color);
} }
`; `;
} }

View File

@ -50,6 +50,11 @@ class HassLoadingScreen extends LitElement {
return [ return [
haStyle, haStyle,
css` css`
:host {
display: block;
height: 100%;
background-color: var(--primary-background-color);
}
.content { .content {
height: calc(100% - 64px); height: calc(100% - 64px);
display: flex; display: flex;

View File

@ -59,6 +59,7 @@ class HassSubpage extends LitElement {
background-color: var(--app-header-background-color); background-color: var(--app-header-background-color);
font-weight: 400; font-weight: 400;
color: var(--app-header-text-color, white); color: var(--app-header-text-color, white);
border-bottom: var(--app-header-border-bottom, none);
} }
ha-menu-button, ha-menu-button,

View File

@ -0,0 +1,239 @@
import {
LitElement,
property,
TemplateResult,
html,
customElement,
css,
CSSResult,
PropertyValues,
} from "lit-element";
import "../components/ha-menu-button";
import "../components/ha-paper-icon-button-arrow-prev";
import { classMap } from "lit-html/directives/class-map";
import { Route, HomeAssistant } from "../types";
import { navigate } from "../common/navigate";
import "@material/mwc-ripple";
import { isComponentLoaded } from "../common/config/is_component_loaded";
export interface PageNavigation {
path: string;
translationKey?: string;
component?: string;
name?: string;
core?: boolean;
exportOnly?: boolean;
icon?: string;
info?: any;
}
@customElement("hass-tabs-subpage")
class HassTabsSubpage extends LitElement {
@property() public hass!: HomeAssistant;
@property({ type: String, attribute: "back-path" }) public backPath?: string;
@property() public backCallback?: () => void;
@property({ type: Boolean }) public hassio = false;
@property({ type: Boolean }) public showAdvanced = false;
@property() public route!: Route;
@property() public tabs!: PageNavigation[];
@property({ type: Boolean, reflect: true }) public narrow = false;
@property() private _activeTab: number = -1;
protected updated(changedProperties: PropertyValues) {
super.updated(changedProperties);
if (changedProperties.has("route")) {
this._activeTab = this.tabs.findIndex((tab) =>
this.route.prefix.includes(tab.path)
);
}
}
protected render(): TemplateResult {
return html`
<div class="toolbar">
<ha-paper-icon-button-arrow-prev
aria-label="Back"
.hassio=${this.hassio}
@click=${this._backTapped}
></ha-paper-icon-button-arrow-prev>
<div id="tabbar" class=${classMap({ "bottom-bar": this.narrow })}>
${this.tabs.map((page, index) =>
(!page.component ||
page.core ||
isComponentLoaded(this.hass, page.component)) &&
(!page.exportOnly || this.showAdvanced)
? html`
<div
class="tab ${classMap({
active: index === this._activeTab,
})}"
@click=${this._tabTapped}
.path=${page.path}
>
${this.narrow
? html`
<ha-icon .icon=${page.icon}></ha-icon>
`
: ""}
${!this.narrow || index === this._activeTab
? html`
<span class="name"
>${page.translationKey
? this.hass.localize(page.translationKey)
: name}</span
>
`
: ""}
<mwc-ripple></mwc-ripple>
</div>
`
: ""
)}
</div>
<div id="toolbar-icon">
<slot name="toolbar-icon"></slot>
</div>
</div>
<div class="content">
<slot></slot>
</div>
`;
}
private _tabTapped(ev: MouseEvent): void {
navigate(this, (ev.currentTarget as any).path, true);
}
private _backTapped(): void {
if (this.backPath) {
navigate(this, this.backPath);
return;
}
if (this.backCallback) {
this.backCallback();
return;
}
history.back();
}
static get styles(): CSSResult {
return css`
:host {
display: block;
height: 100%;
background-color: var(--primary-background-color);
}
.toolbar {
display: flex;
align-items: center;
font-size: 20px;
height: 64px;
background-color: var(--sidebar-background-color);
font-weight: 400;
color: var(--sidebar-text-color);
border-bottom: 1px solid var(--divider-color);
padding: 0 16px;
box-sizing: border-box;
}
:host([narrow]) .toolbar {
background-color: var(--primary-background-color);
border-bottom: none;
}
#tabbar {
display: flex;
font-size: 14px;
}
#tabbar.bottom-bar {
position: absolute;
bottom: 0;
left: 0;
padding: 0 16px;
box-sizing: border-box;
background-color: var(--sidebar-background-color);
border-top: 1px solid var(--divider-color);
justify-content: space-between;
z-index: 1;
font-size: 12px;
width: 100%;
}
#tabbar:not(.bottom-bar) {
margin: auto;
left: 50%;
position: absolute;
transform: translate(-50%, 0);
}
.tab {
padding: 0 32px;
display: flex;
flex-direction: column;
text-align: center;
align-items: center;
justify-content: center;
height: 64px;
cursor: pointer;
}
.name {
white-space: nowrap;
}
.tab.active {
color: var(--primary-color);
}
#tabbar:not(.bottom-bar) .tab.active {
border-bottom: 2px solid var(--primary-color);
}
.bottom-bar .tab {
padding: 0 16px;
width: 20%;
min-width: 0;
}
ha-menu-button,
ha-paper-icon-button-arrow-prev,
::slotted([slot="toolbar-icon"]) {
pointer-events: auto;
color: var(--sidebar-icon-color);
}
[main-title] {
margin: 0 0 0 24px;
line-height: 20px;
flex-grow: 1;
}
.content {
position: relative;
width: 100%;
height: calc(100% - 64px);
overflow-y: auto;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
#toolbar-icon {
position: absolute;
right: 16px;
}
:host([narrow]) .content {
height: calc(100% - 128px);
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"hass-tabs-subpage": HassTabsSubpage;
}
}

View File

@ -10,7 +10,7 @@ import {
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
import { HomeAssistant } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import { import {
AreaRegistryEntry, AreaRegistryEntry,
updateAreaRegistryEntry, updateAreaRegistryEntry,
@ -20,7 +20,7 @@ import {
} from "../../../data/area_registry"; } from "../../../data/area_registry";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-fab"; import "../../../components/ha-fab";
import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage";
import "../../../layouts/hass-loading-screen"; import "../../../layouts/hass-loading-screen";
import "../ha-config-section"; import "../ha-config-section";
import { import {
@ -30,11 +30,14 @@ import {
import { classMap } from "lit-html/directives/class-map"; import { classMap } from "lit-html/directives/class-map";
import { computeRTL } from "../../../common/util/compute_rtl"; import { computeRTL } from "../../../common/util/compute_rtl";
import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { configSections } from "../ha-panel-config";
@customElement("ha-config-areas") @customElement("ha-config-areas")
export class HaConfigAreas extends LitElement { export class HaConfigAreas extends LitElement {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public isWide?: boolean; @property() public isWide?: boolean;
@property() public narrow!: boolean;
@property() public route!: Route;
@property() private _areas?: AreaRegistryEntry[]; @property() private _areas?: AreaRegistryEntry[];
private _unsubAreas?: UnsubscribeFunc; private _unsubAreas?: UnsubscribeFunc;
@ -52,9 +55,12 @@ export class HaConfigAreas extends LitElement {
`; `;
} }
return html` return html`
<hass-subpage <hass-tabs-subpage
.header="${this.hass.localize("ui.panel.config.areas.caption")}" .hass=${this.hass}
.showBackButton=${!this.isWide} .narrow=${this.narrow}
back-path="/config"
.route=${this.route}
.tabs=${configSections.persons}
> >
<ha-config-section .isWide=${this.isWide}> <ha-config-section .isWide=${this.isWide}>
<span slot="header"> <span slot="header">
@ -95,10 +101,11 @@ export class HaConfigAreas extends LitElement {
: html``} : html``}
</ha-card> </ha-card>
</ha-config-section> </ha-config-section>
</hass-subpage> </hass-tabs-subpage>
<ha-fab <ha-fab
?is-wide=${this.isWide} ?is-wide=${this.isWide}
?narrow=${this.narrow}
icon="hass:plus" icon="hass:plus"
title="${this.hass.localize("ui.panel.config.areas.create_area")}" title="${this.hass.localize("ui.panel.config.areas.create_area")}"
@click=${this._createArea} @click=${this._createArea}
@ -162,6 +169,10 @@ All devices in this area will become unassigned.`)
static get styles(): CSSResult { static get styles(): CSSResult {
return css` return css`
hass-loading-screen {
--app-header-background-color: var(--sidebar-background-color);
--app-header-text-color: var(--sidebar-text-color);
}
a { a {
color: var(--primary-color); color: var(--primary-color);
} }
@ -189,7 +200,9 @@ All devices in this area will become unassigned.`)
bottom: 24px; bottom: 24px;
right: 24px; right: 24px;
} }
ha-fab[narrow] {
bottom: 84px;
}
ha-fab.rtl { ha-fab.rtl {
right: auto; right: auto;
left: 16px; left: 16px;

View File

@ -11,7 +11,6 @@ import {
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { classMap } from "lit-html/directives/class-map"; import { classMap } from "lit-html/directives/class-map";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate"; import { navigate } from "../../../common/navigate";
import { computeRTL } from "../../../common/util/compute_rtl"; import { computeRTL } from "../../../common/util/compute_rtl";
import "../../../components/ha-fab"; import "../../../components/ha-fab";
@ -31,15 +30,19 @@ import {
} from "../../../dialogs/generic/show-dialog-box"; } from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/ha-app-layout"; import "../../../layouts/ha-app-layout";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import "./action/ha-automation-action"; import "./action/ha-automation-action";
import "./condition/ha-automation-condition"; import "./condition/ha-automation-condition";
import "./trigger/ha-automation-trigger"; import "./trigger/ha-automation-trigger";
import "../../../layouts/hass-tabs-subpage";
import { configSections } from "../ha-panel-config";
export class HaAutomationEditor extends LitElement { export class HaAutomationEditor extends LitElement {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public automation!: AutomationEntity; @property() public automation!: AutomationEntity;
@property() public isWide?: boolean; @property() public isWide?: boolean;
@property() public narrow!: boolean;
@property() public route!: Route;
@property() public creatingNew?: boolean; @property() public creatingNew?: boolean;
@property() private _config?: AutomationConfig; @property() private _config?: AutomationConfig;
@property() private _dirty?: boolean; @property() private _dirty?: boolean;
@ -47,169 +50,159 @@ export class HaAutomationEditor extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<ha-app-layout has-scrolling-region> <hass-tabs-subpage
<app-header slot="header" fixed> .hass=${this.hass}
<app-toolbar> .narrow=${this.narrow}
<ha-paper-icon-button-arrow-prev .route=${this.route}
@click=${this._backTapped} .backCallback=${() => this._backTapped()}
></ha-paper-icon-button-arrow-prev> .tabs=${configSections.automation}
<div main-title> >
${this.automation ${this.creatingNew
? computeStateName(this.automation) ? ""
: this.hass.localize( : html`
"ui.panel.config.automation.editor.default_name" <paper-icon-button
)} slot="toolbar-icon"
</div> title="${this.hass.localize(
${this.creatingNew "ui.panel.config.automation.picker.delete_automation"
? "" )}"
: html` icon="hass:delete"
<paper-icon-button @click=${this._deleteConfirm}
title="${this.hass.localize( ></paper-icon-button>
"ui.panel.config.automation.picker.delete_automation" `}
)}" ${this._errors
icon="hass:delete" ? html`
@click=${this._deleteConfirm} <div class="errors">${this._errors}</div>
></paper-icon-button> `
`} : ""}
</app-toolbar> <div
</app-header> class="${classMap({
rtl: computeRTL(this.hass),
<div class="content"> })}"
${this._errors >
${this._config
? html` ? html`
<div class="errors">${this._errors}</div> <ha-config-section .isWide=${this.isWide}>
<span slot="header">${this._config.alias}</span>
<span slot="introduction">
${this.hass.localize(
"ui.panel.config.automation.editor.introduction"
)}
</span>
<ha-card>
<div class="card-content">
<paper-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.alias"
)}
name="alias"
.value=${this._config.alias}
@value-changed=${this._valueChanged}
>
</paper-input>
<ha-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.description.label"
)}
.placeholder=${this.hass.localize(
"ui.panel.config.automation.editor.description.placeholder"
)}
name="description"
.value=${this._config.description}
@value-changed=${this._valueChanged}
></ha-textarea>
</div>
</ha-card>
</ha-config-section>
<ha-config-section .isWide=${this.isWide}>
<span slot="header">
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.header"
)}
</span>
<span slot="introduction">
<p>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.introduction"
)}
</p>
<a
href="https://home-assistant.io/docs/automation/trigger/"
target="_blank"
>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.learn_more"
)}
</a>
</span>
<ha-automation-trigger
.triggers=${this._config.trigger}
@value-changed=${this._triggerChanged}
.hass=${this.hass}
></ha-automation-trigger>
</ha-config-section>
<ha-config-section .isWide=${this.isWide}>
<span slot="header">
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.header"
)}
</span>
<span slot="introduction">
<p>
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.introduction"
)}
</p>
<a
href="https://home-assistant.io/docs/scripts/conditions/"
target="_blank"
>
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.learn_more"
)}
</a>
</span>
<ha-automation-condition
.conditions=${this._config.condition || []}
@value-changed=${this._conditionChanged}
.hass=${this.hass}
></ha-automation-condition>
</ha-config-section>
<ha-config-section .isWide=${this.isWide}>
<span slot="header">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.header"
)}
</span>
<span slot="introduction">
<p>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.introduction"
)}
</p>
<a
href="https://home-assistant.io/docs/automation/action/"
target="_blank"
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.learn_more"
)}
</a>
</span>
<ha-automation-action
.actions=${this._config.action}
@value-changed=${this._actionChanged}
.hass=${this.hass}
></ha-automation-action>
</ha-config-section>
` `
: ""} : ""}
<div
class="${classMap({
rtl: computeRTL(this.hass),
})}"
>
${this._config
? html`
<ha-config-section .isWide=${this.isWide}>
<span slot="header">${this._config.alias}</span>
<span slot="introduction">
${this.hass.localize(
"ui.panel.config.automation.editor.introduction"
)}
</span>
<ha-card>
<div class="card-content">
<paper-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.alias"
)}
name="alias"
.value=${this._config.alias}
@value-changed=${this._valueChanged}
>
</paper-input>
<ha-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.description.label"
)}
.placeholder=${this.hass.localize(
"ui.panel.config.automation.editor.description.placeholder"
)}
name="description"
.value=${this._config.description}
@value-changed=${this._valueChanged}
></ha-textarea>
</div>
</ha-card>
</ha-config-section>
<ha-config-section .isWide=${this.isWide}>
<span slot="header">
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.header"
)}
</span>
<span slot="introduction">
<p>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.introduction"
)}
</p>
<a
href="https://home-assistant.io/docs/automation/trigger/"
target="_blank"
>
${this.hass.localize(
"ui.panel.config.automation.editor.triggers.learn_more"
)}
</a>
</span>
<ha-automation-trigger
.triggers=${this._config.trigger}
@value-changed=${this._triggerChanged}
.hass=${this.hass}
></ha-automation-trigger>
</ha-config-section>
<ha-config-section .isWide=${this.isWide}>
<span slot="header">
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.header"
)}
</span>
<span slot="introduction">
<p>
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.introduction"
)}
</p>
<a
href="https://home-assistant.io/docs/scripts/conditions/"
target="_blank"
>
${this.hass.localize(
"ui.panel.config.automation.editor.conditions.learn_more"
)}
</a>
</span>
<ha-automation-condition
.conditions=${this._config.condition || []}
@value-changed=${this._conditionChanged}
.hass=${this.hass}
></ha-automation-condition>
</ha-config-section>
<ha-config-section .isWide=${this.isWide}>
<span slot="header">
${this.hass.localize(
"ui.panel.config.automation.editor.actions.header"
)}
</span>
<span slot="introduction">
<p>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.introduction"
)}
</p>
<a
href="https://home-assistant.io/docs/automation/action/"
target="_blank"
>
${this.hass.localize(
"ui.panel.config.automation.editor.actions.learn_more"
)}
</a>
</span>
<ha-automation-action
.actions=${this._config.action}
@value-changed=${this._actionChanged}
.hass=${this.hass}
></ha-automation-action>
</ha-config-section>
`
: ""}
</div>
</div> </div>
<ha-fab <ha-fab
slot="fab"
?is-wide="${this.isWide}" ?is-wide="${this.isWide}"
?narrow="${this.narrow}"
?dirty="${this._dirty}" ?dirty="${this._dirty}"
icon="hass:content-save" icon="hass:content-save"
.title="${this.hass.localize( .title="${this.hass.localize(
@ -220,7 +213,7 @@ export class HaAutomationEditor extends LitElement {
rtl: computeRTL(this.hass), rtl: computeRTL(this.hass),
})}" })}"
></ha-fab> ></ha-fab>
</ha-app-layout> </hass-tabs-subpage>
`; `;
} }
@ -409,7 +402,10 @@ export class HaAutomationEditor extends LitElement {
bottom: 24px; bottom: 24px;
right: 24px; right: 24px;
} }
ha-fab[narrow] {
bottom: 84px;
margin-bottom: -140px;
}
ha-fab[dirty] { ha-fab[dirty] {
margin-bottom: 0; margin-bottom: 0;
} }

View File

@ -11,7 +11,7 @@ import { ifDefined } from "lit-html/directives/if-defined";
import "@polymer/paper-icon-button/paper-icon-button"; import "@polymer/paper-icon-button/paper-icon-button";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
import "@polymer/paper-tooltip/paper-tooltip"; import "@polymer/paper-tooltip/paper-tooltip";
import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-fab"; import "../../../components/ha-fab";
@ -22,7 +22,7 @@ import "../ha-config-section";
import { computeStateName } from "../../../common/entity/compute_state_name"; import { computeStateName } from "../../../common/entity/compute_state_name";
import { computeRTL } from "../../../common/util/compute_rtl"; import { computeRTL } from "../../../common/util/compute_rtl";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import { import {
AutomationEntity, AutomationEntity,
showAutomationEditor, showAutomationEditor,
@ -32,18 +32,24 @@ import format_date_time from "../../../common/datetime/format_date_time";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import { showThingtalkDialog } from "./show-dialog-thingtalk"; import { showThingtalkDialog } from "./show-dialog-thingtalk";
import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { configSections } from "../ha-panel-config";
@customElement("ha-automation-picker") @customElement("ha-automation-picker")
class HaAutomationPicker extends LitElement { class HaAutomationPicker extends LitElement {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public isWide!: boolean; @property() public isWide!: boolean;
@property() public narrow!: boolean;
@property() public route!: Route;
@property() public automations!: AutomationEntity[]; @property() public automations!: AutomationEntity[];
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<hass-subpage <hass-tabs-subpage
.showBackButton=${!this.isWide} .hass=${this.hass}
.header=${this.hass.localize("ui.panel.config.automation.caption")} .narrow=${this.narrow}
back-path="/config"
.route=${this.route}
.tabs=${configSections.automation}
> >
<ha-config-section .isWide=${this.isWide}> <ha-config-section .isWide=${this.isWide}>
<div slot="header"> <div slot="header">
@ -150,6 +156,7 @@ class HaAutomationPicker extends LitElement {
<ha-fab <ha-fab
slot="fab" slot="fab"
?is-wide=${this.isWide} ?is-wide=${this.isWide}
?narrow=${this.narrow}
icon="hass:plus" icon="hass:plus"
title=${this.hass.localize( title=${this.hass.localize(
"ui.panel.config.automation.picker.add_automation" "ui.panel.config.automation.picker.add_automation"
@ -158,7 +165,7 @@ class HaAutomationPicker extends LitElement {
@click=${this._createNew} @click=${this._createNew}
></ha-fab> ></ha-fab>
</div> </div>
</hass-subpage> </hass-tabs-subpage>
`; `;
} }
@ -221,7 +228,9 @@ class HaAutomationPicker extends LitElement {
bottom: 24px; bottom: 24px;
right: 24px; right: 24px;
} }
ha-fab[narrow] {
bottom: 84px;
}
ha-fab[rtl] { ha-fab[rtl] {
right: auto; right: auto;
left: 16px; left: 16px;

View File

@ -33,6 +33,8 @@ class HaConfigAutomation extends PolymerElement {
hass="[[hass]]" hass="[[hass]]"
automations="[[automations]]" automations="[[automations]]"
is-wide="[[isWide]]" is-wide="[[isWide]]"
narrow="[[narrow]]"
route="[[route]]"
></ha-automation-picker> ></ha-automation-picker>
</template> </template>
@ -41,6 +43,8 @@ class HaConfigAutomation extends PolymerElement {
hass="[[hass]]" hass="[[hass]]"
automation="[[automation]]" automation="[[automation]]"
is-wide="[[isWide]]" is-wide="[[isWide]]"
narrow="[[narrow]]"
route="[[route]]"
creating-new="[[_creatingNew]]" creating-new="[[_creatingNew]]"
></ha-automation-editor> ></ha-automation-editor>
</template> </template>
@ -52,6 +56,7 @@ class HaConfigAutomation extends PolymerElement {
hass: Object, hass: Object,
route: Object, route: Object,
isWide: Boolean, isWide: Boolean,
narrow: Boolean,
_routeData: Object, _routeData: Object,
_routeMatches: Boolean, _routeMatches: Boolean,
_creatingNew: Boolean, _creatingNew: Boolean,

View File

@ -63,10 +63,7 @@ class CloudAccount extends EventsMixin(LocalizeMixin(PolymerElement)) {
color: var(--primary-color); color: var(--primary-color);
} }
</style> </style>
<hass-subpage <hass-subpage header="[[localize('ui.panel.config.cloud.caption')]]">
show-back-button="[[!isWide]]"
header="[[localize('ui.panel.config.cloud.caption')]]"
>
<div class="content"> <div class="content">
<ha-config-section is-wide="[[isWide]]"> <ha-config-section is-wide="[[isWide]]">
<span slot="header" <span slot="header"

View File

@ -346,7 +346,7 @@ class CloudAlexa extends LitElement {
} }
ha-card { ha-card {
margin: 4px; margin: 4px;
flex-basis: 300px; width: 300px;
flex-grow: 1; flex-grow: 1;
} }
.card-content { .card-content {

View File

@ -369,7 +369,7 @@ class CloudGoogleAssistant extends LitElement {
} }
ha-card { ha-card {
margin: 4px; margin: 4px;
flex-basis: 300px; width: 300px;
flex-grow: 1; flex-grow: 1;
} }
.card-content { .card-content {

View File

@ -72,10 +72,7 @@ class CloudLogin extends LocalizeMixin(
color: var(--secondary-text-color); color: var(--secondary-text-color);
} }
</style> </style>
<hass-subpage <hass-subpage header="[[localize('ui.panel.config.cloud.caption')]]">
show-back-button="[[!isWide]]"
header="[[localize('ui.panel.config.cloud.caption')]]"
>
<div class="content"> <div class="content">
<ha-config-section is-wide="[[isWide]]"> <ha-config-section is-wide="[[isWide]]">
<span slot="header" <span slot="header"

View File

@ -4,11 +4,13 @@ import "@polymer/paper-icon-button/paper-icon-button";
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage";
import "../../../resources/ha-style"; import "../../../resources/ha-style";
import "./ha-config-section-core"; import "./ha-config-section-core";
import { configSections } from "../ha-panel-config";
import LocalizeMixin from "../../../mixins/localize-mixin"; import LocalizeMixin from "../../../mixins/localize-mixin";
/* /*
@ -33,9 +35,13 @@ class HaConfigCore extends LocalizeMixin(PolymerElement) {
} }
</style> </style>
<hass-subpage <hass-tabs-subpage
header="[[localize('ui.panel.config.core.caption')]]" hass="[[hass]]"
show-back-button="[[!isWide]]" narrow="[[narrow]]"
route="[[route]]"
back-path="/config"
tabs="[[_computeTabs()]]"
show-advanced="[[showAdvanced]]"
> >
<div class$="[[computeClasses(isWide)]]"> <div class$="[[computeClasses(isWide)]]">
<ha-config-section-core <ha-config-section-core
@ -44,7 +50,7 @@ class HaConfigCore extends LocalizeMixin(PolymerElement) {
hass="[[hass]]" hass="[[hass]]"
></ha-config-section-core> ></ha-config-section-core>
</div> </div>
</hass-subpage> </hass-tabs-subpage>
`; `;
} }
@ -52,10 +58,16 @@ class HaConfigCore extends LocalizeMixin(PolymerElement) {
return { return {
hass: Object, hass: Object,
isWide: Boolean, isWide: Boolean,
narrow: Boolean,
showAdvanced: Boolean, showAdvanced: Boolean,
route: Object,
}; };
} }
_computeTabs() {
return configSections.general;
}
computeClasses(isWide) { computeClasses(isWide) {
return isWide ? "content" : "content narrow"; return isWide ? "content" : "content narrow";
} }

View File

@ -1,10 +1,8 @@
import "@polymer/app-layout/app-header-layout/app-header-layout";
import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import "@polymer/paper-icon-button/paper-icon-button"; import "@polymer/paper-icon-button/paper-icon-button";
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../layouts/hass-tabs-subpage";
import "../../../resources/ha-style"; import "../../../resources/ha-style";
import "../../../components/ha-paper-icon-button-arrow-prev"; import "../../../components/ha-paper-icon-button-arrow-prev";
@ -17,6 +15,8 @@ import { computeStateDomain } from "../../../common/entity/compute_state_domain"
import { sortStatesByName } from "../../../common/entity/states_sort_by_name"; import { sortStatesByName } from "../../../common/entity/states_sort_by_name";
import LocalizeMixin from "../../../mixins/localize-mixin"; import LocalizeMixin from "../../../mixins/localize-mixin";
import { configSections } from "../ha-panel-config";
/* /*
* @appliesMixin LocalizeMixin * @appliesMixin LocalizeMixin
*/ */
@ -29,19 +29,14 @@ class HaConfigCustomize extends LocalizeMixin(PolymerElement) {
} }
</style> </style>
<app-header-layout has-scrolling-region=""> <hass-tabs-subpage
<app-header slot="header" fixed=""> hass="[[hass]]"
<app-toolbar> narrow="[[narrow]]"
<ha-paper-icon-button-arrow-prev route="[[route]]"
hide$="[[isWide]]" back-path="/config"
on-click="_backTapped" tabs="[[_computeTabs()]]"
></ha-paper-icon-button-arrow-prev> show-advanced="[[showAdvanced]]"
<div main-title=""> >
[[localize('ui.panel.config.customize.caption')]]
</div>
</app-toolbar>
</app-header>
<div class$="[[computeClasses(isWide)]]"> <div class$="[[computeClasses(isWide)]]">
<ha-config-section is-wide="[[isWide]]"> <ha-config-section is-wide="[[isWide]]">
<span slot="header"> <span slot="header">
@ -59,7 +54,7 @@ class HaConfigCustomize extends LocalizeMixin(PolymerElement) {
</ha-entity-config> </ha-entity-config>
</ha-config-section> </ha-config-section>
</div> </div>
</app-header-layout> </hass-tabs-subpage>
`; `;
} }
@ -67,7 +62,9 @@ class HaConfigCustomize extends LocalizeMixin(PolymerElement) {
return { return {
hass: Object, hass: Object,
isWide: Boolean, isWide: Boolean,
narrow: Boolean,
route: Object,
showAdvanced: Boolean,
entities: { entities: {
type: Array, type: Array,
computed: "computeEntities(hass)", computed: "computeEntities(hass)",
@ -95,6 +92,10 @@ class HaConfigCustomize extends LocalizeMixin(PolymerElement) {
history.back(); history.back();
} }
_computeTabs() {
return configSections.general;
}
computeEntities(hass) { computeEntities(hass) {
return Object.keys(hass.states) return Object.keys(hass.states)
.map((key) => hass.states[key]) .map((key) => hass.states[key])

View File

@ -15,7 +15,7 @@ import "../../../components/ha-menu-button";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { CloudStatus, CloudStatusLoggedIn } from "../../../data/cloud"; import { CloudStatus } from "../../../data/cloud";
import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import "../../../components/ha-card"; import "../../../components/ha-card";
@ -23,6 +23,7 @@ import "../../../components/ha-icon-next";
import "../ha-config-section"; import "../ha-config-section";
import "./ha-config-navigation"; import "./ha-config-navigation";
import { configSections } from "../ha-panel-config";
@customElement("ha-config-dashboard") @customElement("ha-config-dashboard")
class HaConfigDashboard extends LitElement { class HaConfigDashboard extends LitElement {
@ -41,7 +42,6 @@ class HaConfigDashboard extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.narrow=${this.narrow} .narrow=${this.narrow}
></ha-menu-button> ></ha-menu-button>
<div main-title>${this.hass.localize("panel.config")}</div>
</app-toolbar> </app-toolbar>
</app-header> </app-header>
@ -57,68 +57,33 @@ class HaConfigDashboard extends LitElement {
${this.cloudStatus && isComponentLoaded(this.hass, "cloud") ${this.cloudStatus && isComponentLoaded(this.hass, "cloud")
? html` ? html`
<ha-card> <ha-card>
<a href="/config/cloud" tabindex="-1"> <ha-config-navigation
<paper-item> .hass=${this.hass}
<paper-item-body two-line=""> .showAdvanced=${this.showAdvanced}
${this.hass.localize("ui.panel.config.cloud.caption")} .pages=${[
${this.cloudStatus.logged_in {
? html` component: "cloud",
<div secondary=""> path: "/config/cloud",
${this.hass.localize( translationKey: "ui.panel.config.cloud.caption",
"ui.panel.config.cloud.description_login", info: this.cloudStatus,
"email", icon: "hass:cloud-lock",
(this.cloudStatus as CloudStatusLoggedIn) },
.email ]}
)} ></ha-config-navigation>
</div>
`
: html`
<div secondary="">
${this.hass.localize(
"ui.panel.config.cloud.description_features"
)}
</div>
`}
</paper-item-body>
<ha-icon-next></ha-icon-next>
</paper-item>
</a>
</ha-card> </ha-card>
` `
: ""} : ""}
${Object.values(configSections).map(
<ha-card> (section) => html`
<ha-config-navigation <ha-card>
.hass=${this.hass} <ha-config-navigation
.showAdvanced=${this.showAdvanced} .hass=${this.hass}
.pages=${[ .showAdvanced=${this.showAdvanced}
{ page: "integrations", core: true }, .pages=${section}
{ page: "devices", core: true }, ></ha-config-navigation>
{ page: "entities", core: true }, </ha-card>
{ page: "automation" }, `
{ page: "script" }, )}
{ page: "scene" },
]}
></ha-config-navigation>
</ha-card>
<ha-card>
<ha-config-navigation
.hass=${this.hass}
.showAdvanced=${this.showAdvanced}
.pages=${[
{ page: "core", core: true },
{ page: "server_control", core: true },
{ page: "areas", core: true },
{ page: "zone" },
{ page: "person" },
{ page: "users", core: true },
{ page: "zha" },
{ page: "zwave" },
{ page: "customize", core: true, advanced: true },
]}
></ha-config-navigation>
</ha-card>
${!this.showAdvanced ${!this.showAdvanced
? html` ? html`
<div class="promo-advanced"> <div class="promo-advanced">
@ -142,9 +107,15 @@ class HaConfigDashboard extends LitElement {
return [ return [
haStyle, haStyle,
css` css`
app-header {
--app-header-background-color: var(--primary-background-color);
}
ha-config-navigation:last-child { ha-config-navigation:last-child {
margin-bottom: 24px; margin-bottom: 24px;
} }
ha-config-section {
margin-top: -20px;
}
ha-card { ha-card {
overflow: hidden; overflow: hidden;
} }

View File

@ -1,6 +1,6 @@
import "@polymer/iron-icon/iron-icon"; import "@polymer/iron-icon/iron-icon";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-icon-item";
import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { isComponentLoaded } from "../../../common/config/is_component_loaded";
@ -17,70 +17,65 @@ import {
} from "lit-element"; } from "lit-element";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { CloudStatus, CloudStatusLoggedIn } from "../../../data/cloud"; import { CloudStatus, CloudStatusLoggedIn } from "../../../data/cloud";
import { PageNavigation } from "../../../layouts/hass-tabs-subpage";
export interface ConfigPageNavigation {
page: string;
core?: boolean;
advanced?: boolean;
info?: any;
}
@customElement("ha-config-navigation") @customElement("ha-config-navigation")
class HaConfigNavigation extends LitElement { class HaConfigNavigation extends LitElement {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public showAdvanced!: boolean; @property() public showAdvanced!: boolean;
@property() public pages!: ConfigPageNavigation[]; @property() public pages!: PageNavigation[];
@property() public curPage!: string;
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<paper-listbox attr-for-selected="data-page" .selected=${this.curPage}> ${this.pages.map((page) =>
${this.pages.map(({ page, core, advanced, info }) => (!page.component ||
(core || isComponentLoaded(this.hass, page)) && page.core ||
(!advanced || this.showAdvanced) isComponentLoaded(this.hass, page.component)) &&
? html` (!page.exportOnly || this.showAdvanced)
<a ? html`
href=${`/config/${page}`} <a
aria-role="option" href=${`/config/${page.component}`}
data-page="${page}" aria-role="option"
tabindex="-1" tabindex="-1"
> >
<paper-item> <paper-icon-item>
<paper-item-body two-line> <ha-icon .icon=${page.icon} slot="item-icon"></ha-icon>
${this.hass.localize(`ui.panel.config.${page}.caption`)} <paper-item-body two-line>
${page === "cloud" && (info as CloudStatus) ${this.hass.localize(
? info.logged_in `ui.panel.config.${page.component}.caption`
? html` )}
<div secondary> ${page.component === "cloud" && (page.info as CloudStatus)
${this.hass.localize( ? page.info.logged_in
"ui.panel.config.cloud.description_login", ? html`
"email", <div secondary>
(info as CloudStatusLoggedIn).email ${this.hass.localize(
)} "ui.panel.config.cloud.description_login",
</div> "email",
` (page.info as CloudStatusLoggedIn).email
: html` )}
<div secondary> </div>
${this.hass.localize( `
"ui.panel.config.cloud.description_features"
)}
</div>
`
: html` : html`
<div secondary> <div secondary>
${this.hass.localize( ${this.hass.localize(
`ui.panel.config.${page}.description` "ui.panel.config.cloud.description_features"
)} )}
</div> </div>
`} `
</paper-item-body> : html`
<ha-icon-next></ha-icon-next> <div secondary>
</paper-item> ${this.hass.localize(
</a> `ui.panel.config.${page.component}.description`
` )}
: "" </div>
)} `}
</paper-listbox> </paper-item-body>
<ha-icon-next></ha-icon-next>
</paper-icon-item>
</a>
`
: ""
)}
`; `;
} }
@ -93,6 +88,10 @@ class HaConfigNavigation extends LitElement {
display: block; display: block;
outline: 0; outline: 0;
} }
ha-icon,
ha-icon-next {
color: var(--secondary-text-color);
}
.iron-selected paper-item::before, .iron-selected paper-item::before,
a:not(.iron-selected):focus::before { a:not(.iron-selected):focus::before {
position: absolute; position: absolute;
@ -105,10 +104,6 @@ class HaConfigNavigation extends LitElement {
transition: opacity 15ms linear; transition: opacity 15ms linear;
will-change: opacity; will-change: opacity;
} }
.iron-selected paper-item::before {
background-color: var(--sidebar-selected-icon-color);
opacity: 0.12;
}
a:not(.iron-selected):focus::before { a:not(.iron-selected):focus::before {
background-color: currentColor; background-color: currentColor;
opacity: var(--dark-divider-opacity); opacity: var(--dark-divider-opacity);
@ -117,12 +112,6 @@ class HaConfigNavigation extends LitElement {
.iron-selected:focus paper-item::before { .iron-selected:focus paper-item::before {
opacity: 0.2; opacity: 0.2;
} }
.iron-selected paper-item[pressed]::before {
opacity: 0.37;
}
paper-listbox {
padding: 0;
}
`; `;
} }
} }

View File

@ -9,7 +9,7 @@ import {
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage";
import "../../../layouts/hass-error-screen"; import "../../../layouts/hass-error-screen";
import "../ha-config-section"; import "../ha-config-section";
@ -18,7 +18,7 @@ import "./device-detail/ha-device-triggers-card";
import "./device-detail/ha-device-conditions-card"; import "./device-detail/ha-device-conditions-card";
import "./device-detail/ha-device-actions-card"; import "./device-detail/ha-device-actions-card";
import "./device-detail/ha-device-entities-card"; import "./device-detail/ha-device-entities-card";
import { HomeAssistant } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import { ConfigEntry } from "../../../data/config_entries"; import { ConfigEntry } from "../../../data/config_entries";
import { import {
EntityRegistryEntry, EntityRegistryEntry,
@ -45,6 +45,7 @@ import {
import { compare } from "../../../common/string/compare"; import { compare } from "../../../common/string/compare";
import { computeStateName } from "../../../common/entity/compute_state_name"; import { computeStateName } from "../../../common/entity/compute_state_name";
import { createValidEntityId } from "../../../common/entity/valid_entity_id"; import { createValidEntityId } from "../../../common/entity/valid_entity_id";
import { configSections } from "../ha-panel-config";
export interface EntityRegistryStateEntry extends EntityRegistryEntry { export interface EntityRegistryStateEntry extends EntityRegistryEntry {
stateName?: string; stateName?: string;
@ -60,6 +61,7 @@ export class HaConfigDevicePage extends LitElement {
@property() public deviceId!: string; @property() public deviceId!: string;
@property() public narrow!: boolean; @property() public narrow!: boolean;
@property() public showAdvanced!: boolean; @property() public showAdvanced!: boolean;
@property() public route!: Route;
@property() private _triggers: DeviceTrigger[] = []; @property() private _triggers: DeviceTrigger[] = [];
@property() private _conditions: DeviceCondition[] = []; @property() private _conditions: DeviceCondition[] = [];
@property() private _actions: DeviceAction[] = []; @property() private _actions: DeviceAction[] = [];
@ -133,7 +135,12 @@ export class HaConfigDevicePage extends LitElement {
const entities = this._entities(this.deviceId, this.entities); const entities = this._entities(this.deviceId, this.entities);
return html` return html`
<hass-subpage .header=${device.name_by_user || device.name}> <hass-tabs-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.tabs=${configSections.integrations}
.route=${this.route}
>
<paper-icon-button <paper-icon-button
slot="toolbar-icon" slot="toolbar-icon"
icon="hass:settings" icon="hass:settings"
@ -201,7 +208,7 @@ export class HaConfigDevicePage extends LitElement {
` `
: html``} : html``}
</ha-config-section> </ha-config-section>
</hass-subpage> </hass-tabs-subpage>
`; `;
} }

View File

@ -1,4 +1,4 @@
import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage";
import "./ha-devices-data-table"; import "./ha-devices-data-table";
import { import {
@ -10,11 +10,12 @@ import {
CSSResult, CSSResult,
css, css,
} from "lit-element"; } from "lit-element";
import { HomeAssistant } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import { DeviceRegistryEntry } from "../../../data/device_registry"; import { DeviceRegistryEntry } from "../../../data/device_registry";
import { EntityRegistryEntry } from "../../../data/entity_registry"; import { EntityRegistryEntry } from "../../../data/entity_registry";
import { ConfigEntry } from "../../../data/config_entries"; import { ConfigEntry } from "../../../data/config_entries";
import { AreaRegistryEntry } from "../../../data/area_registry"; import { AreaRegistryEntry } from "../../../data/area_registry";
import { configSections } from "../ha-panel-config";
@customElement("ha-config-devices-dashboard") @customElement("ha-config-devices-dashboard")
export class HaConfigDeviceDashboard extends LitElement { export class HaConfigDeviceDashboard extends LitElement {
@ -26,12 +27,16 @@ export class HaConfigDeviceDashboard extends LitElement {
@property() public entities!: EntityRegistryEntry[]; @property() public entities!: EntityRegistryEntry[];
@property() public areas!: AreaRegistryEntry[]; @property() public areas!: AreaRegistryEntry[];
@property() public domain!: string; @property() public domain!: string;
@property() public route!: Route;
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<hass-subpage <hass-tabs-subpage
.showBackButton=${!this.isWide} .hass=${this.hass}
.header=${this.hass.localize("ui.panel.config.devices.caption")} .narrow=${this.narrow}
back-path="/config"
.tabs=${configSections.integrations}
.route=${this.route}
> >
<div class="content"> <div class="content">
<ha-devices-data-table <ha-devices-data-table
@ -44,7 +49,7 @@ export class HaConfigDeviceDashboard extends LitElement {
.domain=${this.domain} .domain=${this.domain}
></ha-devices-data-table> ></ha-devices-data-table>
</div> </div>
</hass-subpage> </hass-tabs-subpage>
`; `;
} }

View File

@ -100,6 +100,7 @@ class HaConfigDevices extends HassRouterPage {
pageEl.narrow = this.narrow; pageEl.narrow = this.narrow;
pageEl.isWide = this.isWide; pageEl.isWide = this.isWide;
pageEl.showAdvanced = this.showAdvanced; pageEl.showAdvanced = this.showAdvanced;
pageEl.route = this.routeTail;
} }
private _loadData() { private _loadData() {

View File

@ -1,32 +1,29 @@
import "@polymer/paper-input/paper-input";
import { HassEntity } from "home-assistant-js-websocket";
import { import {
LitElement,
html,
css, css,
CSSResult, CSSResult,
TemplateResult,
property,
customElement, customElement,
html,
LitElement,
property,
PropertyValues, PropertyValues,
TemplateResult,
} from "lit-element"; } from "lit-element";
import "@polymer/paper-input/paper-input"; import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-switch";
import { PolymerChangedEvent } from "../../../polymer-types";
import { HomeAssistant } from "../../../types";
import { HassEntity } from "home-assistant-js-websocket";
// tslint:disable-next-line: no-duplicate-imports
import { HaSwitch } from "../../../components/ha-switch";
import { computeDomain } from "../../../common/entity/compute_domain"; import { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateName } from "../../../common/entity/compute_state_name"; import { computeStateName } from "../../../common/entity/compute_state_name";
import "../../../components/ha-switch";
// tslint:disable-next-line: no-duplicate-imports
import { HaSwitch } from "../../../components/ha-switch";
import { import {
updateEntityRegistryEntry,
removeEntityRegistryEntry,
EntityRegistryEntry, EntityRegistryEntry,
removeEntityRegistryEntry,
updateEntityRegistryEntry,
} from "../../../data/entity_registry"; } from "../../../data/entity_registry";
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
import { fireEvent } from "../../../common/dom/fire_event"; import { PolymerChangedEvent } from "../../../polymer-types";
import { HomeAssistant } from "../../../types";
@customElement("entity-registry-settings") @customElement("entity-registry-settings")
export class EntityRegistrySettings extends LitElement { export class EntityRegistrySettings extends LitElement {

View File

@ -1,59 +1,59 @@
import {
LitElement,
TemplateResult,
html,
css,
CSSResult,
property,
query,
customElement,
} from "lit-element";
import { styleMap } from "lit-html/directives/style-map";
import "@polymer/paper-checkbox/paper-checkbox"; import "@polymer/paper-checkbox/paper-checkbox";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu"; import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import "@polymer/paper-item/paper-icon-item"; import "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-listbox/paper-listbox"; import "@polymer/paper-listbox/paper-listbox";
import "@polymer/paper-tooltip/paper-tooltip"; import "@polymer/paper-tooltip/paper-tooltip";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { HomeAssistant } from "../../../types";
import { import {
EntityRegistryEntry, css,
computeEntityRegistryName, CSSResult,
subscribeEntityRegistry, customElement,
removeEntityRegistryEntry, html,
updateEntityRegistryEntry, LitElement,
} from "../../../data/entity_registry"; property,
import "../../../layouts/hass-subpage"; query,
import "../../../layouts/hass-loading-screen"; TemplateResult,
import "../../../components/data-table/ha-data-table"; } from "lit-element";
import "../../../components/ha-icon"; import { styleMap } from "lit-html/directives/style-map";
import memoize from "memoize-one";
import { computeDomain } from "../../../common/entity/compute_domain";
import { domainIcon } from "../../../common/entity/domain_icon"; import { domainIcon } from "../../../common/entity/domain_icon";
import { stateIcon } from "../../../common/entity/state_icon"; import { stateIcon } from "../../../common/entity/state_icon";
import { computeDomain } from "../../../common/entity/compute_domain"; import "../../../components/data-table/ha-data-table";
import {
showEntityRegistryDetailDialog,
loadEntityRegistryDetailDialog,
} from "./show-dialog-entity-registry-detail";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import memoize from "memoize-one";
// tslint:disable-next-line // tslint:disable-next-line
import { import {
DataTableColumnContainer, DataTableColumnContainer,
DataTableColumnData,
HaDataTable,
RowClickedEvent, RowClickedEvent,
SelectionChangedEvent, SelectionChangedEvent,
HaDataTable,
DataTableColumnData,
} from "../../../components/data-table/ha-data-table"; } from "../../../components/data-table/ha-data-table";
import "../../../components/ha-icon";
import {
computeEntityRegistryName,
EntityRegistryEntry,
removeEntityRegistryEntry,
subscribeEntityRegistry,
updateEntityRegistryEntry,
} from "../../../data/entity_registry";
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-loading-screen";
import "../../../layouts/hass-tabs-subpage";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { HomeAssistant, Route } from "../../../types";
import { DialogEntityRegistryDetail } from "./dialog-entity-registry-detail"; import { DialogEntityRegistryDetail } from "./dialog-entity-registry-detail";
import {
loadEntityRegistryDetailDialog,
showEntityRegistryDetailDialog,
} from "./show-dialog-entity-registry-detail";
import { configSections } from "../ha-panel-config";
@customElement("ha-config-entities") @customElement("ha-config-entities")
export class HaConfigEntities extends SubscribeMixin(LitElement) { export class HaConfigEntities extends SubscribeMixin(LitElement) {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public isWide!: boolean; @property() public isWide!: boolean;
@property() public narrow!: boolean; @property() public narrow!: boolean;
@property() public route!: Route;
@property() private _entities?: EntityRegistryEntry[]; @property() private _entities?: EntityRegistryEntry[];
@property() private _showDisabled = false; @property() private _showDisabled = false;
@property() private _showUnavailable = true; @property() private _showUnavailable = true;
@ -224,9 +224,12 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
`; `;
} }
return html` return html`
<hass-subpage <hass-tabs-subpage
.header="${this.hass.localize("ui.panel.config.entities.caption")}" .hass=${this.hass}
.showBackButton=${!this.isWide} .narrow=${this.narrow}
back-path="/config"
.route=${this.route}
.tabs=${configSections.integrations}
> >
<div class="content"> <div class="content">
<div class="intro"> <div class="intro">
@ -367,7 +370,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
</div> </div>
</ha-data-table> </ha-data-table>
</div> </div>
</hass-subpage> </hass-tabs-subpage>
`; `;
} }
@ -490,6 +493,10 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
static get styles(): CSSResult { static get styles(): CSSResult {
return css` return css`
hass-loading-screen {
--app-header-background-color: var(--sidebar-background-color);
--app-header-text-color: var(--sidebar-text-color);
}
a { a {
color: var(--primary-color); color: var(--primary-color);
} }

View File

@ -1,177 +0,0 @@
import { property, customElement } from "lit-element";
import "../../layouts/hass-loading-screen";
import { HomeAssistant } from "../../types";
import { CloudStatus } from "../../data/cloud";
import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
import { PolymerElement } from "@polymer/polymer";
declare global {
// for fire event
interface HASSDomEvents {
"ha-refresh-cloud-status": undefined;
}
}
@customElement("ha-config-router")
class HaConfigRouter extends HassRouterPage {
@property() public hass!: HomeAssistant;
@property() public narrow!: boolean;
@property() public wideSidebar: boolean = false;
@property() public wide: boolean = false;
@property() public isWide: boolean = false;
@property() public showAdvanced: boolean = false;
@property() public cloudStatus?: CloudStatus;
protected routerOptions: RouterOptions = {
defaultPage: "dashboard",
cacheAll: true,
preloadAll: true,
routes: {
areas: {
tag: "ha-config-areas",
load: () =>
import(
/* webpackChunkName: "panel-config-areas" */ "./areas/ha-config-areas"
),
},
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"
),
},
devices: {
tag: "ha-config-devices",
load: () =>
import(
/* webpackChunkName: "panel-config-devices" */ "./devices/ha-config-devices"
),
},
server_control: {
tag: "ha-config-server-control",
load: () =>
import(
/* webpackChunkName: "panel-config-server-control" */ "./server_control/ha-config-server-control"
),
},
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"
),
},
entities: {
tag: "ha-config-entities",
load: () =>
import(
/* webpackChunkName: "panel-config-entities" */ "./entities/ha-config-entities"
),
},
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"
),
},
scene: {
tag: "ha-config-scene",
load: () =>
import(
/* webpackChunkName: "panel-config-scene" */ "./scene/ha-config-scene"
),
},
users: {
tag: "ha-config-users",
load: () =>
import(
/* webpackChunkName: "panel-config-users" */ "./users/ha-config-users"
),
},
zone: {
tag: "ha-config-zone",
load: () =>
import(
/* webpackChunkName: "panel-config-zone" */ "./zone/ha-config-zone"
),
},
zha: {
tag: "zha-config-dashboard-router",
load: () =>
import(
/* webpackChunkName: "panel-config-zha" */ "./zha/zha-config-dashboard-router"
),
},
zwave: {
tag: "ha-config-zwave",
load: () =>
import(
/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave"
),
},
},
};
protected updatePageEl(el) {
if ("setProperties" in el) {
// As long as we have Polymer panels
(el as PolymerElement).setProperties({
route: this.routeTail,
hass: this.hass,
showAdvanced: this.showAdvanced,
isWide: this.isWide,
narrow: this.narrow,
cloudStatus: this.cloudStatus,
});
} else {
el.route = this.routeTail;
el.hass = this.hass;
el.showAdvanced = this.showAdvanced;
el.isWide = this.isWide;
el.narrow = this.narrow;
el.cloudStatus = this.cloudStatus;
}
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-config-router": HaConfigRouter;
}
}

View File

@ -1,18 +1,59 @@
import { customElement } from "lit-element"; import { customElement, LitElement, html, css, property } from "lit-element";
import { classMap } from "lit-html/directives/class-map";
@customElement("ha-config-section") @customElement("ha-config-section")
export class HaConfigSection extends HTMLElement { export class HaConfigSection extends LitElement {
constructor() { @property() public isWide: boolean = false;
super();
this.attachShadow({ mode: "open" }); protected render() {
this.shadowRoot!.innerHTML = ` return html`
<style> <div
class="content ${classMap({
narrow: !this.isWide,
})}"
>
<div class="header"><slot name="header"></slot></div>
<div
class="together layout ${classMap({
narrow: !this.isWide,
vertical: !this.isWide,
horizontal: this.isWide,
})}"
>
<div class="intro"><slot name="introduction"></slot></div>
<div class="panel flex-auto"><slot></slot></div>
</div>
</div>
`;
}
static get styles() {
return css`
:host {
display: block;
}
.content { .content {
padding: 28px 20px 0; padding: 28px 20px 0;
max-width: 640px; max-width: 1040px;
margin: 0 auto; margin: 0 auto;
} }
.layout {
display: flex;
}
.horizontal {
flex-direction: row;
}
.vertical {
flex-direction: column;
}
.flex-auto {
flex: 1 1 auto;
}
.header { .header {
font-family: var(--paper-font-headline_-_font-family); font-family: var(--paper-font-headline_-_font-family);
-webkit-font-smoothing: var( -webkit-font-smoothing: var(
@ -26,7 +67,7 @@ export class HaConfigSection extends HTMLElement {
} }
.together { .together {
margin-top: 20px; margin-top: 32px;
} }
.intro { .intro {
@ -37,8 +78,8 @@ export class HaConfigSection extends HTMLElement {
font-weight: var(--paper-font-subhead_-_font-weight); font-weight: var(--paper-font-subhead_-_font-weight);
line-height: var(--paper-font-subhead_-_line-height); line-height: var(--paper-font-subhead_-_line-height);
width: 100%; width: 100%;
width: 100%; max-width: 400px;
max-width: 500px; margin-right: 40px;
opacity: var(--dark-primary-opacity); opacity: var(--dark-primary-opacity);
font-size: 14px; font-size: 14px;
padding-bottom: 20px; padding-bottom: 20px;
@ -52,14 +93,18 @@ export class HaConfigSection extends HTMLElement {
margin-top: 24px; margin-top: 24px;
display: block; display: block;
} }
</style>
<div class="content"> .narrow.content {
<div class="header"><slot name="header"></slot></div> max-width: 640px;
<div class="together"> }
<div class="intro"><slot name="introduction"></slot></div> .narrow .together {
<div class="panel"><slot></slot></div> margin-top: 20px;
</div> }
</div> .narrow .intro {
padding-bottom: 20px;
margin-right: 0;
max-width: 500px;
}
`; `;
} }
} }

View File

@ -1,12 +1,4 @@
import { import { property, PropertyValues, customElement } from "lit-element";
property,
PropertyValues,
customElement,
LitElement,
html,
CSSResult,
css,
} from "lit-element";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "../../layouts/hass-loading-screen"; import "../../layouts/hass-loading-screen";
@ -18,9 +10,9 @@ import {
getOptimisticFrontendUserDataCollection, getOptimisticFrontendUserDataCollection,
CoreFrontendUserData, CoreFrontendUserData,
} from "../../data/frontend"; } from "../../data/frontend";
import "./ha-config-router"; import { HassRouterPage, RouterOptions } from "../../layouts/hass-router-page";
import "./dashboard/ha-config-navigation"; import { PolymerElement } from "@polymer/polymer";
import { classMap } from "lit-html/directives/class-map"; import { PageNavigation } from "../../layouts/hass-tabs-subpage";
declare global { declare global {
// for fire event // for fire event
@ -29,14 +21,252 @@ declare global {
} }
} }
const NO_SIDEBAR_PAGES = ["zone"]; export const configSections: { [name: string]: PageNavigation[] } = {
integrations: [
{
component: "integrations",
path: "/config/integrations",
translationKey: "ui.panel.config.integrations.caption",
icon: "hass:puzzle",
core: true,
},
{
component: "devices",
path: "/config/devices",
translationKey: "ui.panel.config.devices.caption",
icon: "hass:devices",
core: true,
},
{
component: "entities",
path: "/config/entities",
translationKey: "ui.panel.config.entities.caption",
icon: "hass:shape",
core: true,
},
{
component: "areas",
path: "/config/areas",
translationKey: "ui.panel.config.areas.caption",
icon: "hass:sofa",
core: true,
},
],
automation: [
{
component: "automation",
path: "/config/automation",
translationKey: "ui.panel.config.automation.caption",
icon: "hass:robot",
},
{
component: "scene",
path: "/config/scene",
translationKey: "ui.panel.config.scene.caption",
icon: "hass:palette",
},
{
component: "script",
path: "/config/script",
translationKey: "ui.panel.config.script.caption",
icon: "hass:script-text",
},
],
persons: [
{
component: "person",
path: "/config/person",
translationKey: "ui.panel.config.person.caption",
icon: "hass:account",
},
{
component: "zone",
path: "/config/zone",
translationKey: "ui.panel.config.zone.caption",
icon: "hass:map-marker-radius",
core: true,
},
{
component: "users",
path: "/config/users",
translationKey: "ui.panel.config.users.caption",
icon: "hass:account-badge-horizontal",
core: true,
},
],
general: [
{
component: "core",
path: "/config/core",
translationKey: "ui.panel.config.core.caption",
icon: "hass:home-assistant",
core: true,
},
{
component: "server_control",
path: "/config/server_control",
translationKey: "ui.panel.config.server_control.caption",
icon: "hass:server",
core: true,
},
{
component: "customize",
path: "/config/customize",
translationKey: "ui.panel.config.customize.caption",
icon: "hass:pencil",
core: true,
exportOnly: true,
},
],
other: [
{
component: "zha",
path: "/config/zha",
translationKey: "ui.panel.config.zha.caption",
icon: "hass:zigbee",
},
{
component: "zwave",
path: "/config/zwave",
translationKey: "ui.panel.config.zwave.caption",
icon: "hass:z-wave",
},
],
};
@customElement("ha-panel-config") @customElement("ha-panel-config")
class HaPanelConfig extends LitElement { class HaPanelConfig extends HassRouterPage {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public narrow!: boolean; @property() public narrow!: boolean;
@property() public route!: Route; @property() public route!: Route;
protected routerOptions: RouterOptions = {
defaultPage: "dashboard",
cacheAll: true,
preloadAll: true,
routes: {
areas: {
tag: "ha-config-areas",
load: () =>
import(
/* webpackChunkName: "panel-config-areas" */ "./areas/ha-config-areas"
),
},
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"
),
},
devices: {
tag: "ha-config-devices",
load: () =>
import(
/* webpackChunkName: "panel-config-devices" */ "./devices/ha-config-devices"
),
},
server_control: {
tag: "ha-config-server-control",
load: () =>
import(
/* webpackChunkName: "panel-config-server-control" */ "./server_control/ha-config-server-control"
),
},
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"
),
},
entities: {
tag: "ha-config-entities",
load: () =>
import(
/* webpackChunkName: "panel-config-entities" */ "./entities/ha-config-entities"
),
},
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"
),
},
scene: {
tag: "ha-config-scene",
load: () =>
import(
/* webpackChunkName: "panel-config-scene" */ "./scene/ha-config-scene"
),
},
users: {
tag: "ha-config-users",
load: () =>
import(
/* webpackChunkName: "panel-config-users" */ "./users/ha-config-users"
),
},
zone: {
tag: "ha-config-zone",
load: () =>
import(
/* webpackChunkName: "panel-config-zone" */ "./zone/ha-config-zone"
),
},
zha: {
tag: "zha-config-dashboard-router",
load: () =>
import(
/* webpackChunkName: "panel-config-zha" */ "./zha/zha-config-dashboard-router"
),
},
zwave: {
tag: "ha-config-zwave",
load: () =>
import(
/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave"
),
},
},
};
@property() private _wideSidebar: boolean = false; @property() private _wideSidebar: boolean = false;
@property() private _wide: boolean = false; @property() private _wide: boolean = false;
@property() private _coreUserData?: CoreFrontendUserData; @property() private _coreUserData?: CoreFrontendUserData;
@ -85,64 +315,42 @@ class HaPanelConfig extends LitElement {
this.addEventListener("ha-refresh-cloud-status", () => this.addEventListener("ha-refresh-cloud-status", () =>
this._updateCloudStatus() this._updateCloudStatus()
); );
this.style.setProperty(
"--app-header-background-color",
"var(--sidebar-background-color)"
);
this.style.setProperty(
"--app-header-text-color",
"var(--sidebar-text-color)"
);
this.style.setProperty(
"--app-header-border-bottom",
"1px solid var(--divider-color)"
);
} }
protected render() { protected updatePageEl(el) {
const dividerPos = this.route.path.indexOf("/", 1);
const curPage =
dividerPos === -1
? this.route.path.substr(1)
: this.route.path.substr(1, dividerPos - 1);
const isWide = const isWide =
this.hass.dockedSidebar === "docked" ? this._wideSidebar : this._wide; this.hass.dockedSidebar === "docked" ? this._wideSidebar : this._wide;
const showSidebar = isWide && !NO_SIDEBAR_PAGES.includes(curPage); if ("setProperties" in el) {
return html` // As long as we have Polymer panels
${showSidebar (el as PolymerElement).setProperties({
? html` route: this.routeTail,
<div class="side-bar"> hass: this.hass,
<div class="toolbar">Configuration</div> showAdvanced: this._showAdvanced,
<div class="navigation"> isWide,
<ha-config-navigation narrow: this.narrow,
.hass=${this.hass} cloudStatus: this._cloudStatus,
.showAdvanced=${this._showAdvanced} });
.curPage=${curPage} } else {
.pages=${[ el.route = this.routeTail;
{ page: "cloud", info: this._cloudStatus }, el.hass = this.hass;
{ page: "integrations", core: true }, el.showAdvanced = this._showAdvanced;
{ page: "devices", core: true }, el.isWide = isWide;
{ page: "entities", core: true }, el.narrow = this.narrow;
{ page: "automation" }, el.cloudStatus = this._cloudStatus;
{ page: "script" }, }
{ page: "scene" },
{ page: "core", core: true },
{ page: "areas", core: true },
{ page: "zone" },
{ page: "person" },
{ page: "users", core: true },
{ page: "server_control", core: true },
{ page: "zha" },
{ page: "zwave" },
{ page: "customize", core: true, advanced: true },
]}
></ha-config-navigation>
</div>
</div>
`
: ""}
<ha-config-router
.hass=${this.hass}
.route=${this.route}
.narrow=${this.narrow}
.isWide=${isWide}
.wide=${this._wide}
.wideSidebar=${this._wideSidebar}
.showAdvanced=${this._showAdvanced}
.cloudStatus=${this._cloudStatus}
class=${classMap({ "wide-config": showSidebar })}
></ha-config-router>
`;
} }
private async _updateCloudStatus() { private async _updateCloudStatus() {
@ -152,54 +360,6 @@ class HaPanelConfig extends LitElement {
setTimeout(() => this._updateCloudStatus(), 5000); setTimeout(() => this._updateCloudStatus(), 5000);
} }
} }
static get styles(): CSSResult {
return css`
:host {
display: block;
height: 100%;
background-color: var(--primary-background-color);
}
a {
text-decoration: none;
color: var(--primary-text-color);
}
.side-bar {
border-right: 1px solid var(--divider-color);
background: white;
width: 320px;
float: left;
box-sizing: border-box;
position: fixed;
}
.toolbar {
display: flex;
align-items: center;
font-size: 20px;
height: 64px;
padding: 0 16px 0 16px;
pointer-events: none;
background-color: var(--primary-background-color);
font-weight: 400;
color: var(--primary-text-color);
border-bottom: 1px solid var(--divider-color);
}
.wide-config {
float: right;
width: calc(100% - 320px);
height: 100%;
}
.navigation {
height: calc(100vh - 64px);
overflow: auto;
}
`;
}
} }
declare global { declare global {

View File

@ -12,7 +12,7 @@ import "../../../components/ha-card";
import "../../../components/ha-icon-next"; import "../../../components/ha-icon-next";
import "../../../components/ha-fab"; import "../../../components/ha-fab";
import "../../../components/entity/ha-state-icon"; import "../../../components/entity/ha-state-icon";
import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage";
import "../../../resources/ha-style"; import "../../../resources/ha-style";
import "../../../components/ha-icon"; import "../../../components/ha-icon";
@ -38,18 +38,21 @@ import {
css, css,
CSSResult, CSSResult,
} from "lit-element"; } from "lit-element";
import { HomeAssistant } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import { ConfigEntry, deleteConfigEntry } from "../../../data/config_entries"; import { ConfigEntry, deleteConfigEntry } from "../../../data/config_entries";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import { EntityRegistryEntry } from "../../../data/entity_registry"; import { EntityRegistryEntry } from "../../../data/entity_registry";
import { DataEntryFlowProgress } from "../../../data/data_entry_flow"; import { DataEntryFlowProgress } from "../../../data/data_entry_flow";
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
import { configSections } from "../ha-panel-config";
@customElement("ha-config-entries-dashboard") @customElement("ha-config-entries-dashboard")
export class HaConfigManagerDashboard extends LitElement { export class HaConfigManagerDashboard extends LitElement {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public showAdvanced!: boolean; @property() public showAdvanced!: boolean;
@property() public isWide!: boolean; @property() public isWide!: boolean;
@property() public narrow!: boolean;
@property() public route!: Route;
@property() private configEntries!: ConfigEntry[]; @property() private configEntries!: ConfigEntry[];
@ -72,9 +75,12 @@ export class HaConfigManagerDashboard extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<hass-subpage <hass-tabs-subpage
.showBackButton=${!this.isWide} .hass=${this.hass}
.header=${this.hass.localize("ui.panel.config.integrations.caption")} .narrow=${this.narrow}
back-path="/config"
.route=${this.route}
.tabs=${configSections.integrations}
> >
<paper-menu-button <paper-menu-button
close-on-activate close-on-activate
@ -247,8 +253,9 @@ export class HaConfigManagerDashboard extends LitElement {
title=${this.hass.localize("ui.panel.config.integrations.new")} title=${this.hass.localize("ui.panel.config.integrations.new")}
@click=${this._createFlow} @click=${this._createFlow}
?rtl=${computeRTL(this.hass!)} ?rtl=${computeRTL(this.hass!)}
?narrow=${this.narrow}
></ha-fab> ></ha-fab>
</hass-subpage> </hass-tabs-subpage>
`; `;
} }
@ -361,7 +368,9 @@ export class HaConfigManagerDashboard extends LitElement {
right: 16px; right: 16px;
z-index: 1; z-index: 1;
} }
ha-fab[narrow] {
bottom: 84px;
}
ha-fab[rtl] { ha-fab[rtl] {
right: auto; right: auto;
left: 16px; left: 16px;

View File

@ -104,7 +104,7 @@ class HaConfigIntegrations extends HassRouterPage {
pageEl.narrow = this.narrow; pageEl.narrow = this.narrow;
pageEl.isWide = this.isWide; pageEl.isWide = this.isWide;
pageEl.showAdvanced = this.showAdvanced; pageEl.showAdvanced = this.showAdvanced;
pageEl.route = this.routeTail;
if (this._currentPage === "dashboard") { if (this._currentPage === "dashboard") {
pageEl.configEntriesInProgress = this._configEntriesInProgress; pageEl.configEntriesInProgress = this._configEntriesInProgress;
return; return;

View File

@ -9,7 +9,7 @@ import {
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
import { HomeAssistant } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import { import {
Person, Person,
fetchPersons, fetchPersons,
@ -19,7 +19,7 @@ import {
} from "../../../data/person"; } from "../../../data/person";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-fab"; import "../../../components/ha-fab";
import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage";
import "../../../layouts/hass-loading-screen"; import "../../../layouts/hass-loading-screen";
import { compare } from "../../../common/string/compare"; import { compare } from "../../../common/string/compare";
import "../ha-config-section"; import "../ha-config-section";
@ -28,10 +28,13 @@ import {
loadPersonDetailDialog, loadPersonDetailDialog,
} from "./show-dialog-person-detail"; } from "./show-dialog-person-detail";
import { User, fetchUsers } from "../../../data/user"; import { User, fetchUsers } from "../../../data/user";
import { configSections } from "../ha-panel-config";
class HaConfigPerson extends LitElement { class HaConfigPerson extends LitElement {
@property() public hass?: HomeAssistant; @property() public hass?: HomeAssistant;
@property() public isWide?: boolean; @property() public isWide?: boolean;
@property() public narrow?: boolean;
@property() public route!: Route;
@property() private _storageItems?: Person[]; @property() private _storageItems?: Person[];
@property() private _configItems?: Person[]; @property() private _configItems?: Person[];
private _usersLoad?: Promise<User[]>; private _usersLoad?: Promise<User[]>;
@ -48,9 +51,12 @@ class HaConfigPerson extends LitElement {
} }
const hass = this.hass; const hass = this.hass;
return html` return html`
<hass-subpage <hass-tabs-subpage
.header=${hass.localize("ui.panel.config.person.caption")} .hass=${this.hass}
.showBackButton=${!this.isWide} .narrow=${this.narrow}
.route=${this.route}
back-path="/config"
.tabs=${configSections.persons}
> >
<ha-config-section .isWide=${this.isWide}> <ha-config-section .isWide=${this.isWide}>
<span slot="header" <span slot="header"
@ -109,10 +115,11 @@ class HaConfigPerson extends LitElement {
` `
: ""} : ""}
</ha-config-section> </ha-config-section>
</hass-subpage> </hass-tabs-subpage>
<ha-fab <ha-fab
?is-wide=${this.isWide} ?is-wide=${this.isWide}
?narrow=${this.narrow}
icon="hass:plus" icon="hass:plus"
title="${hass.localize("ui.panel.config.person.add_person")}" title="${hass.localize("ui.panel.config.person.add_person")}"
@click=${this._createPerson} @click=${this._createPerson}
@ -231,7 +238,9 @@ ${this.hass!.localize("ui.panel.config.person.confirm_delete2")}`)
right: 16px; right: 16px;
z-index: 1; z-index: 1;
} }
ha-fab[narrow] {
bottom: 84px;
}
ha-fab[is-wide] { ha-fab[is-wide] {
bottom: 24px; bottom: 24px;
right: 24px; right: 24px;

View File

@ -54,6 +54,7 @@ class HaConfigScene extends HassRouterPage {
pageEl.hass = this.hass; pageEl.hass = this.hass;
pageEl.narrow = this.narrow; pageEl.narrow = this.narrow;
pageEl.isWide = this.isWide; pageEl.isWide = this.isWide;
pageEl.route = this.routeTail;
pageEl.showAdvanced = this.showAdvanced; pageEl.showAdvanced = this.showAdvanced;
if (this.hass) { if (this.hass) {

View File

@ -10,7 +10,7 @@ import {
import "@polymer/paper-icon-button/paper-icon-button"; import "@polymer/paper-icon-button/paper-icon-button";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
import "@polymer/paper-tooltip/paper-tooltip"; import "@polymer/paper-tooltip/paper-tooltip";
import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-fab"; import "../../../components/ha-fab";
@ -20,24 +20,29 @@ import "../ha-config-section";
import { computeStateName } from "../../../common/entity/compute_state_name"; import { computeStateName } from "../../../common/entity/compute_state_name";
import { computeRTL } from "../../../common/util/compute_rtl"; import { computeRTL } from "../../../common/util/compute_rtl";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import { SceneEntity, activateScene } from "../../../data/scene"; import { SceneEntity, activateScene } from "../../../data/scene";
import { showToast } from "../../../util/toast"; import { showToast } from "../../../util/toast";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import { forwardHaptic } from "../../../data/haptics"; import { forwardHaptic } from "../../../data/haptics";
import { configSections } from "../ha-panel-config";
@customElement("ha-scene-dashboard") @customElement("ha-scene-dashboard")
class HaSceneDashboard extends LitElement { class HaSceneDashboard extends LitElement {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public narrow!: boolean; @property() public narrow!: boolean;
@property() public isWide!: boolean; @property() public isWide!: boolean;
@property() public route!: Route;
@property() public scenes!: SceneEntity[]; @property() public scenes!: SceneEntity[];
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<hass-subpage <hass-tabs-subpage
.showBackButton=${!this.isWide} .hass=${this.hass}
.header=${this.hass.localize("ui.panel.config.scene.caption")} .narrow=${this.narrow}
back-path="/config"
.route=${this.route}
.tabs=${configSections.automation}
> >
<ha-config-section .isWide=${this.isWide}> <ha-config-section .isWide=${this.isWide}>
<div slot="header"> <div slot="header">
@ -72,8 +77,7 @@ class HaSceneDashboard extends LitElement {
` `
: this.scenes.map( : this.scenes.map(
(scene) => html` (scene) => html`
<div class="scene">
<div class='scene'>
<paper-icon-button <paper-icon-button
.scene=${scene} .scene=${scene}
icon="hass:play" icon="hass:play"
@ -82,46 +86,44 @@ class HaSceneDashboard extends LitElement {
)}" )}"
@click=${this._activateScene} @click=${this._activateScene}
></paper-icon-button> ></paper-icon-button>
<paper-item-body two-line> <paper-item-body two-line>
<div>${computeStateName(scene)}</div> <div>${computeStateName(scene)}</div>
</paper-item-body> </paper-item-body>
<a <div class="actions">
href=${ifDefined( <a
scene.attributes.id href=${ifDefined(
? `/config/scene/edit/${scene.attributes.id}` scene.attributes.id
: undefined ? `/config/scene/edit/${scene.attributes.id}`
)} : undefined
> )}
<paper-icon-button >
title="${this.hass.localize( <paper-icon-button
"ui.panel.config.scene.picker.edit_scene" title="${this.hass.localize(
)}" "ui.panel.config.scene.picker.edit_scene"
icon="hass:pencil" )}"
.disabled=${!scene.attributes.id} icon="hass:pencil"
></paper-icon-button> .disabled=${!scene.attributes.id}
${ ></paper-icon-button>
!scene.attributes.id ${!scene.attributes.id
? html` ? html`
<paper-tooltip position="left"> <paper-tooltip position="left">
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.scene.picker.only_editable" "ui.panel.config.scene.picker.only_editable"
)} )}
</paper-tooltip> </paper-tooltip>
` `
: "" : ""}
} </a>
</a>
</div> </div>
</a> </div>
` `
)} )}
</ha-card> </ha-card>
</ha-config-section> </ha-config-section>
<a href="/config/scene/edit/new"> <a href="/config/scene/edit/new">
<ha-fab <ha-fab
slot="fab" ?is-wide=${this.isWide}
?is-wide=${!this.narrow} ?narrow=${this.narrow}
icon="hass:plus" icon="hass:plus"
title=${this.hass.localize( title=${this.hass.localize(
"ui.panel.config.scene.picker.add_scene" "ui.panel.config.scene.picker.add_scene"
@ -129,7 +131,7 @@ class HaSceneDashboard extends LitElement {
?rtl=${computeRTL(this.hass)} ?rtl=${computeRTL(this.hass)}
></ha-fab> ></ha-fab>
</a> </a>
</hass-subpage> </hass-tabs-subpage>
`; `;
} }
@ -152,13 +154,11 @@ class HaSceneDashboard extends LitElement {
css` css`
:host { :host {
display: block; display: block;
} height: 100%;
hass-subpage {
min-height: 100vh;
} }
ha-card { ha-card {
padding-bottom: 8px;
margin-bottom: 56px; margin-bottom: 56px;
} }
@ -173,6 +173,10 @@ class HaSceneDashboard extends LitElement {
color: var(--primary-text-color); color: var(--primary-text-color);
} }
.actions {
display: flex;
}
ha-entity-toggle { ha-entity-toggle {
margin-right: 16px; margin-right: 16px;
} }
@ -188,7 +192,9 @@ class HaSceneDashboard extends LitElement {
bottom: 24px; bottom: 24px;
right: 24px; right: 24px;
} }
ha-fab[narrow] {
bottom: 84px;
}
ha-fab[rtl] { ha-fab[rtl] {
right: auto; right: auto;
left: 16px; left: 16px;

View File

@ -26,7 +26,7 @@ import "../../../layouts/ha-app-layout";
import { computeStateName } from "../../../common/entity/compute_state_name"; import { computeStateName } from "../../../common/entity/compute_state_name";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import { navigate } from "../../../common/navigate"; import { navigate } from "../../../common/navigate";
import { computeRTL } from "../../../common/util/compute_rtl"; import { computeRTL } from "../../../common/util/compute_rtl";
import { import {
@ -55,6 +55,7 @@ import memoizeOne from "memoize-one";
import { computeDomain } from "../../../common/entity/compute_domain"; import { computeDomain } from "../../../common/entity/compute_domain";
import { HassEvent } from "home-assistant-js-websocket"; import { HassEvent } from "home-assistant-js-websocket";
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
import { configSections } from "../ha-panel-config";
interface DeviceEntities { interface DeviceEntities {
id: string; id: string;
@ -69,7 +70,9 @@ interface DeviceEntitiesLookup {
@customElement("ha-scene-editor") @customElement("ha-scene-editor")
export class HaSceneEditor extends SubscribeMixin(LitElement) { export class HaSceneEditor extends SubscribeMixin(LitElement) {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public isWide?: boolean; @property() public narrow!: boolean;
@property() public isWide!: boolean;
@property() public route!: Route;
@property() public scene?: SceneEntity; @property() public scene?: SceneEntity;
@property() public creatingNew?: boolean; @property() public creatingNew?: boolean;
@property() public showAdvanced!: boolean; @property() public showAdvanced!: boolean;
@ -157,39 +160,36 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) {
this._deviceRegistryEntries this._deviceRegistryEntries
); );
return html` return html`
<ha-app-layout has-scrolling-region> <hass-tabs-subpage
<app-header slot="header" fixed> .hass=${this.hass}
<app-toolbar> .narrow=${this.narrow}
<ha-paper-icon-button-arrow-prev .route=${this.route}
@click=${this._backTapped} .backCallback=${() => this._backTapped()}
></ha-paper-icon-button-arrow-prev> .tabs=${configSections.automation}
<div main-title> >
${this.scene
? computeStateName(this.scene)
: this.hass.localize(
"ui.panel.config.scene.editor.default_name"
)}
</div>
${this.creatingNew
? ""
: html`
<paper-icon-button
title="${this.hass.localize(
"ui.panel.config.scene.picker.delete_scene"
)}"
icon="hass:delete"
@click=${this._deleteTapped}
></paper-icon-button>
`}
</app-toolbar>
</app-header>
<div class="content"> ${
${this._errors this.creatingNew
? html` ? ""
<div class="errors">${this._errors}</div> : html`
` <paper-icon-button
: ""} slot="toolbar-icon"
title="${this.hass.localize(
"ui.panel.config.scene.picker.delete_scene"
)}"
icon="hass:delete"
@click=${this._deleteTapped}
></paper-icon-button>
`
}
${
this._errors
? html`
<div class="errors">${this._errors}</div>
`
: ""
}
<div <div
id="root" id="root"
class="${classMap({ class="${classMap({
@ -198,11 +198,13 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) {
> >
<ha-config-section .isWide=${this.isWide}> <ha-config-section .isWide=${this.isWide}>
<div slot="header"> <div slot="header">
${this.scene ${
? computeStateName(this.scene) this.scene
: this.hass.localize( ? computeStateName(this.scene)
"ui.panel.config.scene.editor.default_name" : this.hass.localize(
)} "ui.panel.config.scene.editor.default_name"
)
}
</div> </div>
<div slot="introduction"> <div slot="introduction">
${this.hass.localize( ${this.hass.localize(
@ -291,87 +293,88 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) {
</ha-card> </ha-card>
</ha-config-section> </ha-config-section>
${this.showAdvanced ${
? html` this.showAdvanced
<ha-config-section .isWide=${this.isWide}> ? html`
<div slot="header"> <ha-config-section .isWide=${this.isWide}>
${this.hass.localize( <div slot="header">
"ui.panel.config.scene.editor.entities.header"
)}
</div>
<div slot="introduction">
${this.hass.localize(
"ui.panel.config.scene.editor.entities.introduction"
)}
</div>
${entities.length
? html`
<ha-card
class="entities"
.header=${this.hass.localize(
"ui.panel.config.scene.editor.entities.without_device"
)}
>
${entities.map((entityId) => {
const stateObj = this.hass.states[entityId];
if (!stateObj) {
return html``;
}
return html`
<paper-icon-item
.entityId=${entityId}
@click=${this._showMoreInfo}
class="device-entity"
>
<state-badge
.stateObj=${stateObj}
slot="item-icon"
></state-badge>
<paper-item-body>
${computeStateName(stateObj)}
</paper-item-body>
<paper-icon-button
icon="hass:delete"
.entityId=${entityId}
.title="${this.hass.localize(
"ui.panel.config.scene.editor.entities.delete"
)}"
@click=${this._deleteEntity}
></paper-icon-button>
</paper-icon-item>
`;
})}
</ha-card>
`
: ""}
<ha-card
header=${this.hass.localize(
"ui.panel.config.scene.editor.entities.add"
)}
>
<div class="card-content">
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.scene.editor.entities.device_entities" "ui.panel.config.scene.editor.entities.header"
)} )}
<ha-entity-picker
@value-changed=${this._entityPicked}
.excludeDomains=${SCENE_IGNORED_DOMAINS}
.hass=${this.hass}
label=${this.hass.localize(
"ui.panel.config.scene.editor.entities.add"
)}
/>
</div> </div>
</ha-card> <div slot="introduction">
</ha-config-section> ${this.hass.localize(
` "ui.panel.config.scene.editor.entities.introduction"
: ""} )}
</div>
${entities.length
? html`
<ha-card
class="entities"
.header=${this.hass.localize(
"ui.panel.config.scene.editor.entities.without_device"
)}
>
${entities.map((entityId) => {
const stateObj = this.hass.states[entityId];
if (!stateObj) {
return html``;
}
return html`
<paper-icon-item
.entityId=${entityId}
@click=${this._showMoreInfo}
class="device-entity"
>
<state-badge
.stateObj=${stateObj}
slot="item-icon"
></state-badge>
<paper-item-body>
${computeStateName(stateObj)}
</paper-item-body>
<paper-icon-button
icon="hass:delete"
.entityId=${entityId}
.title="${this.hass.localize(
"ui.panel.config.scene.editor.entities.delete"
)}"
@click=${this._deleteEntity}
></paper-icon-button>
</paper-icon-item>
`;
})}
</ha-card>
`
: ""}
<ha-card
header=${this.hass.localize(
"ui.panel.config.scene.editor.entities.add"
)}
>
<div class="card-content">
${this.hass.localize(
"ui.panel.config.scene.editor.entities.device_entities"
)}
<ha-entity-picker
@value-changed=${this._entityPicked}
.excludeDomains=${SCENE_IGNORED_DOMAINS}
.hass=${this.hass}
label=${this.hass.localize(
"ui.panel.config.scene.editor.entities.add"
)}
/>
</div>
</ha-card>
</ha-config-section>
`
: ""
}
</div> </div>
</div>
<ha-fab <ha-fab
slot="fab"
?is-wide="${this.isWide}" ?is-wide="${this.isWide}"
?narrow="${this.narrow}"
?dirty="${this._dirty}" ?dirty="${this._dirty}"
icon="hass:content-save" icon="hass:content-save"
.title="${this.hass.localize("ui.panel.config.scene.editor.save")}" .title="${this.hass.localize("ui.panel.config.scene.editor.save")}"
@ -706,7 +709,10 @@ export class HaSceneEditor extends SubscribeMixin(LitElement) {
bottom: 24px; bottom: 24px;
right: 24px; right: 24px;
} }
ha-fab[narrow] {
bottom: 84px;
margin-bottom: -140px;
}
ha-fab[dirty] { ha-fab[dirty] {
margin-bottom: 0; margin-bottom: 0;
} }

View File

@ -34,6 +34,8 @@ class HaConfigScript extends PolymerElement {
hass="[[hass]]" hass="[[hass]]"
scripts="[[scripts]]" scripts="[[scripts]]"
is-wide="[[isWide]]" is-wide="[[isWide]]"
narrow="[[narrow]]"
route="[[route]]"
></ha-script-picker> ></ha-script-picker>
</template> </template>
@ -42,6 +44,8 @@ class HaConfigScript extends PolymerElement {
hass="[[hass]]" hass="[[hass]]"
script="[[script]]" script="[[script]]"
is-wide="[[isWide]]" is-wide="[[isWide]]"
narrow="[[narrow]]"
route="[[route]]"
creating-new="[[_creatingNew]]" creating-new="[[_creatingNew]]"
></ha-script-editor> ></ha-script-editor>
</template> </template>
@ -53,6 +57,7 @@ class HaConfigScript extends PolymerElement {
hass: Object, hass: Object,
route: Object, route: Object,
isWide: Boolean, isWide: Boolean,
narrow: Boolean,
_routeData: Object, _routeData: Object,
_routeMatches: Boolean, _routeMatches: Boolean,
_creatingNew: Boolean, _creatingNew: Boolean,

View File

@ -11,7 +11,6 @@ import {
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { classMap } from "lit-html/directives/class-map"; import { classMap } from "lit-html/directives/class-map";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate"; import { navigate } from "../../../common/navigate";
import { computeRTL } from "../../../common/util/compute_rtl"; import { computeRTL } from "../../../common/util/compute_rtl";
import "../../../components/ha-fab"; import "../../../components/ha-fab";
@ -25,14 +24,17 @@ import {
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box"; import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/ha-app-layout"; import "../../../layouts/ha-app-layout";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import "../automation/action/ha-automation-action"; import "../automation/action/ha-automation-action";
import { computeObjectId } from "../../../common/entity/compute_object_id"; import { computeObjectId } from "../../../common/entity/compute_object_id";
import { configSections } from "../ha-panel-config";
export class HaScriptEditor extends LitElement { export class HaScriptEditor extends LitElement {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public script!: ScriptEntity; @property() public script!: ScriptEntity;
@property() public isWide?: boolean; @property() public isWide?: boolean;
@property() public narrow!: boolean;
@property() public route!: Route;
@property() public creatingNew?: boolean; @property() public creatingNew?: boolean;
@property() private _config?: ScriptConfig; @property() private _config?: ScriptConfig;
@property() private _dirty?: boolean; @property() private _dirty?: boolean;
@ -40,32 +42,25 @@ export class HaScriptEditor extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<ha-app-layout has-scrolling-region> <hass-tabs-subpage
<app-header slot="header" fixed> .hass=${this.hass}
<app-toolbar> .narrow=${this.narrow}
<ha-paper-icon-button-arrow-prev .route=${this.route}
@click=${this._backTapped} .backCallback=${() => this._backTapped()}
></ha-paper-icon-button-arrow-prev> .tabs=${configSections.automation}
<div main-title> >
${this.script ${this.creatingNew
? computeStateName(this.script) ? ""
: this.hass.localize( : html`
"ui.panel.config.script.editor.default_name" <paper-icon-button
)} slot="toolbar-icon"
</div> title="${this.hass.localize(
${this.creatingNew "ui.panel.config.script.editor.delete_script"
? "" )}"
: html` icon="hass:delete"
<paper-icon-button @click=${this._deleteConfirm}
title="${this.hass.localize( ></paper-icon-button>
"ui.panel.config.script.editor.delete_script" `}
)}"
icon="hass:delete"
@click=${this._deleteConfirm}
></paper-icon-button>
`}
</app-toolbar>
</app-header>
<div class="content"> <div class="content">
${this._errors ${this._errors
@ -134,9 +129,9 @@ export class HaScriptEditor extends LitElement {
</div> </div>
</div> </div>
<ha-fab <ha-fab
slot="fab" ?is-wide=${this.isWide}
?is-wide="${this.isWide}" ?narrow=${this.narrow}
?dirty="${this._dirty}" ?dirty=${this._dirty}
icon="hass:content-save" icon="hass:content-save"
.title="${this.hass.localize("ui.common.save")}" .title="${this.hass.localize("ui.common.save")}"
@click=${this._saveScript} @click=${this._saveScript}
@ -144,7 +139,7 @@ export class HaScriptEditor extends LitElement {
rtl: computeRTL(this.hass), rtl: computeRTL(this.hass),
})}" })}"
></ha-fab> ></ha-fab>
</ha-app-layout> </hass-tabs-subpage>
`; `;
} }
@ -301,7 +296,10 @@ export class HaScriptEditor extends LitElement {
bottom: 24px; bottom: 24px;
right: 24px; right: 24px;
} }
ha-fab[narrow] {
bottom: 84px;
margin-bottom: -140px;
}
ha-fab[dirty] { ha-fab[dirty] {
margin-bottom: 0; margin-bottom: 0;
} }

View File

@ -11,7 +11,7 @@ import "@polymer/paper-icon-button/paper-icon-button";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
import { HassEntity } from "home-assistant-js-websocket"; import { HassEntity } from "home-assistant-js-websocket";
import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage";
import { computeRTL } from "../../../common/util/compute_rtl"; import { computeRTL } from "../../../common/util/compute_rtl";
@ -22,21 +22,27 @@ import "../ha-config-section";
import { computeStateName } from "../../../common/entity/compute_state_name"; import { computeStateName } from "../../../common/entity/compute_state_name";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import { triggerScript } from "../../../data/script"; import { triggerScript } from "../../../data/script";
import { showToast } from "../../../util/toast"; import { showToast } from "../../../util/toast";
import { configSections } from "../ha-panel-config";
@customElement("ha-script-picker") @customElement("ha-script-picker")
class HaScriptPicker extends LitElement { class HaScriptPicker extends LitElement {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public scripts!: HassEntity[]; @property() public scripts!: HassEntity[];
@property() public isWide!: boolean; @property() public isWide!: boolean;
@property() public narrow!: boolean;
@property() public route!: Route;
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<hass-subpage <hass-tabs-subpage
.showBackButton=${!this.isWide} .hass=${this.hass}
.header=${this.hass.localize("ui.panel.config.script.caption")} .narrow=${this.narrow}
back-path="/config"
.route=${this.route}
.tabs=${configSections.automation}
> >
<ha-config-section .isWide=${this.isWide}> <ha-config-section .isWide=${this.isWide}>
<div slot="header"> <div slot="header">
@ -99,8 +105,8 @@ class HaScriptPicker extends LitElement {
<a href="/config/script/new"> <a href="/config/script/new">
<ha-fab <ha-fab
slot="fab"
?is-wide=${this.isWide} ?is-wide=${this.isWide}
?narrow=${this.narrow}
icon="hass:plus" icon="hass:plus"
title="${this.hass.localize( title="${this.hass.localize(
"ui.panel.config.script.picker.add_script" "ui.panel.config.script.picker.add_script"
@ -108,7 +114,7 @@ class HaScriptPicker extends LitElement {
?rtl=${computeRTL(this.hass)} ?rtl=${computeRTL(this.hass)}
></ha-fab> ></ha-fab>
</a> </a>
</hass-subpage> </hass-tabs-subpage>
`; `;
} }
@ -169,7 +175,9 @@ class HaScriptPicker extends LitElement {
bottom: 24px; bottom: 24px;
right: 24px; right: 24px;
} }
ha-fab[narrow] {
bottom: 84px;
}
ha-fab[rtl] { ha-fab[rtl] {
right: auto; right: auto;
left: 16px; left: 16px;

View File

@ -4,12 +4,13 @@ import "@polymer/paper-icon-button/paper-icon-button";
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage";
import "../../../resources/ha-style"; import "../../../resources/ha-style";
import "./ha-config-section-server-control"; import "./ha-config-section-server-control";
import LocalizeMixin from "../../../mixins/localize-mixin"; import LocalizeMixin from "../../../mixins/localize-mixin";
import { configSections } from "../ha-panel-config";
/* /*
* @appliesMixin LocalizeMixin * @appliesMixin LocalizeMixin
@ -33,9 +34,13 @@ class HaConfigServerControl extends LocalizeMixin(PolymerElement) {
} }
</style> </style>
<hass-subpage <hass-tabs-subpage
header="[[localize('ui.panel.config.server_control.caption')]]" hass="[[hass]]"
show-back-button="[[!isWide]]" narrow="[[narrow]]"
route="[[route]]"
back-path="/config"
tabs="[[_computeTabs()]]"
show-advanced="[[showAdvanced]]"
> >
<div class$="[[computeClasses(isWide)]]"> <div class$="[[computeClasses(isWide)]]">
<ha-config-section-server-control <ha-config-section-server-control
@ -44,7 +49,7 @@ class HaConfigServerControl extends LocalizeMixin(PolymerElement) {
hass="[[hass]]" hass="[[hass]]"
></ha-config-section-server-control> ></ha-config-section-server-control>
</div> </div>
</hass-subpage> </hass-tabs-subpage>
`; `;
} }
@ -52,10 +57,16 @@ class HaConfigServerControl extends LocalizeMixin(PolymerElement) {
return { return {
hass: Object, hass: Object,
isWide: Boolean, isWide: Boolean,
narrow: Boolean,
route: Object,
showAdvanced: Boolean, showAdvanced: Boolean,
}; };
} }
_computeTabs() {
return configSections.general;
}
computeClasses(isWide) { computeClasses(isWide) {
return isWide ? "content" : "content narrow"; return isWide ? "content" : "content narrow";
} }

View File

@ -3,7 +3,7 @@ import "@polymer/paper-item/paper-item-body";
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage";
import "../../../components/ha-icon-next"; import "../../../components/ha-icon-next";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-fab"; import "../../../components/ha-fab";
@ -13,6 +13,7 @@ import NavigateMixin from "../../../mixins/navigate-mixin";
import { EventsMixin } from "../../../mixins/events-mixin"; import { EventsMixin } from "../../../mixins/events-mixin";
import { computeRTL } from "../../../common/util/compute_rtl"; import { computeRTL } from "../../../common/util/compute_rtl";
import { configSections } from "../ha-panel-config";
let registeredDialog = false; let registeredDialog = false;
@ -41,6 +42,9 @@ class HaUserPicker extends EventsMixin(
right: auto; right: auto;
left: 16px; left: 16px;
} }
ha-fab[narrow] {
bottom: 84px;
}
ha-fab[rtl][is-wide] { ha-fab[rtl][is-wide] {
bottom: 24px; bottom: 24px;
right: auto; right: auto;
@ -58,9 +62,12 @@ class HaUserPicker extends EventsMixin(
} }
</style> </style>
<hass-subpage <hass-tabs-subpage
header="[[localize('ui.panel.config.users.picker.title')]]" hass="[[hass]]"
show-back-button="[[!isWide]]" narrow="[[narrow]]"
route="[[route]]"
back-path="/config"
tabs="[[_computeTabs()]]"
> >
<ha-card> <ha-card>
<template is="dom-repeat" items="[[users]]" as="user"> <template is="dom-repeat" items="[[users]]" as="user">
@ -84,12 +91,13 @@ class HaUserPicker extends EventsMixin(
<ha-fab <ha-fab
is-wide$="[[isWide]]" is-wide$="[[isWide]]"
narrow$="[[narrow]]"
icon="hass:plus" icon="hass:plus"
title="[[localize('ui.panel.config.users.picker.add_user')]]" title="[[localize('ui.panel.config.users.picker.add_user')]]"
on-click="_addUser" on-click="_addUser"
rtl$="[[rtl]]" rtl$="[[rtl]]"
></ha-fab> ></ha-fab>
</hass-subpage> </hass-tabs-subpage>
`; `;
} }
@ -98,6 +106,8 @@ class HaUserPicker extends EventsMixin(
hass: Object, hass: Object,
users: Array, users: Array,
isWide: Boolean, isWide: Boolean,
narrow: Boolean,
route: Object,
rtl: { rtl: {
type: Boolean, type: Boolean,
reflectToAttribute: true, reflectToAttribute: true,
@ -138,6 +148,10 @@ class HaUserPicker extends EventsMixin(
return computeRTL(hass); return computeRTL(hass);
} }
_computeTabs() {
return configSections.persons;
}
_addUser() { _addUser() {
this.fire("show-add-user", { this.fire("show-add-user", {
hass: this.hass, hass: this.hass,

View File

@ -28,6 +28,8 @@ class HaConfigUsers extends NavigateMixin(PolymerElement) {
hass="[[hass]]" hass="[[hass]]"
users="[[_users]]" users="[[_users]]"
is-wide="[[isWide]]" is-wide="[[isWide]]"
narrow="[[narrow]]"
route="[[route]]"
></ha-config-user-picker> ></ha-config-user-picker>
</template> </template>
<template <template
@ -38,6 +40,8 @@ class HaConfigUsers extends NavigateMixin(PolymerElement) {
<ha-user-editor <ha-user-editor
hass="[[hass]]" hass="[[hass]]"
user="[[_computeUser(_users, _routeData.user)]]" user="[[_computeUser(_users, _routeData.user)]]"
narrow="[[narrow]]"
route="[[route]]"
></ha-user-editor> ></ha-user-editor>
</template> </template>
`; `;
@ -47,6 +51,7 @@ class HaConfigUsers extends NavigateMixin(PolymerElement) {
return { return {
hass: Object, hass: Object,
isWide: Boolean, isWide: Boolean,
narrow: Boolean,
route: { route: {
type: Object, type: Object,
observer: "_checkRoute", observer: "_checkRoute",

View File

@ -10,10 +10,10 @@ import {
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import "@material/mwc-button"; import "@material/mwc-button";
import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage";
import { haStyle } from "../../../resources/styles"; import { haStyle } from "../../../resources/styles";
import "../../../components/ha-card"; import "../../../components/ha-card";
import { HomeAssistant } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate"; import { navigate } from "../../../common/navigate";
import { import {
@ -29,6 +29,7 @@ import {
showConfirmationDialog, showConfirmationDialog,
showPromptDialog, showPromptDialog,
} from "../../../dialogs/generic/show-dialog-box"; } from "../../../dialogs/generic/show-dialog-box";
import { configSections } from "../ha-panel-config";
declare global { declare global {
interface HASSDomEvents { interface HASSDomEvents {
@ -42,6 +43,8 @@ const GROUPS = [SYSTEM_GROUP_ID_USER, SYSTEM_GROUP_ID_ADMIN];
class HaUserEditor extends LitElement { class HaUserEditor extends LitElement {
@property() public hass?: HomeAssistant; @property() public hass?: HomeAssistant;
@property() public user?: User; @property() public user?: User;
@property() public narrow?: boolean;
@property() public route!: Route;
protected render(): TemplateResult { protected render(): TemplateResult {
const hass = this.hass; const hass = this.hass;
@ -51,8 +54,11 @@ class HaUserEditor extends LitElement {
} }
return html` return html`
<hass-subpage <hass-tabs-subpage
.header=${hass.localize("ui.panel.config.users.editor.caption")} .hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
.tabs=${configSections.persons}
> >
<ha-card .header=${this._name}> <ha-card .header=${this._name}>
<table class="card-content"> <table class="card-content">
@ -130,7 +136,7 @@ class HaUserEditor extends LitElement {
: ""} : ""}
</div> </div>
</ha-card> </ha-card>
</hass-subpage> </hass-tabs-subpage>
`; `;
} }
@ -225,7 +231,7 @@ class HaUserEditor extends LitElement {
} }
ha-card { ha-card {
max-width: 600px; max-width: 600px;
margin: 0 auto 16px; margin: 16px auto 16px;
} }
hass-subpage ha-card:first-of-type { hass-subpage ha-card:first-of-type {
direction: ltr; direction: ltr;

View File

@ -16,10 +16,10 @@ import "@polymer/paper-tooltip/paper-tooltip";
import "../../../components/map/ha-locations-editor"; import "../../../components/map/ha-locations-editor";
import { HomeAssistant } from "../../../types"; import { HomeAssistant, Route } from "../../../types";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-fab"; import "../../../components/ha-fab";
import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage";
import "../../../layouts/hass-loading-screen"; import "../../../layouts/hass-loading-screen";
import { compare } from "../../../common/string/compare"; import { compare } from "../../../common/string/compare";
import "../ha-config-section"; import "../ha-config-section";
@ -42,12 +42,14 @@ import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import { subscribeEntityRegistry } from "../../../data/entity_registry"; import { subscribeEntityRegistry } from "../../../data/entity_registry";
import { configSections } from "../ha-panel-config";
@customElement("ha-config-zone") @customElement("ha-config-zone")
export class HaConfigZone extends SubscribeMixin(LitElement) { export class HaConfigZone extends SubscribeMixin(LitElement) {
@property() public hass!: HomeAssistant; @property() public hass!: HomeAssistant;
@property() public isWide?: boolean; @property() public isWide?: boolean;
@property() public narrow?: boolean; @property() public narrow?: boolean;
@property() public route!: Route;
@property() private _storageItems?: Zone[]; @property() private _storageItems?: Zone[];
@property() private _stateItems?: HassEntity[]; @property() private _stateItems?: HassEntity[];
@property() private _activeEntry: string = ""; @property() private _activeEntry: string = "";
@ -179,7 +181,13 @@ export class HaConfigZone extends SubscribeMixin(LitElement) {
`; `;
return html` return html`
<hass-subpage .header=${hass.localize("ui.panel.config.zone.caption")}> <hass-tabs-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
back-path="/config"
.tabs=${configSections.persons}
>
${this.narrow ${this.narrow
? html` ? html`
<ha-config-section .isWide=${this.isWide}> <ha-config-section .isWide=${this.isWide}>
@ -206,10 +214,11 @@ export class HaConfigZone extends SubscribeMixin(LitElement) {
</div> </div>
` `
: ""} : ""}
</hass-subpage> </hass-tabs-subpage>
<ha-fab <ha-fab
?is-wide=${this.isWide} ?is-wide=${this.isWide}
?narrow=${this.narrow}
icon="hass:plus" icon="hass:plus"
title="${hass.localize("ui.panel.config.zone.add_zone")}" title="${hass.localize("ui.panel.config.zone.add_zone")}"
@click=${this._createZone} @click=${this._createZone}
@ -389,6 +398,10 @@ ${this.hass!.localize("ui.panel.config.zone.confirm_delete2")}`)
static get styles(): CSSResult { static get styles(): CSSResult {
return css` return css`
hass-loading-screen {
--app-header-background-color: var(--sidebar-background-color);
--app-header-text-color: var(--sidebar-text-color);
}
a { a {
color: var(--primary-color); color: var(--primary-color);
} }
@ -449,6 +462,9 @@ ${this.hass!.localize("ui.panel.config.zone.confirm_delete2")}`)
bottom: 24px; bottom: 24px;
right: 24px; right: 24px;
} }
ha-fab[narrow] {
bottom: 84px;
}
`; `;
} }
} }