Unify usage of dashboard title (#20853)

This commit is contained in:
Paul Bottein 2024-05-29 12:29:52 +02:00 committed by GitHub
parent ff9c794659
commit 7a7bd87f50
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 122 additions and 291 deletions

View File

@ -1,5 +1,5 @@
import "@material/mwc-button/mwc-button";
import { mdiCast, mdiCastConnected } from "@mdi/js";
import { mdiCast, mdiCastConnected, mdiViewDashboard } from "@mdi/js";
import "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-listbox/paper-listbox";
import { Auth, Connection } from "home-assistant-js-websocket";
@ -104,8 +104,11 @@ class HcCast extends LitElement {
slot="item-icon"
></ha-icon>
`
: ""}
${view.title || view.path}
: html`<ha-svg-icon
slot="item-icon"
.path=${mdiViewDashboard}
></ha-svg-icon>`}
${view.title || view.path || "Unnamed view"}
</paper-icon-item>
`
)}
@ -250,7 +253,8 @@ class HcCast extends LitElement {
padding-top: 0;
}
paper-listbox ha-icon {
paper-listbox ha-icon,
paper-listbox ha-svg-icon {
padding: 12px;
color: var(--secondary-text-color);
}

View File

@ -2,6 +2,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import { LovelaceConfig } from "../../../../src/data/lovelace/config/types";
import { getPanelTitleFromUrlPath } from "../../../../src/data/panel";
import { Lovelace } from "../../../../src/panels/lovelace/types";
import "../../../../src/panels/lovelace/views/hui-view";
import { HomeAssistant } from "../../../../src/types";
@ -61,7 +62,12 @@ class HcLovelace extends LitElement {
const index = this._viewIndex;
if (index !== undefined) {
const dashboardTitle = this.lovelaceConfig.title || this.urlPath;
const title = getPanelTitleFromUrlPath(
this.hass,
this.urlPath || "lovelace"
);
const dashboardTitle = title || this.urlPath;
const viewTitle =
this.lovelaceConfig.views[index].title ||

View File

@ -35,6 +35,7 @@ import { loadLovelaceResources } from "../../../../src/panels/lovelace/common/lo
import { HassElement } from "../../../../src/state/hass-element";
import { castContext } from "../cast_context";
import "./hc-launch-screen";
import { getPanelTitleFromUrlPath } from "../../../../src/data/panel";
const DEFAULT_CONFIG: LovelaceDashboardStrategyConfig = {
strategy: {
@ -359,7 +360,11 @@ export class HcMain extends HassElement {
}
private _handleNewLovelaceConfig(lovelaceConfig: LovelaceConfig) {
castContext.setApplicationState(lovelaceConfig.title || "");
const title = getPanelTitleFromUrlPath(
this.hass!,
this._urlPath || "lovelace"
);
castContext.setApplicationState(title || "");
this._lovelaceConfig = lovelaceConfig;
}

View File

@ -7,7 +7,6 @@ import type { LovelaceViewRawConfig } from "./view";
export interface LovelaceDashboardBaseConfig {}
export interface LovelaceConfig extends LovelaceDashboardBaseConfig {
title?: string;
background?: string;
views: LovelaceViewRawConfig[];
}

View File

@ -33,22 +33,32 @@ export const getPanelNameTranslationKey = (panel: PanelInfo) => {
return `panel.${panel.title}` as const;
};
export const getPanelTitle = (hass: HomeAssistant): string | undefined => {
export const getPanelTitle = (
hass: HomeAssistant,
panel: PanelInfo
): string | undefined => {
const translationKey = getPanelNameTranslationKey(panel);
return hass.localize(translationKey) || panel.title || undefined;
};
export const getPanelTitleFromUrlPath = (
hass: HomeAssistant,
urlPath: string
): string | undefined => {
if (!hass.panels) {
return undefined;
}
const panel = Object.values(hass.panels).find(
(p: PanelInfo): boolean => p.url_path === hass.panelUrl
(p: PanelInfo): boolean => p.url_path === urlPath
);
if (!panel) {
return undefined;
}
const translationKey = getPanelNameTranslationKey(panel);
return hass.localize(translationKey) || panel.title || undefined;
return getPanelTitle(hass, panel);
};
export const getPanelIcon = (panel: PanelInfo): string | null => {

View File

@ -277,7 +277,7 @@ export class DialogLovelaceDashboardDetail extends LitElement {
title: this._data!.title,
};
await this._params!.updateDashboard(values);
} else {
} else if (this._params!.createDashboard) {
await this._params!.createDashboard(
this._data as LovelaceDashboardCreateParams
);

View File

@ -423,22 +423,20 @@ export class HaConfigLovelaceDashboards extends LitElement {
);
},
removeDashboard: async () => {
if (
!(await showConfirmationDialog(this, {
title: this.hass!.localize(
"ui.panel.config.lovelace.dashboards.confirm_delete_title",
{ dashboard_title: dashboard!.title }
),
text: this.hass!.localize(
"ui.panel.config.lovelace.dashboards.confirm_delete_text"
),
confirmText: this.hass!.localize("ui.common.delete"),
destructive: true,
}))
) {
const confirm = await showConfirmationDialog(this, {
title: this.hass!.localize(
"ui.panel.config.lovelace.dashboards.confirm_delete_title",
{ dashboard_title: dashboard!.title }
),
text: this.hass!.localize(
"ui.panel.config.lovelace.dashboards.confirm_delete_text"
),
confirmText: this.hass!.localize("ui.common.delete"),
destructive: true,
});
if (!confirm) {
return false;
}
try {
await deleteDashboard(this.hass!, dashboard!.id);
this._dashboards = this._dashboards!.filter(

View File

@ -8,7 +8,7 @@ import {
export interface LovelaceDashboardDetailsDialogParams {
dashboard?: LovelaceDashboard;
urlPath?: string;
createDashboard: (values: LovelaceDashboardCreateParams) => Promise<unknown>;
createDashboard?: (values: LovelaceDashboardCreateParams) => Promise<unknown>;
updateDashboard: (
updates: Partial<LovelaceDashboardMutableParams>
) => Promise<unknown>;

View File

@ -1,124 +0,0 @@
import "@material/mwc-button";
import { CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-circular-progress";
import "../../../../components/ha-dialog";
import { haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import type { Lovelace } from "../../types";
import "./hui-lovelace-editor";
import { LovelaceConfig } from "../../../../data/lovelace/config/types";
@customElement("hui-dialog-edit-lovelace")
export class HuiDialogEditLovelace extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@state() private _lovelace?: Lovelace;
@state() private _config?: LovelaceConfig;
private _saving = false;
public showDialog(lovelace: Lovelace): void {
this._lovelace = lovelace;
const { views, ...lovelaceConfig } = this._lovelace!.config;
this._config = lovelaceConfig as LovelaceConfig;
}
public closeDialog(): void {
this._config = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render() {
if (!this._config) {
return nothing;
}
return html`
<ha-dialog
open
.heading=${this.hass!.localize(
"ui.panel.lovelace.editor.edit_lovelace.header"
)}
>
<div>
${this.hass!.localize(
"ui.panel.lovelace.editor.edit_lovelace.explanation"
)}
<hui-lovelace-editor
.hass=${this.hass}
.config=${this._config}
@lovelace-config-changed=${this._ConfigChanged}
dialogInitialFocus
></hui-lovelace-editor>
</div>
<mwc-button @click=${this.closeDialog} slot="secondaryAction">
${this.hass!.localize("ui.common.cancel")}
</mwc-button>
<mwc-button
.disabled=${!this._config || this._saving}
@click=${this._save}
slot="primaryAction"
>
${this._saving
? html`<ha-circular-progress
indeterminate
size="small"
aria-label="Saving"
></ha-circular-progress>`
: ""}
${this.hass!.localize("ui.common.save")}</mwc-button
>
</ha-dialog>
`;
}
private async _save(): Promise<void> {
if (!this._config) {
return;
}
if (!this._isConfigChanged()) {
this.closeDialog();
return;
}
this._saving = true;
const lovelace = this._lovelace!;
const config: LovelaceConfig = {
...lovelace.config,
...this._config,
};
try {
await lovelace.saveConfig(config);
this.closeDialog();
} catch (err: any) {
alert(`Saving failed: ${err.message}`);
} finally {
this._saving = false;
}
}
private _ConfigChanged(ev: CustomEvent): void {
if (ev.detail && ev.detail.config) {
this._config = ev.detail.config;
}
}
private _isConfigChanged(): boolean {
const { views, ...lovelaceConfig } = this._lovelace!.config;
return JSON.stringify(this._config) !== JSON.stringify(lovelaceConfig);
}
static get styles(): CSSResultGroup {
return haStyleDialog;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-dialog-edit-lovelace": HuiDialogEditLovelace;
}
}

View File

@ -1,77 +0,0 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-textfield";
import type { LovelaceConfig } from "../../../../data/lovelace/config/types";
import { HomeAssistant } from "../../../../types";
import { EditorTarget } from "../types";
declare global {
interface HASSDomEvents {
"lovelace-config-changed": {
config: LovelaceConfig;
};
}
}
@customElement("hui-lovelace-editor")
export class HuiLovelaceEditor extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public config?: LovelaceConfig;
get _title(): string {
if (!this.config) {
return "";
}
return this.config.title || "";
}
protected render(): TemplateResult {
return html`
<ha-textfield
.label=${this.hass.localize(
"ui.panel.lovelace.editor.edit_lovelace.title"
)}
.value=${this._title}
.configValue=${"title"}
@change=${this._valueChanged}
></ha-textfield>
`;
}
private _valueChanged(ev: Event): void {
if (!this.config) {
return;
}
const target = ev.currentTarget! as EditorTarget;
if (this[`_${target.configValue}`] === target.value) {
return;
}
let newConfig;
if (target.configValue) {
newConfig = {
...this.config,
[target.configValue]: target.value,
};
}
fireEvent(this, "lovelace-config-changed", { config: newConfig });
}
static styles: CSSResultGroup = css`
ha-textfield {
display: block;
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"hui-lovelace-editor": HuiLovelaceEditor;
}
}

View File

@ -1,31 +0,0 @@
import { fireEvent } from "../../../../common/dom/fire_event";
import { Lovelace } from "../../types";
declare global {
// for fire event
interface HASSDomEvents {
"show-edit-lovelace": Lovelace;
}
}
let registeredDialog = false;
const dialogShowEvent = "show-edit-lovelace";
const dialogTag = "hui-dialog-edit-lovelace";
const registerEditLovelaceDialog = (element: HTMLElement): Event =>
fireEvent(element, "register-dialog", {
dialogShowEvent,
dialogTag,
dialogImport: () => import("./hui-dialog-edit-lovelace"),
});
export const showEditLovelaceDialog = (
element: HTMLElement,
lovelace: Lovelace
): void => {
if (!registeredDialog) {
registeredDialog = true;
registerEditLovelaceDialog(element);
}
fireEvent(element, dialogShowEvent, lovelace);
};

View File

@ -120,6 +120,7 @@ export class LovelacePanel extends LitElement {
if (panelState === "loaded") {
return html`
<hui-root
.panel=${this.panel}
.hass=${this.hass}
.lovelace=${this.lovelace}
.route=${this.route}

View File

@ -33,6 +33,7 @@ import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { fireEvent } from "../../common/dom/fire_event";
import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event";
import { navigate } from "../../common/navigate";
import { LocalizeKeys } from "../../common/translations/localize";
import { constructUrlCurrentPath } from "../../common/url/construct-url";
import {
addSearchParam,
@ -51,6 +52,16 @@ import "../../components/ha-menu-button";
import "../../components/ha-svg-icon";
import "../../components/ha-tabs";
import type { LovelacePanelConfig } from "../../data/lovelace";
import {
LovelaceConfig,
isStrategyDashboard,
} from "../../data/lovelace/config/types";
import { LovelaceViewConfig } from "../../data/lovelace/config/view";
import {
deleteDashboard,
fetchDashboards,
updateDashboard,
} from "../../data/lovelace/dashboard";
import {
showAlertDialog,
showConfirmationDialog,
@ -58,27 +69,24 @@ import {
import { showQuickBar } from "../../dialogs/quick-bar/show-dialog-quick-bar";
import { showVoiceCommandDialog } from "../../dialogs/voice-command-dialog/show-ha-voice-command-dialog";
import { haStyle } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
import type { HomeAssistant, PanelInfo } from "../../types";
import { documentationUrl } from "../../util/documentation-url";
import { showDashboardDetailDialog } from "../config/lovelace/dashboards/show-dialog-lovelace-dashboard-detail";
import { swapView } from "./editor/config-util";
import { showEditLovelaceDialog } from "./editor/lovelace-editor/show-edit-lovelace-dialog";
import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog";
import { showDashboardStrategyEditorDialog } from "./editor/dashboard-strategy-editor/dialogs/show-dialog-dashboard-strategy-editor";
import { showSaveDialog } from "./editor/show-save-config-dialog";
import { showEditViewDialog } from "./editor/view-editor/show-edit-view-dialog";
import { getLovelaceStrategy } from "./strategies/get-strategy";
import { isLegacyStrategyConfig } from "./strategies/legacy-strategy";
import type { Lovelace } from "./types";
import "./views/hui-view";
import type { HUIView } from "./views/hui-view";
import { LovelaceViewConfig } from "../../data/lovelace/config/view";
import {
LovelaceConfig,
isStrategyDashboard,
} from "../../data/lovelace/config/types";
import { showSaveDialog } from "./editor/show-save-config-dialog";
import { isLegacyStrategyConfig } from "./strategies/legacy-strategy";
import { LocalizeKeys } from "../../common/translations/localize";
import { getLovelaceStrategy } from "./strategies/get-strategy";
import { getPanelTitle } from "../../data/panel";
@customElement("hui-root")
class HUIRoot extends LitElement {
@property({ attribute: false }) public panel?: PanelInfo<LovelacePanelConfig>;
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public lovelace?: Lovelace;
@ -279,6 +287,10 @@ class HUIRoot extends LitElement {
const curViewConfig =
typeof this._curView === "number" ? views[this._curView] : undefined;
const dashboardTitle = this.panel
? getPanelTitle(this.hass, this.panel)
: undefined;
return html`
<div
class=${classMap({
@ -290,7 +302,7 @@ class HUIRoot extends LitElement {
${this._editMode
? html`
<div class="main-title">
${this.config.title ||
${dashboardTitle ||
this.hass!.localize("ui.panel.lovelace.editor.header")}
<ha-icon-button
slot="actionItems"
@ -299,7 +311,7 @@ class HUIRoot extends LitElement {
)}
.path=${mdiPencil}
class="edit-icon"
@click=${this._editLovelace}
@click=${this._editDashboard}
></ha-icon-button>
</div>
<div class="action-items">${this._renderActionItems()}</div>
@ -360,9 +372,11 @@ class HUIRoot extends LitElement {
)}
</ha-tabs>
`
: html`<div class="main-title">
${this.config.title}
</div>`}
: html`
<div class="main-title">
${views[0]?.title ?? dashboardTitle}
</div>
`}
<div class="action-items">${this._renderActionItems()}</div>
`}
</div>
@ -758,8 +772,41 @@ class HUIRoot extends LitElement {
this.lovelace!.setEditMode(false);
}
private _editLovelace() {
showEditLovelaceDialog(this, this.lovelace!);
private async _editDashboard() {
const urlPath = this.route?.prefix.slice(1);
await this.hass.loadFragmentTranslation("config");
const dashboards = await fetchDashboards(this.hass);
const dashboard = dashboards.find((d) => d.url_path === urlPath);
showDashboardDetailDialog(this, {
dashboard,
urlPath,
updateDashboard: async (values) => {
await updateDashboard(this.hass!, dashboard!.id, values);
},
removeDashboard: async () => {
const confirm = await showConfirmationDialog(this, {
title: this.hass!.localize(
"ui.panel.config.lovelace.dashboards.confirm_delete_title",
{ dashboard_title: dashboard!.title }
),
text: this.hass!.localize(
"ui.panel.config.lovelace.dashboards.confirm_delete_text"
),
confirmText: this.hass!.localize("ui.common.delete"),
destructive: true,
});
if (!confirm) {
return false;
}
try {
await deleteDashboard(this.hass!, dashboard!.id);
return true;
} catch (err: any) {
return false;
}
},
});
}
private _navigateToView(path: string | number, replace?: boolean) {

View File

@ -12,7 +12,6 @@ export class IframeDashboardStrategy extends ReactiveElement {
config: IframeDashboardStrategyConfig
): Promise<LovelaceConfig> {
return {
title: config.title,
views: [
{
strategy: config,

View File

@ -1,7 +1,6 @@
import { ReactiveElement } from "lit";
import { customElement } from "lit/decorators";
import { LovelaceConfig } from "../../../../data/lovelace/config/types";
import { HomeAssistant } from "../../../../types";
import { MapViewStrategyConfig } from "./map-view-strategy";
export type MapDashboardStrategyConfig = MapViewStrategyConfig;
@ -9,11 +8,9 @@ export type MapDashboardStrategyConfig = MapViewStrategyConfig;
@customElement("map-dashboard-strategy")
export class MapDashboardStrategy extends ReactiveElement {
static async generate(
config: MapDashboardStrategyConfig,
hass: HomeAssistant
config: MapDashboardStrategyConfig
): Promise<LovelaceConfig> {
return {
title: hass.localize("panel.map"),
views: [
{
strategy: config,

View File

@ -1,9 +1,8 @@
import { ReactiveElement } from "lit";
import { customElement } from "lit/decorators";
import { LovelaceConfig } from "../../../../data/lovelace/config/types";
import { HomeAssistant } from "../../../../types";
import { OriginalStatesViewStrategyConfig } from "./original-states-view-strategy";
import { LovelaceStrategyEditor } from "../types";
import { OriginalStatesViewStrategyConfig } from "./original-states-view-strategy";
export type OriginalStatesDashboardStrategyConfig =
OriginalStatesViewStrategyConfig;
@ -11,11 +10,9 @@ export type OriginalStatesDashboardStrategyConfig =
@customElement("original-states-dashboard-strategy")
export class OriginalStatesDashboardStrategy extends ReactiveElement {
static async generate(
config: OriginalStatesDashboardStrategyConfig,
hass: HomeAssistant
config: OriginalStatesDashboardStrategyConfig
): Promise<LovelaceConfig> {
return {
title: hass.config.location_name,
views: [
{
strategy: config,

View File

@ -1,4 +1,4 @@
import { getPanelTitle } from "../data/panel";
import { getPanelTitleFromUrlPath } from "../data/panel";
import { Constructor, HomeAssistant } from "../types";
import { HassBaseEl } from "./hass-base-mixin";
@ -24,7 +24,7 @@ export const panelTitleMixin = <T extends Constructor<HassBaseEl>>(
oldHass.panelUrl !== this.hass.panelUrl ||
oldHass.localize !== this.hass.localize
) {
setTitle(getPanelTitle(this.hass));
setTitle(getPanelTitleFromUrlPath(this.hass, this.hass.panelUrl));
}
}
};