1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-02-15 07:25:54 +00:00

Migrate automation/script dialogs to ha-wa-dialog (#29030)

* Migrate dialog-automation-save to ha-wa-dialog

* Migrate dialog-automation-mode to ha-wa-dialog

* Migrate dialog-paste-replace to ha-wa-dialog

* Migrate dialog-new-automation to ha-wa-dialog

* Migrate ha-device-automation-dialog to ha-wa-dialog
This commit is contained in:
Aidan Timson
2026-01-16 11:23:56 +00:00
committed by GitHub
parent c9e1c9e0a3
commit af8659d8ed
5 changed files with 179 additions and 172 deletions

View File

@@ -1,16 +1,16 @@
import { mdiClose, mdiHelpCircle } from "@mdi/js";
import { mdiHelpCircle } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-dialog-header";
import "../../../../components/ha-wa-dialog";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-md-list-item";
import "../../../../components/ha-md-list";
import "../../../../components/ha-radio";
import "../../../../components/ha-button";
import "../../../../components/ha-textfield";
import "../../../../components/ha-dialog";
import {
AUTOMATION_DEFAULT_MAX,
@@ -27,16 +27,16 @@ import type { AutomationModeDialog } from "./show-dialog-automation-mode";
class DialogAutomationMode extends LitElement implements HassDialog {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _opened = false;
@state() private _open = false;
private _params!: AutomationModeDialog;
@state() private _params?: AutomationModeDialog;
@state() private _newMode: (typeof MODES)[number] = AUTOMATION_DEFAULT_MODE;
@state() private _newMax?: number;
public showDialog(params: AutomationModeDialog): void {
this._opened = true;
this._open = true;
this._params = params;
this._newMode = params.config.mode || AUTOMATION_DEFAULT_MODE;
this._newMax = isMaxMode(this._newMode)
@@ -44,18 +44,21 @@ class DialogAutomationMode extends LitElement implements HassDialog {
: undefined;
}
public closeDialog() {
this._params.onClose();
if (this._opened) {
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
this._opened = false;
public closeDialog(): boolean {
this._open = false;
return true;
}
private _dialogClosed() {
if (this._params?.onClose) {
this._params.onClose();
}
this._params = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected render() {
if (!this._opened) {
if (!this._params) {
return nothing;
}
@@ -64,34 +67,25 @@ class DialogAutomationMode extends LitElement implements HassDialog {
);
return html`
<ha-dialog
open
scrimClickAction
@closed=${this.closeDialog}
.heading=${title}
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
header-title=${title}
@closed=${this._dialogClosed}
>
<ha-dialog-header slot="heading">
<a
href=${documentationUrl(this.hass, "/docs/automation/modes/")}
slot="headerActionItems"
target="_blank"
rel="noopener noreferer"
>
<ha-icon-button
slot="navigationIcon"
dialogAction="cancel"
.label=${this.hass.localize("ui.common.close")}
.path=${mdiClose}
.label=${this.hass.localize(
"ui.panel.config.automation.editor.modes.learn_more"
)}
.path=${mdiHelpCircle}
></ha-icon-button>
<div slot="title">${title}</div>
<a
href=${documentationUrl(this.hass, "/docs/automation/modes/")}
slot="actionItems"
target="_blank"
rel="noopener noreferer"
>
<ha-icon-button
.label=${this.hass.localize(
"ui.panel.config.automation.editor.modes.learn_more"
)}
.path=${mdiHelpCircle}
></ha-icon-button>
</a>
</ha-dialog-header>
</a>
<ha-md-list
role="listbox"
tabindex="0"
@@ -157,17 +151,21 @@ class DialogAutomationMode extends LitElement implements HassDialog {
`
: nothing}
<ha-button
appearance="plain"
@click=${this.closeDialog}
slot="primaryAction"
>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button @click=${this._save} slot="primaryAction">
${this.hass.localize("ui.panel.config.automation.editor.change_mode")}
</ha-button>
</ha-dialog>
<ha-dialog-footer slot="footer">
<ha-button
slot="secondaryAction"
appearance="plain"
@click=${this.closeDialog}
>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button slot="primaryAction" @click=${this._save}>
${this.hass.localize(
"ui.panel.config.automation.editor.change_mode"
)}
</ha-button>
</ha-dialog-footer>
</ha-wa-dialog>
`;
}
@@ -190,6 +188,9 @@ class DialogAutomationMode extends LitElement implements HassDialog {
}
private _save(): void {
if (!this._params) {
return;
}
this._params.updateConfig({
...this._params.config,
mode: this._newMode,
@@ -206,15 +207,12 @@ class DialogAutomationMode extends LitElement implements HassDialog {
ha-textfield {
display: block;
}
ha-dialog {
ha-wa-dialog {
--dialog-content-padding: 0;
}
.options {
padding: 0 24px 24px 24px;
}
ha-dialog-header a {
color: inherit;
}
`,
];
}

View File

@@ -1,4 +1,4 @@
import { mdiClose, mdiPlus } from "@mdi/js";
import { mdiPlus } from "@mdi/js";
import { dump } from "js-yaml";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
@@ -8,6 +8,7 @@ import "../../../../components/chips/ha-assist-chip";
import "../../../../components/chips/ha-chip-set";
import "../../../../components/ha-alert";
import "../../../../components/ha-area-picker";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-domain-icon";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-icon-picker";
@@ -17,6 +18,7 @@ import type { SuggestWithAIGenerateTask } from "../../../../components/ha-sugges
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-textarea";
import "../../../../components/ha-textfield";
import "../../../../components/ha-wa-dialog";
import "../../category/ha-category-picker";
import { computeStateDomain } from "../../../../common/entity/compute_state_domain";
@@ -38,7 +40,7 @@ import type {
class DialogAutomationSave extends LitElement implements HassDialog {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _opened = false;
@state() private _open = false;
@state() private _error?: string;
@@ -46,7 +48,7 @@ class DialogAutomationSave extends LitElement implements HassDialog {
@state() private _entryUpdates!: EntityRegistryUpdate;
private _params!: SaveDialogParams;
@state() private _params?: SaveDialogParams;
@state() private _newName?: string;
@@ -55,7 +57,7 @@ class DialogAutomationSave extends LitElement implements HassDialog {
private _newDescription?: string;
public showDialog(params: SaveDialogParams): void {
this._opened = true;
this._open = true;
this._params = params;
this._newIcon = "icon" in params.config ? params.config.icon : undefined;
this._newName =
@@ -79,17 +81,18 @@ class DialogAutomationSave extends LitElement implements HassDialog {
].filter(Boolean);
}
public closeDialog() {
this._params.onClose();
if (this._opened) {
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
this._opened = false;
this._visibleOptionals = [];
public closeDialog(): boolean {
this._open = false;
return true;
}
private _dialogClosed() {
this._params?.onClose();
this._visibleOptionals = [];
this._params = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected _renderOptionalChip(id: string, label: string) {
if (this._visibleOptionals.includes(id)) {
return nothing;
@@ -103,15 +106,15 @@ class DialogAutomationSave extends LitElement implements HassDialog {
}
protected _renderDiscard() {
if (!this._params.onDiscard) {
if (!this._params?.onDiscard) {
return nothing;
}
return html`
<ha-button
@click=${this._handleDiscard}
slot="secondaryAction"
variant="danger"
appearance="plain"
variant="danger"
@click=${this._handleDiscard}
>
${this.hass.localize("ui.common.dont_save")}
</ha-button>
@@ -119,13 +122,13 @@ class DialogAutomationSave extends LitElement implements HassDialog {
}
protected _renderInputs() {
if (this._params.hideInputs) {
if (!this._params || this._params.hideInputs) {
return nothing;
}
return html`
<ha-textfield
dialogInitialFocus
autofocus
.value=${this._newName}
.placeholder=${this.hass.localize(
`ui.panel.config.${this._params.domain}.editor.default_name`
@@ -239,7 +242,7 @@ class DialogAutomationSave extends LitElement implements HassDialog {
}
protected render() {
if (!this._opened) {
if (!this._params) {
return nothing;
}
@@ -250,29 +253,20 @@ class DialogAutomationSave extends LitElement implements HassDialog {
);
return html`
<ha-dialog
open
scrimClickAction
@closed=${this.closeDialog}
.heading=${title}
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
@closed=${this._dialogClosed}
header-title=${this._params.title || title}
>
<ha-dialog-header slot="heading">
<ha-icon-button
slot="navigationIcon"
dialogAction="cancel"
.label=${this.hass.localize("ui.common.close")}
.path=${mdiClose}
></ha-icon-button>
<span slot="title">${this._params.title || title}</span>
${this._params.hideInputs
? nothing
: html` <ha-suggest-with-ai-button
slot="actionItems"
.hass=${this.hass}
.generateTask=${this._generateTask}
@suggestion=${this._handleSuggestion}
></ha-suggest-with-ai-button>`}
</ha-dialog-header>
${this._params.hideInputs
? nothing
: html` <ha-suggest-with-ai-button
slot="headerActionItems"
.hass=${this.hass}
.generateTask=${this._generateTask}
@suggestion=${this._handleSuggestion}
></ha-suggest-with-ai-button>`}
${this._error
? html`<ha-alert alert-type="error"
>${this.hass.localize(
@@ -283,21 +277,25 @@ class DialogAutomationSave extends LitElement implements HassDialog {
${this._params.description
? html`<p>${this._params.description}</p>`
: nothing}
${this._renderInputs()} ${this._renderDiscard()}
<div slot="primaryAction">
<ha-button appearance="plain" @click=${this.closeDialog}>
${this._renderInputs()}
<ha-dialog-footer slot="footer">
${this._renderDiscard()}
<ha-button
slot="secondaryAction"
appearance="plain"
@click=${this.closeDialog}
>
${this.hass.localize("ui.common.cancel")}
</ha-button>
<ha-button @click=${this._save}>
<ha-button slot="primaryAction" @click=${this._save}>
${this.hass.localize(
this._params.config.alias && !this._params.onDiscard
? "ui.panel.config.automation.editor.rename"
: "ui.common.save"
)}
</ha-button>
</div>
</ha-dialog>
</ha-dialog-footer>
</ha-wa-dialog>
`;
}
@@ -331,7 +329,7 @@ class DialogAutomationSave extends LitElement implements HassDialog {
}
private _handleDiscard() {
this._params.onDiscard?.();
this._params?.onDiscard?.();
this.closeDialog();
}
@@ -350,6 +348,10 @@ class DialogAutomationSave extends LitElement implements HassDialog {
}
private _generateTask = async (): Promise<SuggestWithAIGenerateTask> => {
if (!this._params) {
throw new Error("Dialog params not set");
}
const [labels, entities, categories] = await this._getSuggestData();
const inspirations: string[] = [];
@@ -514,6 +516,10 @@ ${dump(this._params.config)}
}
private async _save(): Promise<void> {
if (!this._params) {
return;
}
if (!this._newName) {
this._error = "Name is required";
return;
@@ -548,17 +554,10 @@ ${dump(this._params.config)}
haStyle,
haStyleDialog,
css`
ha-dialog {
ha-wa-dialog {
--dialog-content-padding: 0 24px 24px 24px;
}
@media all and (min-width: 500px) {
ha-dialog {
--mdc-dialog-min-width: min(500px, 95vw);
--mdc-dialog-max-width: min(500px, 95vw);
}
}
ha-textfield,
ha-textarea,
ha-icon-picker,

View File

@@ -12,8 +12,8 @@ import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
import { stringCompare } from "../../../common/string/compare";
import { createCloseHeading } from "../../../components/ha-dialog";
import "../../../components/ha-icon-next";
import "../../../components/ha-wa-dialog";
import "../../../components/ha-list";
import "../../../components/ha-list-item";
import "../../../components/ha-tip";
@@ -29,7 +29,6 @@ import {
getBlueprintSourceType,
} from "../../../data/blueprint";
import { showScriptEditor } from "../../../data/script";
import type { HassDialog } from "../../../dialogs/make-dialog-manager";
import { mdiHomeAssistant } from "../../../resources/home-assistant-logo-svg";
import { haStyle, haStyleDialog } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
@@ -43,17 +42,20 @@ const SOURCE_TYPE_ICONS: Record<BlueprintSourceType, string> = {
};
@customElement("ha-dialog-new-automation")
class DialogNewAutomation extends LitElement implements HassDialog {
class DialogNewAutomation extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _opened = false;
@state() private _open = false;
@state() private _params?: NewAutomationDialogParams;
@state() private _mode: BlueprintDomain = "automation";
@state() public blueprints?: Blueprints;
public showDialog(params: NewAutomationDialogParams): void {
this._opened = true;
this._params = params;
this._open = true;
this._mode = params?.mode || "automation";
fetchBlueprints(this.hass!, this._mode).then((blueprints) => {
@@ -61,12 +63,14 @@ class DialogNewAutomation extends LitElement implements HassDialog {
});
}
public closeDialog() {
if (this._opened) {
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
this._opened = false;
return true;
public closeDialog(): void {
this._open = false;
}
private _dialogClosed(): void {
this._params = undefined;
this.blueprints = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
private _processedBlueprints = memoizeOne((blueprints?: Blueprints) => {
@@ -90,21 +94,20 @@ class DialogNewAutomation extends LitElement implements HassDialog {
});
protected render() {
if (!this._opened) {
if (!this._params) {
return nothing;
}
const processedBlueprints = this._processedBlueprints(this.blueprints);
return html`
<ha-dialog
open
hideActions
@closed=${this.closeDialog}
.heading=${createCloseHeading(
this.hass,
this.hass.localize(`ui.panel.config.${this._mode}.dialog_new.header`)
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
header-title=${this.hass.localize(
`ui.panel.config.${this._mode}.dialog_new.header`
)}
@closed=${this._dialogClosed}
>
<ha-list
innerRole="listbox"
@@ -113,7 +116,7 @@ class DialogNewAutomation extends LitElement implements HassDialog {
`ui.panel.config.${this._mode}.dialog_new.header`
)}
rootTabbable
dialogInitialFocus
autofocus
>
<ha-list-item
hasmeta
@@ -197,7 +200,7 @@ class DialogNewAutomation extends LitElement implements HassDialog {
</ha-tip>
`}
</ha-list>
</ha-dialog>
</ha-wa-dialog>
`;
}
@@ -229,13 +232,13 @@ class DialogNewAutomation extends LitElement implements HassDialog {
haStyle,
haStyleDialog,
css`
ha-dialog {
ha-wa-dialog {
--dialog-content-padding: 0;
--mdc-dialog-max-height: 60vh;
--mdc-dialog-max-height: 60dvh;
}
@media all and (min-width: 550px) {
ha-dialog {
ha-wa-dialog {
--mdc-dialog-min-width: 500px;
}
}

View File

@@ -2,48 +2,48 @@ import { css, type CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-button";
import { createCloseHeading } from "../../../../components/ha-dialog";
import "../../../../components/ha-dialog-footer";
import "../../../../components/ha-wa-dialog";
import "../../../../components/ha-yaml-editor";
import type { HassDialog } from "../../../../dialogs/make-dialog-manager";
import { haStyle, haStyleDialog } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import type { PasteReplaceDialogParams } from "./show-dialog-paste-replace";
@customElement("ha-dialog-paste-replace")
class DialogPasteReplace extends LitElement implements HassDialog {
class DialogPasteReplace extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _opened = false;
@state() private _open = false;
@state() private _params!: PasteReplaceDialogParams;
public showDialog(params: PasteReplaceDialogParams): void {
this._opened = true;
this._open = true;
this._params = params;
}
public closeDialog() {
if (this._opened) {
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
this._opened = false;
return true;
public closeDialog(): void {
this._open = false;
}
private _dialogClosed() {
this._params = undefined!;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
public render() {
if (!this._opened) {
if (!this._params) {
return nothing;
}
return html`
<ha-dialog
open
@closed=${this.closeDialog}
.heading=${createCloseHeading(
this.hass,
this.hass.localize(
`ui.panel.config.${this._params.domain}.editor.paste_confirm.title`
)
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
@closed=${this._dialogClosed}
header-title=${this.hass.localize(
`ui.panel.config.${this._params.domain}.editor.paste_confirm.title`
)}
>
<p>
@@ -58,15 +58,19 @@ class DialogPasteReplace extends LitElement implements HassDialog {
read-only
></ha-yaml-editor>
<div slot="primaryAction">
<ha-button appearance="plain" @click=${this._handleAppend}>
<ha-dialog-footer slot="footer">
<ha-button
slot="secondaryAction"
appearance="plain"
@click=${this._handleAppend}
>
${this.hass.localize("ui.common.append")}
</ha-button>
<ha-button @click=${this._handleReplace}>
<ha-button slot="primaryAction" @click=${this._handleReplace}>
${this.hass.localize("ui.common.replace")}
</ha-button>
</div>
</ha-dialog>
</ha-dialog-footer>
</ha-wa-dialog>
`;
}
@@ -90,10 +94,6 @@ class DialogPasteReplace extends LitElement implements HassDialog {
font-size: inherit;
font-weight: inherit;
}
div[slot="primaryAction"] {
display: flex;
gap: var(--ha-space-2);
}
`,
];
}

View File

@@ -9,10 +9,10 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import { shouldHandleRequestSelectedEvent } from "../../../../common/mwc/handle-request-selected-event";
import { createCloseHeading } from "../../../../components/ha-dialog";
import "../../../../components/ha-icon-next";
import "../../../../components/ha-list";
import "../../../../components/ha-list-item";
import "../../../../components/ha-wa-dialog";
import type { AutomationConfig } from "../../../../data/automation";
import { showAutomationEditor } from "../../../../data/automation";
import type {
@@ -44,12 +44,19 @@ export class DialogDeviceAutomation extends LitElement {
@state() private _params?: DeviceAutomationDialogParams;
@state() private _open = false;
public async showDialog(params: DeviceAutomationDialogParams): Promise<void> {
this._params = params;
this._open = true;
await this.updateComplete;
}
public closeDialog(): void {
this._open = false;
}
private _dialogClosed(): void {
this._params = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
@@ -136,18 +143,18 @@ export class DialogDeviceAutomation extends LitElement {
});
return html`
<ha-dialog
open
hideActions
@closed=${this.closeDialog}
.heading=${createCloseHeading(this.hass, title)}
<ha-wa-dialog
.hass=${this.hass}
.open=${this._open}
header-title=${title}
@closed=${this._dialogClosed}
>
<ha-list
innerRole="listbox"
itemRoles="option"
innerAriaLabel="Create new automation"
rootTabbable
dialogInitialFocus
autofocus
>
${this._triggers.length
? html`
@@ -245,7 +252,7 @@ export class DialogDeviceAutomation extends LitElement {
<ha-icon-next slot="meta"></ha-icon-next>
</ha-list-item>
</ha-list>
</ha-dialog>
</ha-wa-dialog>
`;
}
@@ -254,12 +261,12 @@ export class DialogDeviceAutomation extends LitElement {
haStyle,
haStyleDialog,
css`
ha-dialog {
ha-wa-dialog {
--dialog-content-padding: 0;
--mdc-dialog-max-height: 60vh;
}
@media all and (min-width: 550px) {
ha-dialog {
ha-wa-dialog {
--mdc-dialog-min-width: 500px;
}
}