ha-frontend-cdce8p/src/panels/lovelace/editor/view-editor/hui-dialog-edit-view.ts

453 lines
13 KiB
TypeScript

import "@material/mwc-button";
import "@polymer/paper-tabs/paper-tab";
import "@polymer/paper-tabs/paper-tabs";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
import { navigate } from "../../../../common/navigate";
import "../../../../components/ha-circular-progress";
import "../../../../components/ha-dialog";
import "../../../../components/ha-alert";
import type {
LovelaceBadgeConfig,
LovelaceCardConfig,
LovelaceViewConfig,
} from "../../../../data/lovelace";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../../dialogs/generic/show-dialog-box";
import { haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import "../../components/hui-entity-editor";
import { addView, deleteView, replaceView } from "../config-util";
import "../hui-badge-preview";
import { processEditorEntities } from "../process-editor-entities";
import {
EntitiesEditorEvent,
ViewEditEvent,
ViewVisibilityChangeEvent,
} from "../types";
import "./hui-view-editor";
import "./hui-view-visibility-editor";
import { EditViewDialogParams } from "./show-edit-view-dialog";
import {
DEFAULT_VIEW_LAYOUT,
PANEL_VIEW_LAYOUT,
VIEWS_NO_BADGE_SUPPORT,
} from "../../views/const";
import { deepEqual } from "../../../../common/util/deep-equal";
@customElement("hui-dialog-edit-view")
export class HuiDialogEditView extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@state() private _params?: EditViewDialogParams;
@state() private _config?: LovelaceViewConfig;
@state() private _badges?: LovelaceBadgeConfig[];
@state() private _cards?: LovelaceCardConfig[];
@state() private _saving = false;
@state() private _curTab?: string;
@state() private _dirty = false;
private _curTabIndex = 0;
get _type(): string {
if (!this._config) {
return DEFAULT_VIEW_LAYOUT;
}
return this._config.panel
? PANEL_VIEW_LAYOUT
: this._config.type || DEFAULT_VIEW_LAYOUT;
}
public showDialog(params: EditViewDialogParams): void {
this._params = params;
if (this._params.viewIndex === undefined) {
this._config = {};
this._badges = [];
this._cards = [];
this._dirty = false;
} else {
const { cards, badges, ...viewConfig } =
this._params.lovelace!.config.views[this._params.viewIndex];
this._config = viewConfig;
this._badges = badges ? processEditorEntities(badges) : [];
this._cards = cards;
}
}
public closeDialog(): void {
this._curTabIndex = 0;
this._params = undefined;
this._config = {};
this._badges = [];
this._dirty = false;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
private get _viewConfigTitle(): string {
if (!this._config || !this._config.title) {
return this.hass!.localize("ui.panel.lovelace.editor.edit_view.header");
}
return this.hass!.localize(
"ui.panel.lovelace.editor.edit_view.header_name",
"name",
this._config.title
);
}
protected render(): TemplateResult {
if (!this._params) {
return html``;
}
let content;
switch (this._curTab) {
case "tab-settings":
content = html`
<hui-view-editor
.isNew=${this._params.viewIndex === undefined}
.hass=${this.hass}
.config=${this._config}
@view-config-changed=${this._viewConfigChanged}
></hui-view-editor>
`;
break;
case "tab-badges":
content = html`
${this._badges?.length
? html`
${VIEWS_NO_BADGE_SUPPORT.includes(this._type)
? html`
<ha-alert alert-type="warning">
${this.hass!.localize(
"ui.panel.lovelace.editor.edit_badges.view_no_badges"
)}
</ha-alert>
`
: ""}
<div class="preview-badges">
${this._badges.map(
(badgeConfig) => html`
<hui-badge-preview
.hass=${this.hass}
.config=${badgeConfig}
></hui-badge-preview>
`
)}
</div>
`
: ""}
<hui-entity-editor
.hass=${this.hass}
.entities=${this._badges}
@entities-changed=${this._badgesChanged}
></hui-entity-editor>
`;
break;
case "tab-visibility":
content = html`
<hui-view-visibility-editor
.hass=${this.hass}
.config=${this._config}
@view-visibility-changed=${this._viewVisibilityChanged}
></hui-view-visibility-editor>
`;
break;
case "tab-cards":
content = html` Cards `;
break;
}
return html`
<ha-dialog
open
scrimClickAction
escapeKeyAction
@closed=${this.closeDialog}
.heading=${this._viewConfigTitle}
>
<div slot="heading">
<h2>${this._viewConfigTitle}</h2>
<paper-tabs
scrollable
hide-scroll-buttons
.selected=${this._curTabIndex}
@selected-item-changed=${this._handleTabSelected}
>
<paper-tab id="tab-settings"
>${this.hass!.localize(
"ui.panel.lovelace.editor.edit_view.tab_settings"
)}</paper-tab
>
<paper-tab id="tab-badges"
>${this.hass!.localize(
"ui.panel.lovelace.editor.edit_view.tab_badges"
)}</paper-tab
>
<paper-tab id="tab-visibility"
>${this.hass!.localize(
"ui.panel.lovelace.editor.edit_view.tab_visibility"
)}</paper-tab
>
</paper-tabs>
</div>
${content}
${this._params.viewIndex !== undefined
? html`
<mwc-button
class="warning"
slot="secondaryAction"
@click=${this._deleteConfirm}
>
${this.hass!.localize(
"ui.panel.lovelace.editor.edit_view.delete"
)}
</mwc-button>
`
: ""}
<mwc-button @click=${this.closeDialog} slot="primaryAction"
>${this.hass!.localize("ui.common.cancel")}</mwc-button
>
<mwc-button
slot="primaryAction"
?disabled=${!this._config || this._saving || !this._dirty}
@click=${this._save}
>
${this._saving
? html`<ha-circular-progress
active
size="small"
title="Saving"
></ha-circular-progress>`
: ""}
${this.hass!.localize("ui.common.save")}</mwc-button
>
</ha-dialog>
`;
}
private async _delete(): Promise<void> {
if (!this._params) {
return;
}
try {
await this._params.lovelace!.saveConfig(
deleteView(this._params.lovelace!.config, this._params.viewIndex!)
);
this.closeDialog();
navigate(`/${window.location.pathname.split("/")[1]}`);
} catch (err: any) {
showAlertDialog(this, {
text: `Deleting failed: ${err.message}`,
});
}
}
private _deleteConfirm(): void {
showConfirmationDialog(this, {
title: this.hass!.localize(
`ui.panel.lovelace.views.confirm_delete${
this._cards?.length ? `_existing_cards` : ""
}`
),
text: this.hass!.localize(
`ui.panel.lovelace.views.confirm_delete${
this._cards?.length ? "_existing_cards" : ""
}_text`,
"name",
this._config?.title || "Unnamed view",
"number",
this._cards?.length || 0
),
confirm: () => this._delete(),
});
}
private _handleTabSelected(ev: CustomEvent): void {
if (!ev.detail.value) {
return;
}
this._curTab = ev.detail.value.id;
}
private async _save(): Promise<void> {
if (!this._params || !this._config) {
return;
}
if (!this._isConfigChanged()) {
this.closeDialog();
return;
}
this._saving = true;
const viewConf: LovelaceViewConfig = {
...this._config,
badges: this._badges,
cards: this._cards,
};
const lovelace = this._params.lovelace!;
try {
await lovelace.saveConfig(
this._creatingView
? addView(lovelace.config, viewConf)
: replaceView(lovelace.config, this._params.viewIndex!, viewConf)
);
if (this._params.saveCallback) {
this._params.saveCallback(
this._params.viewIndex || lovelace.config.views.length,
viewConf
);
}
this.closeDialog();
} catch (err: any) {
showAlertDialog(this, {
text: `Saving failed: ${err.message}`,
});
} finally {
this._saving = false;
}
}
private _viewConfigChanged(ev: ViewEditEvent): void {
if (
ev.detail &&
ev.detail.config &&
!deepEqual(this._config, ev.detail.config)
) {
this._config = ev.detail.config;
this._dirty = true;
}
}
private _viewVisibilityChanged(
ev: HASSDomEvent<ViewVisibilityChangeEvent>
): void {
if (ev.detail.visible && this._config) {
this._config.visible = ev.detail.visible;
}
this._dirty = true;
}
private _badgesChanged(ev: EntitiesEditorEvent): void {
if (!this._badges || !this.hass || !ev.detail || !ev.detail.entities) {
return;
}
this._badges = processEditorEntities(ev.detail.entities);
this._dirty = true;
}
private _isConfigChanged(): boolean {
return (
this._creatingView ||
JSON.stringify(this._config) !==
JSON.stringify(
this._params!.lovelace!.config.views[this._params!.viewIndex!]
)
);
}
private get _creatingView(): boolean {
return this._params!.viewIndex === undefined;
}
static get styles(): CSSResultGroup {
return [
haStyleDialog,
css`
h2 {
display: block;
color: var(--primary-text-color);
line-height: normal;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
font-family: Roboto, sans-serif;
font-family: var(
--mdc-typography-headline6-font-family,
var(--mdc-typography-font-family, Roboto, sans-serif)
);
font-size: 1.25rem;
font-size: var(--mdc-typography-headline6-font-size, 1.25rem);
line-height: 2rem;
line-height: var(--mdc-typography-headline6-line-height, 2rem);
font-weight: 500;
font-weight: var(--mdc-typography-headline6-font-weight, 500);
letter-spacing: 0.0125em;
letter-spacing: var(
--mdc-typography-headline6-letter-spacing,
0.0125em
);
text-decoration: inherit;
text-decoration: var(
--mdc-typography-headline6-text-decoration,
inherit
);
text-transform: inherit;
text-transform: var(
--mdc-typography-headline6-text-transform,
inherit
);
position: relative;
flex-shrink: 0;
box-sizing: border-box;
margin: 0;
padding: 20px 24px 9px;
border-bottom: 1px solid transparent;
}
paper-tabs {
--paper-tabs-selection-bar-color: var(--primary-color);
color: var(--primary-text-color);
text-transform: uppercase;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
padding: 0 20px;
}
mwc-button.warning {
margin-right: auto;
}
ha-circular-progress {
display: none;
}
ha-circular-progress[active] {
display: block;
}
.hidden {
display: none;
}
.error {
color: var(--error-color);
border-bottom: 1px solid var(--error-color);
}
.preview-badges {
display: flex;
justify-content: center;
margin: 12px 16px;
flex-wrap: wrap;
}
@media all and (min-width: 600px) {
ha-dialog {
--mdc-dialog-min-width: 600px;
}
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-dialog-edit-view": HuiDialogEditView;
}
}