164 lines
4.8 KiB
TypeScript
164 lines
4.8 KiB
TypeScript
/** Reusable confirmation dialog wrapping <ha-dialog>. */
|
|
|
|
import { LitElement, html, css, nothing } from "lit";
|
|
import { property, state } from "lit/decorators.js";
|
|
import type { HomeAssistant } from "../types";
|
|
import { t, nativeFieldStyles } from "../styles";
|
|
|
|
export interface ConfirmOptions {
|
|
title: string;
|
|
message: string;
|
|
confirmText?: string;
|
|
danger?: boolean;
|
|
}
|
|
|
|
export interface PromptOptions extends ConfirmOptions {
|
|
inputLabel?: string;
|
|
inputType?: string; // "text" | "date" etc.
|
|
inputValue?: string;
|
|
}
|
|
|
|
export interface PromptResult {
|
|
confirmed: boolean;
|
|
value: string;
|
|
}
|
|
|
|
export class MaintenanceConfirmDialog extends LitElement {
|
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
|
|
@state() private _open = false;
|
|
@state() private _title = "";
|
|
@state() private _message = "";
|
|
@state() private _confirmText = "";
|
|
@state() private _danger = false;
|
|
@state() private _inputLabel = "";
|
|
@state() private _inputType = "";
|
|
@state() private _inputValue = "";
|
|
|
|
private _resolve: ((value: boolean) => void) | null = null;
|
|
private _promptResolve: ((value: PromptResult) => void) | null = null;
|
|
|
|
public confirm(opts: ConfirmOptions): Promise<boolean> {
|
|
this._title = opts.title;
|
|
this._message = opts.message;
|
|
this._confirmText = opts.confirmText || "OK";
|
|
this._danger = opts.danger || false;
|
|
this._inputLabel = "";
|
|
this._inputType = "";
|
|
this._inputValue = "";
|
|
this._open = true;
|
|
return new Promise<boolean>((resolve) => {
|
|
this._resolve = resolve;
|
|
this._promptResolve = null;
|
|
});
|
|
}
|
|
|
|
public prompt(opts: PromptOptions): Promise<PromptResult> {
|
|
this._title = opts.title;
|
|
this._message = opts.message;
|
|
this._confirmText = opts.confirmText || "OK";
|
|
this._danger = opts.danger || false;
|
|
this._inputLabel = opts.inputLabel || "";
|
|
this._inputType = opts.inputType || "text";
|
|
this._inputValue = opts.inputValue || "";
|
|
this._open = true;
|
|
return new Promise<PromptResult>((resolve) => {
|
|
this._promptResolve = resolve;
|
|
this._resolve = null;
|
|
});
|
|
}
|
|
|
|
private _cancel(): void {
|
|
this._open = false;
|
|
if (this._promptResolve) {
|
|
this._promptResolve({ confirmed: false, value: "" });
|
|
this._promptResolve = null;
|
|
}
|
|
this._resolve?.(false);
|
|
this._resolve = null;
|
|
}
|
|
|
|
private _confirmAction(): void {
|
|
this._open = false;
|
|
if (this._promptResolve) {
|
|
this._promptResolve({ confirmed: true, value: this._inputValue });
|
|
this._promptResolve = null;
|
|
}
|
|
this._resolve?.(true);
|
|
this._resolve = null;
|
|
}
|
|
|
|
render() {
|
|
if (!this._open) return nothing;
|
|
const lang = this.hass?.language || "en";
|
|
return html`
|
|
<ha-dialog open @closed=${this._cancel}>
|
|
<div class="dialog-title">${this._title}</div>
|
|
<div class="content">
|
|
${this._message}
|
|
${this._inputLabel ? html`
|
|
<!-- Native <input> rather than <ha-textfield>: HA loads
|
|
ha-textfield lazily for its own panels, so inside this custom
|
|
panel it can be unregistered and render with zero height —
|
|
the prompt then shows no field at all (caught live testing
|
|
the pause/replace prompts; same fix as complete-dialog). -->
|
|
<label class="field">
|
|
<span class="field-label">${this._inputLabel}</span>
|
|
<input class="field-input"
|
|
type="${this._inputType || "text"}"
|
|
.value=${this._inputValue}
|
|
@input=${(e: Event) => (this._inputValue = (e.target as HTMLInputElement).value)} />
|
|
</label>
|
|
` : nothing}
|
|
</div>
|
|
<div class="dialog-actions">
|
|
<ha-button appearance="plain" @click=${this._cancel}>
|
|
${t("cancel", lang)}
|
|
</ha-button>
|
|
<ha-button
|
|
class="${this._danger ? "danger" : ""}"
|
|
@click=${this._confirmAction}
|
|
>
|
|
${this._confirmText}
|
|
</ha-button>
|
|
</div>
|
|
</ha-dialog>
|
|
`;
|
|
}
|
|
|
|
static styles = [nativeFieldStyles, css`
|
|
.dialog-title {
|
|
font-size: 18px;
|
|
font-weight: 500;
|
|
padding-bottom: 12px;
|
|
}
|
|
/* shared native-field scaffold from nativeFieldStyles; the prompt input
|
|
follows the message text, hence the extra top margin here */
|
|
.field { margin-top: 12px; }
|
|
.content {
|
|
padding: 8px 0;
|
|
min-width: 280px;
|
|
line-height: 1.5;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
.dialog-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
padding-top: 16px;
|
|
}
|
|
ha-textfield {
|
|
display: block;
|
|
}
|
|
ha-button.danger {
|
|
--mdc-theme-primary: var(--error-color, #f44336);
|
|
}
|
|
`];
|
|
}
|
|
|
|
if (!customElements.get("maintenance-confirm-dialog")) {
|
|
customElements.define("maintenance-confirm-dialog", MaintenanceConfirmDialog);
|
|
}
|