Add subview option to dashboard views (#13822)

This commit is contained in:
Paul Bottein 2022-09-27 15:37:48 +02:00 committed by GitHub
parent 51874329d1
commit c79955e76a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 110 additions and 21 deletions

View File

@ -93,6 +93,8 @@ export interface LovelaceViewConfig {
panel?: boolean; panel?: boolean;
background?: string; background?: string;
visible?: boolean | ShowViewConfig[]; visible?: boolean | ShowViewConfig[];
subview?: boolean;
back_path?: string;
} }
export interface LovelaceViewElement extends HTMLElement { export interface LovelaceViewElement extends HTMLElement {

View File

@ -1,10 +1,10 @@
import "../../../../components/ha-form/ha-form";
import { html, LitElement, TemplateResult } from "lit"; import { html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { slugify } from "../../../../common/string/slugify"; import { slugify } from "../../../../common/string/slugify";
import type { LocalizeFunc } from "../../../../common/translations/localize"; import type { LocalizeFunc } from "../../../../common/translations/localize";
import "../../../../components/ha-form/ha-form";
import type { SchemaUnion } from "../../../../components/ha-form/types"; import type { SchemaUnion } from "../../../../components/ha-form/types";
import type { LovelaceViewConfig } from "../../../../data/lovelace"; import type { LovelaceViewConfig } from "../../../../data/lovelace";
import type { HomeAssistant } from "../../../../types"; import type { HomeAssistant } from "../../../../types";
@ -33,7 +33,7 @@ export class HuiViewEditor extends LitElement {
private _suggestedPath = false; private _suggestedPath = false;
private _schema = memoizeOne( private _schema = memoizeOne(
(localize: LocalizeFunc) => (localize: LocalizeFunc, subview: boolean, showAdvanced: boolean) =>
[ [
{ name: "title", selector: { text: {} } }, { name: "title", selector: { text: {} } },
{ {
@ -63,6 +63,20 @@ export class HuiViewEditor extends LitElement {
}, },
}, },
}, },
{
name: "subview",
selector: {
boolean: {},
},
},
...(subview && showAdvanced
? [
{
name: "back_path",
selector: { navigation: {} },
},
]
: []),
] as const ] as const
); );
@ -84,7 +98,12 @@ export class HuiViewEditor extends LitElement {
return html``; return html``;
} }
const schema = this._schema(this.hass.localize); const schema = this._schema(
this.hass.localize,
this._config.subview ?? false,
this.hass.userData?.showAdvanced ?? false
);
const data = { const data = {
theme: "Backend-selected", theme: "Backend-selected",
...this._config, ...this._config,
@ -96,18 +115,22 @@ export class HuiViewEditor extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.data=${data} .data=${data}
.schema=${schema} .schema=${schema}
.computeLabel=${this._computeLabelCallback} .computeLabel=${this._computeLabel}
.computeHelper=${this._computeHelper}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
></ha-form> ></ha-form>
`; `;
} }
private _valueChanged(ev: CustomEvent): void { private _valueChanged(ev: CustomEvent): void {
const config = ev.detail.value; const config = ev.detail.value as LovelaceViewConfig;
if (config.type === "masonry") { if (config.type === "masonry") {
delete config.type; delete config.type;
} }
if (!config.subview) {
delete config.back_path;
}
if ( if (
this.isNew && this.isNew &&
@ -122,7 +145,7 @@ export class HuiViewEditor extends LitElement {
fireEvent(this, "view-config-changed", { config }); fireEvent(this, "view-config-changed", { config });
} }
private _computeLabelCallback = ( private _computeLabel = (
schema: SchemaUnion<ReturnType<typeof this._schema>> schema: SchemaUnion<ReturnType<typeof this._schema>>
) => { ) => {
switch (schema.name) { switch (schema.name) {
@ -130,12 +153,35 @@ export class HuiViewEditor extends LitElement {
return this.hass!.localize("ui.panel.lovelace.editor.card.generic.url"); return this.hass!.localize("ui.panel.lovelace.editor.card.generic.url");
case "type": case "type":
return this.hass.localize("ui.panel.lovelace.editor.edit_view.type"); return this.hass.localize("ui.panel.lovelace.editor.edit_view.type");
case "subview":
return this.hass.localize("ui.panel.lovelace.editor.edit_view.subview");
case "back_path":
return this.hass.localize(
"ui.panel.lovelace.editor.edit_view.back_path"
);
default: default:
return this.hass!.localize( return this.hass!.localize(
`ui.panel.lovelace.editor.card.generic.${schema.name}` `ui.panel.lovelace.editor.card.generic.${schema.name}`
); );
} }
}; };
private _computeHelper = (
schema: SchemaUnion<ReturnType<typeof this._schema>>
) => {
switch (schema.name) {
case "subview":
return this.hass.localize(
"ui.panel.lovelace.editor.edit_view.subview_helper"
);
case "back_path":
return this.hass.localize(
"ui.panel.lovelace.editor.edit_view.back_path_helper"
);
default:
return undefined;
}
};
} }
declare global { declare global {

View File

@ -112,6 +112,11 @@ class HUIRoot extends LitElement {
} }
protected render(): TemplateResult { protected render(): TemplateResult {
const views = this.lovelace?.config.views ?? [];
const curViewConfig =
typeof this._curView === "number" ? views[this._curView] : undefined;
return html` return html`
<ha-app-layout <ha-app-layout
class=${classMap({ class=${classMap({
@ -229,11 +234,21 @@ class HUIRoot extends LitElement {
` `
: html` : html`
<app-toolbar> <app-toolbar>
<ha-menu-button ${curViewConfig?.subview
.hass=${this.hass} ? html`
.narrow=${this.narrow} <ha-icon-button-arrow-prev
></ha-menu-button> @click=${this._goBack}
${this.lovelace!.config.views.length > 1 ></ha-icon-button-arrow-prev>
`
: html`
<ha-menu-button
.hass=${this.hass}
.narrow=${this.narrow}
></ha-menu-button>
`}
${curViewConfig?.subview
? html`<div main-title>${curViewConfig.title}</div>`
: views.filter((view) => !view.subview).length > 1
? html` ? html`
<ha-tabs <ha-tabs
scrollable scrollable
@ -241,18 +256,20 @@ class HUIRoot extends LitElement {
@iron-activate=${this._handleViewSelected} @iron-activate=${this._handleViewSelected}
dir=${computeRTLDirection(this.hass!)} dir=${computeRTLDirection(this.hass!)}
> >
${this.lovelace!.config.views.map( ${views.map(
(view) => html` (view) => html`
<paper-tab <paper-tab
aria-label=${ifDefined(view.title)} aria-label=${ifDefined(view.title)}
class=${classMap({ class=${classMap({
"hide-tab": Boolean( "hide-tab": Boolean(
view.visible !== undefined && view.subview ||
((Array.isArray(view.visible) && (view.visible !== undefined &&
!view.visible.some( ((Array.isArray(view.visible) &&
(e) => e.user === this.hass!.user!.id !view.visible.some(
)) || (e) =>
view.visible === false) e.user === this.hass!.user!.id
)) ||
view.visible === false))
), ),
})} })}
> >
@ -473,7 +490,7 @@ class HUIRoot extends LitElement {
@iron-activate=${this._handleViewSelected} @iron-activate=${this._handleViewSelected}
dir=${computeRTLDirection(this.hass!)} dir=${computeRTLDirection(this.hass!)}
> >
${this.lovelace!.config.views.map( ${views.map(
(view) => html` (view) => html`
<paper-tab <paper-tab
aria-label=${ifDefined(view.title)} aria-label=${ifDefined(view.title)}
@ -505,6 +522,9 @@ class HUIRoot extends LitElement {
${view.icon ${view.icon
? html` ? html`
<ha-icon <ha-icon
class=${classMap({
"child-view-icon": Boolean(view.subview),
})}
title=${ifDefined(view.title)} title=${ifDefined(view.title)}
.icon=${view.icon} .icon=${view.icon}
></ha-icon> ></ha-icon>
@ -528,7 +548,7 @@ class HUIRoot extends LitElement {
class="edit-icon view" class="edit-icon view"
@click=${this._moveViewRight} @click=${this._moveViewRight}
?disabled=${(this._curView! as number) + 1 === ?disabled=${(this._curView! as number) + 1 ===
this.lovelace!.config.views.length} views.length}
></ha-icon-button-arrow-next> ></ha-icon-button-arrow-next>
` `
: ""} : ""}
@ -720,6 +740,20 @@ class HUIRoot extends LitElement {
}); });
} }
private _goBack(): void {
const views = this.lovelace?.config.views ?? [];
const curViewConfig =
typeof this._curView === "number" ? views[this._curView] : undefined;
if (curViewConfig?.back_path) {
navigate(curViewConfig.back_path);
} else if (history.length > 0) {
history.back();
} else {
navigate(views[0].path!);
}
}
private _handleRawEditor(ev: CustomEvent<RequestSelectedDetail>): void { private _handleRawEditor(ev: CustomEvent<RequestSelectedDetail>): void {
if (!shouldHandleRequestSelectedEvent(ev)) { if (!shouldHandleRequestSelectedEvent(ev)) {
return; return;
@ -1019,6 +1053,9 @@ class HUIRoot extends LitElement {
--mdc-button-outline-color: var(--app-header-edit-text-color, #fff); --mdc-button-outline-color: var(--app-header-edit-text-color, #fff);
--mdc-typography-button-font-size: 14px; --mdc-typography-button-font-size: 14px;
} }
.child-view-icon {
opacity: 0.5;
}
`, `,
]; ];
} }

View File

@ -3749,7 +3749,11 @@
"masonry": "Masonry (default)", "masonry": "Masonry (default)",
"sidebar": "Sidebar", "sidebar": "Sidebar",
"panel": "Panel (1 card)" "panel": "Panel (1 card)"
} },
"subview": "Subview",
"subview_helper": "Subviews don't appear in tabs and have a back button.",
"back_path": "Back path (optional)",
"back_path_helper": "Only for subviews. If empty, clicking on back button will go to the previous page."
}, },
"edit_badges": { "edit_badges": {
"view_no_badges": "Badges are not be supported by the current view type." "view_no_badges": "Badges are not be supported by the current view type."