From 7ce88da20eb9375737e307734d8f84bb73599e31 Mon Sep 17 00:00:00 2001 From: Wendelin Date: Mon, 28 Jul 2025 17:35:06 +0200 Subject: [PATCH] Fix ha-progress-button --- .../components/ha-progress-button.markdown | 32 ++++ .../pages/components/ha-progress-button.ts | 139 ++++++++++++++++++ hassio/src/system/hassio-core-info.ts | 9 +- hassio/src/system/hassio-host-info.ts | 8 +- src/components/buttons/ha-progress-button.ts | 68 ++++----- src/components/ha-button.ts | 9 +- src/dialogs/tts-try/dialog-tts-try.ts | 6 +- src/panels/config/cloud/login/cloud-login.ts | 1 - .../config/hardware/ha-config-hardware.ts | 1 - 9 files changed, 211 insertions(+), 62 deletions(-) create mode 100644 gallery/src/pages/components/ha-progress-button.markdown create mode 100644 gallery/src/pages/components/ha-progress-button.ts diff --git a/gallery/src/pages/components/ha-progress-button.markdown b/gallery/src/pages/components/ha-progress-button.markdown new file mode 100644 index 0000000000..d0966ff8cc --- /dev/null +++ b/gallery/src/pages/components/ha-progress-button.markdown @@ -0,0 +1,32 @@ +--- +title: Progress Button +--- + + + +# Progress Button `` + +### API + +This component is a wrapper around `` that adds support for showing progress + +**Slots** + +- default slot: Label of the button + ` - no default + +**Properties/Attributes** + +| Name | Type | Default | Description | +| ---------- | ---------------------------------------------- | --------- | -------------------------------------------------- | +| label | string | "accent" | Sets the button label. | +| disabled | Boolean | false | Disables the button if true. | +| progress | Boolean | false | Shows a progress indicator on the button. | +| appearance | "accent"/"filled"/"plain" | "accent" | Sets the button appearance. | +| variants | "brand"/"danger"/"neutral"/"warning"/"success" | "brand" | Sets the button color variant. "brand" is default. | +| iconPath | string | undefined | Sets the icon path for the button. | diff --git a/gallery/src/pages/components/ha-progress-button.ts b/gallery/src/pages/components/ha-progress-button.ts new file mode 100644 index 0000000000..4e4b92379c --- /dev/null +++ b/gallery/src/pages/components/ha-progress-button.ts @@ -0,0 +1,139 @@ +import type { TemplateResult } from "lit"; +import { css, html, LitElement } from "lit"; +import { customElement } from "lit/decorators"; +import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element"; +import "../../../../src/components/buttons/ha-progress-button"; +import "../../../../src/components/ha-card"; +import "../../../../src/components/ha-svg-icon"; +import { mdiHomeAssistant } from "../../../../src/resources/home-assistant-logo-svg"; + +@customElement("demo-components-ha-progress-button") +export class DemoHaProgressButton extends LitElement { + protected render(): TemplateResult { + return html` + ${["light", "dark"].map( + (mode) => html` +
+ +
+ + Success + + + Fail + + + small + + + filled + + + plain + + + warning + + + With Icon + + + progress + + + disabled + +
+
+
+ ` + )} + `; + } + + firstUpdated(changedProps) { + super.firstUpdated(changedProps); + applyThemesOnElement( + this.shadowRoot!.querySelector(".dark"), + { + default_theme: "default", + default_dark_theme: "default", + themes: {}, + darkMode: true, + theme: "default", + }, + undefined, + undefined, + true + ); + } + + private async _clickedSuccess(ev: CustomEvent): Promise { + console.log("Clicked success"); + const button = ev.currentTarget as any; + button.progress = true; + + setTimeout(() => { + button.actionSuccess(); + button.progress = false; + }, 1000); + } + + private async _clickedFail(ev: CustomEvent): Promise { + const button = ev.currentTarget as any; + button.progress = true; + + setTimeout(() => { + button.actionError(); + button.progress = false; + }, 1000); + } + + static styles = css` + :host { + display: flex; + justify-content: center; + } + .dark, + .light { + display: block; + background-color: var(--primary-background-color); + padding: 0 50px; + } + .button { + padding: unset; + } + ha-card { + margin: 24px auto; + } + .card-content { + display: flex; + flex-direction: column; + gap: 24px; + } + .card-content div { + display: flex; + gap: 8px; + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "demo-components-ha-progress-button": DemoHaProgressButton; + } +} diff --git a/hassio/src/system/hassio-core-info.ts b/hassio/src/system/hassio-core-info.ts index 7f94858df3..7206c13859 100644 --- a/hassio/src/system/hassio-core-info.ts +++ b/hassio/src/system/hassio-core-info.ts @@ -3,8 +3,8 @@ import { css, html, LitElement } from "lit"; import { customElement, property, state } from "lit/decorators"; import { atLeastVersion } from "../../../src/common/config/version"; import "../../../src/components/buttons/ha-progress-button"; -import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-button"; +import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-card"; import "../../../src/components/ha-settings-row"; import type { HassioStats } from "../../../src/data/hassio/common"; @@ -94,7 +94,7 @@ class HassioCoreInfo extends LitElement {
${this.supervisor.host.features.includes("reboot") ? html` - + ${this.supervisor.localize("system.host.reboot_host")} ` @@ -179,7 +179,7 @@ class HassioHostInfo extends LitElement { ${this.supervisor.host.features.includes("shutdown") ? html` ${this.supervisor.localize("system.host.shutdown_host")} @@ -435,10 +435,6 @@ class HassioHostInfo extends LitElement { color: var(--secondary-text-color); } - .warning { - --mdc-theme-primary: var(--error-color); - } - ha-button-menu { color: var(--secondary-text-color); --mdc-menu-min-width: 200px; diff --git a/src/components/buttons/ha-progress-button.ts b/src/components/buttons/ha-progress-button.ts index be778521f7..19d9240f74 100644 --- a/src/components/buttons/ha-progress-button.ts +++ b/src/components/buttons/ha-progress-button.ts @@ -2,16 +2,11 @@ import { mdiAlertOctagram, mdiCheckBold } from "@mdi/js"; import type { TemplateResult } from "lit"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { classMap } from "lit/directives/class-map"; import "../ha-button"; +import type { Appearance } from "../ha-button"; import "../ha-spinner"; import "../ha-svg-icon"; -import type { Appearance } from "../ha-button"; - -const HIGHLIGHT_APPEARANCE = { - accent: "accent" as Appearance, - filled: "accent" as Appearance, - plain: "filled" as Appearance, -}; @customElement("ha-progress-button") export class HaProgressButton extends LitElement { @@ -23,18 +18,16 @@ export class HaProgressButton extends LitElement { @property() appearance: Appearance = "accent"; + @property({ attribute: false }) public iconPath?: string; + @property() variant: "brand" | "danger" | "neutral" | "warning" | "success" = "brand"; @state() private _result?: "success" | "error"; - @state() private _hasInitialIcon = false; - public render(): TemplateResult { const appearance = - this.progress || this._result - ? HIGHLIGHT_APPEARANCE[this.appearance] - : this.appearance; + this.progress || this._result ? "accent" : this.appearance; return html` - ${this._hasInitialIcon - ? html`` + ${this.iconPath + ? html`` : nothing} - ${this._result - ? html`` - : this.label} + + ${this.label} ${!this._result ? nothing @@ -75,14 +68,6 @@ export class HaProgressButton extends LitElement { `; } - firstUpdated() { - const iconSlot = this.shadowRoot!.querySelector( - 'slot[name="icon"]' - ) as HTMLSlotElement; - this._hasInitialIcon = - iconSlot && iconSlot.assignedNodes({ flatten: true }).length > 0; - } - public actionSuccess(): void { this._setResult("success"); } @@ -109,10 +94,6 @@ export class HaProgressButton extends LitElement { pointer-events: none; } - ha-button { - transition: all 1s; - } - .progress { bottom: 0; display: flex; @@ -123,14 +104,21 @@ export class HaProgressButton extends LitElement { width: 100%; } - ha-svg-icon { - color: white; + ha-button { + width: 100%; } - ha-button.success slot, - ha-button.error slot { + ha-button.result::part(start), + ha-button.result::part(end), + ha-button.result::part(label), + ha-button.result::part(caret), + ha-button.result::part(spinner) { visibility: hidden; } + + ha-svg-icon { + color: var(--white); + } `; } diff --git a/src/components/ha-button.ts b/src/components/ha-button.ts index cd72bc8f4f..d696de4dd2 100644 --- a/src/components/ha-button.ts +++ b/src/components/ha-button.ts @@ -1,6 +1,6 @@ import Button from "@awesome.me/webawesome/dist/components/button/button"; import { css, type CSSResultGroup } from "lit"; -import { customElement, property } from "lit/decorators"; +import { customElement } from "lit/decorators"; export type Appearance = "accent" | "filled" | "outlined" | "plain"; @@ -36,9 +36,6 @@ export type Appearance = "accent" | "filled" | "outlined" | "plain"; export class HaButton extends Button { variant: "brand" | "neutral" | "success" | "warning" | "danger" = "brand"; - @property({ type: Boolean, attribute: "hide-content", reflect: true }) - hideContent = false; - static get styles(): CSSResultGroup { return [ Button.styles, @@ -150,6 +147,10 @@ export class HaButton extends Button { background-color: var(--color-fill-disabled-loud-resting); color: var(--color-on-disabled-loud); } + + :host([loading]) { + pointer-events: none; + } `, ]; } diff --git a/src/dialogs/tts-try/dialog-tts-try.ts b/src/dialogs/tts-try/dialog-tts-try.ts index 6fc474b616..fc7713099c 100644 --- a/src/dialogs/tts-try/dialog-tts-try.ts +++ b/src/dialogs/tts-try/dialog-tts-try.ts @@ -3,6 +3,7 @@ import { css, html, LitElement, nothing } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import { storage } from "../../common/decorators/storage"; import { fireEvent } from "../../common/dom/fire_event"; +import "../../components/buttons/ha-progress-button"; import { createCloseHeading } from "../../components/ha-dialog"; import "../../components/ha-textarea"; import type { HaTextArea } from "../../components/ha-textarea"; @@ -10,7 +11,6 @@ import { convertTextToSpeech } from "../../data/tts"; import type { HomeAssistant } from "../../types"; import { showAlertDialog } from "../generic/show-dialog-box"; import type { TTSTryDialogParams } from "./show-dialog-tts-try"; -import "../../components/buttons/ha-progress-button"; @customElement("dialog-tts-try") export class TTSTryDialog extends LitElement { @@ -85,11 +85,11 @@ export class TTSTryDialog extends LitElement { .progress=${this._loadingExample} ?dialogInitialFocus=${Boolean(this._defaultMessage)} slot="primaryAction" - .label=${this.hass.localize("ui.dialogs.tts-try.play")} @click=${this._playExample} .disabled=${!this._valid} + .iconPath=${mdiPlayCircleOutline} > - + ${this.hass.localize("ui.dialogs.tts-try.play")} `; diff --git a/src/panels/config/cloud/login/cloud-login.ts b/src/panels/config/cloud/login/cloud-login.ts index 1ebc80981f..89fd4812f7 100644 --- a/src/panels/config/cloud/login/cloud-login.ts +++ b/src/panels/config/cloud/login/cloud-login.ts @@ -114,7 +114,6 @@ export class CloudLogin extends LitElement { )} ${this.localize( diff --git a/src/panels/config/hardware/ha-config-hardware.ts b/src/panels/config/hardware/ha-config-hardware.ts index df14848dfc..5d92b28d42 100644 --- a/src/panels/config/hardware/ha-config-hardware.ts +++ b/src/panels/config/hardware/ha-config-hardware.ts @@ -8,7 +8,6 @@ import memoizeOne from "memoize-one"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { round } from "../../../common/number/round"; import { blankBeforePercent } from "../../../common/translations/blank_before_percent"; -import "../../../components/buttons/ha-progress-button"; import "../../../components/chart/ha-chart-base"; import "../../../components/ha-alert"; import "../../../components/ha-button";