From bc3933d7d7f03a8d04b21196f02d076cf30c62dd Mon Sep 17 00:00:00 2001 From: Wendelin Date: Thu, 22 May 2025 11:54:41 +0200 Subject: [PATCH] Finish new button api --- src/components/ha-button.ts | 198 ++++++++---------- .../integrations/ha-config-flow-card.ts | 31 +-- .../integrations/ha-integration-card.ts | 9 +- src/resources/theme/color.globals.ts | 2 + 4 files changed, 113 insertions(+), 127 deletions(-) diff --git a/src/components/ha-button.ts b/src/components/ha-button.ts index bfa20024ef..122df2f718 100644 --- a/src/components/ha-button.ts +++ b/src/components/ha-button.ts @@ -3,141 +3,123 @@ import styles from "@shoelace-style/shoelace/dist/components/button/button.style import { css } from "lit"; import { customElement, property } from "lit/decorators"; +/** + * Home Assistant button component + * + * @element ha-button + * @extends {Button} + * + * @summary + * A stylable button component supporting Home Assistant theming, variants, and appearances. + * + * @slot - Label of the button + * @slot prefix - The prefix container (usually for icons). + * @slot suffix - The suffix container (usually for icons). + * + * @csspart base - The component's base wrapper. + * @csspart prefix - The container that wraps the prefix. + * @csspart label - The button's label. + * @csspart suffix - The container that wraps the suffix. + * @csspart caret - The button's caret icon, an `` element. + * @csspart spinner - The spinner that shows when the button is in the loading state. + * + * @cssprop --ha-button-font-family - Font family for the button text. + * @cssprop --ha-font-weight-medium - Medium font weight for button text. + * @cssprop --ha-button-line-height - Line height for button text. + * @cssprop --ha-button-border-width - Border width for the button. + * @cssprop --ha-button-theme-color - Main color for the button. + * @cssprop --ha-button-theme-darker-color - Darker variant of the main color. + * @cssprop --ha-button-theme-lighter-color - Lighter variant of the main color. + * @cssprop --ha-button-height - Height of the button. + * @cssprop --ha-button-border-radius - Border radius for the button. + * @cssprop --ha-button-text-color - Text color for the button. + * + * @attr {("small"|"medium")} size - Sets the button size. + * @attr {("primary"|"danger"|"neutral")} variant - Sets the button color variant. "primary" is default. + * @attr {("accent"|"filled"|"plain")} appearance - Sets the button appearance. + */ @customElement("ha-button") export class HaButton extends Button { - @property({ type: Boolean }) pill = true; + @property() appearance?: "accent" | "filled" | "plain"; static override styles = [ styles, css` :host { - --sl-input-border-width: 0; --sl-input-font-family: var( --ha-button-font-family, var(--ha-font-family-body) ); - --sl-font-weight-semibold: var(--ha-font-weight-medium); - --sl-transition-x-fast: 0.1s; /* ? */ - --sl-focus-ring: none; /* ? */ - --sl-focus-ring-offset: 0; /* ? */ + --sl-font-weight-semibold: var( + --ha-button-font-weight, + var(--ha-font-weight-medium) + ); + --sl-transition-x-fast: 0.4s; + --sl-focus-ring: solid 4px var(--accent-color); + --sl-focus-ring-offset: 1px; + + --sl-spacing-medium: 16px; + --sl-spacing-small: 12px; + --sl-spacing-x-small: 8px; + + --ha-button-theme-color: var(--primary-color); + --ha-button-theme-darker-color: var(--dark-primary-color); + --ha-button-theme-lighter-color: var(--light-primary-color); + + line-height: var(--ha-button-line-height, var(--ha-line-height-normal)); + --sl-input-border-width: var(--ha-button-border-width, 0); + } + + :host([destructive]), /* Deprecated */ + :host([variant="danger"]) { + --ha-button-theme-color: var(--error-color); + --ha-button-theme-darker-color: var(--error-color-darker); + --ha-button-theme-lighter-color: var(--error-color-lighter); + } + + :host([variant="neutral"]) { + --ha-button-theme-color: var(--primary-text-color); + --ha-button-theme-darker-color: var(--dark-primary-color); + --ha-button-theme-lighter-color: var(--light-primary-color); } .button { - height: 40px; + height: var(--ha-button-height, var(--button-height, 40px)); align-items: center; + border-radius: var(--ha-button-border-radius, 48px); } .button.button--small { - height: 32px; + border-radius: var(--ha-button-border-radius, 32px); + height: var(--ha-button-height, var(--button-height, 32px)); } - /* Default */ - .button--standard.button--default, - .button--standard.button--primary:active:not(.button--disabled) { - background-color: var( - --ha-button-background-color, - var(--primary-color) - ); + .button, + .button:active:not(.button--disabled) { + background-color: var(--ha-button-theme-color); color: var(--ha-button-text-color, var(--white-color)); } - .button--standard.button--default:hover:not(.button--disabled) { - background-color: var( - --ha-button-background-color, - var(--dark-primary-color) - ); - color: var(--ha-button-text-color, var(--white-color)); + .button:hover:not(.button--disabled) { + background-color: var(--ha-button-theme-darker-color); } - /* Danger */ - :host([destructive]) .button--standard, - .button--standard.button--danger, - .button--standard.button--danger:active:not(.button--disabled) { - background-color: var(--ha-button-background-color, var(--error-color)); - color: var(--ha-button-text-color, var(--white-color)); + :host([appearance="filled"]) .button, + :host([appearance="filled"]) .button:active:not(.button--disabled) { + background-color: var(--ha-button-theme-lighter-color); + color: var(--ha-button-text-color, var(--ha-button-theme-color)); + } + :host([appearance="filled"]) .button:hover:not(.button--disabled) { + background-color: var(--ha-button-theme-color); + color: var(--white-color); } - :host([destructive]) .button--standard:hover:not(.button--disabled), - .button--standard.button--danger:hover:not(.button--disabled) { - background-color: var(--ha-button-background-color, var(--error-color)); - color: var(--ha-button-text-color, var(--white-color)); - } - - /* - * Text buttons - */ - - .button--text, - .button--text:active:not(.button--disabled) { - color: var(--ha-button-text-color, var(--primary-color)); - } - - .button--text:hover:not(.button--disabled), - .button--text:focus-visible:not(.button--disabled) { + :host([appearance="plain"]) .button, + :host([appearance="plain"]) .button:active:not(.button--disabled) { background-color: transparent; - border-color: transparent; - color: var(--ha-button-text-color, var(--dark-primary-color)); + color: var(--ha-button-text-color, var(--ha-button-theme-color)); } - - .button--pill.button--small { - border-radius: 30px; - } - - .button--pill.button--medium { - border-radius: 48px; - } - - /* - * Button spacing - */ - - .button--has-label.button--small .button__label { - padding: 0 12px; - } - - .button--has-label.button--medium .button__label { - padding: 0 16px; - } - - .button--has-prefix.button--small, - .button--has-prefix.button--small .button__label { - padding-inline-start: 8px; - } - - .button--has-prefix.button--medium, - .button--has-prefix.button--medium .button__label { - padding-inline-start: 12px; - } - - .button--has-suffix.button--small, - .button--caret.button--small, - .button--has-suffix.button--small .button__label, - .button--caret.button--small .button__label { - padding-inline-end: 8px; - } - - .button--has-suffix.button--medium, - .button--caret.button--medium, - .button--has-suffix.button--medium .button__label, - .button--caret.button--medium .button__label { - padding-inline-end: 12px; - } - - ::slotted([slot="icon"]) { - margin-inline-start: 0px; - margin-inline-end: 8px; - direction: var(--direction); - display: block; - } - .mdc-button { - height: var(--button-height, 36px); - } - .trailing-icon { - display: flex; - } - .slot-container { - overflow: var(--button-slot-container-overflow, visible); - } - :host([destructive]) { - --mdc-theme-primary: var(--error-color); + :host([appearance="plain"]) .button:hover:not(.button--disabled) { + background-color: var(--ha-button-theme-lighter-color); + color: var(--ha-button-text-color, var(--ha-button-theme-darker-color)); } `, ]; diff --git a/src/panels/config/integrations/ha-config-flow-card.ts b/src/panels/config/integrations/ha-config-flow-card.ts index 499e6770a1..9f70e8deb9 100644 --- a/src/panels/config/integrations/ha-config-flow-card.ts +++ b/src/panels/config/integrations/ha-config-flow-card.ts @@ -4,6 +4,9 @@ import { LitElement, css, html } from "lit"; import { customElement, property } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; import { fireEvent } from "../../../common/dom/fire_event"; +import "../../../components/ha-button"; +import "../../../components/ha-button-menu"; +import "../../../components/ha-list-item"; import { ATTENTION_SOURCES, DISCOVERY_SOURCES, @@ -17,9 +20,6 @@ import type { HomeAssistant } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; import type { DataEntryFlowProgressExtended } from "./ha-config-integrations"; import "./ha-integration-action-card"; -import "../../../components/ha-button-menu"; -import "../../../components/ha-button"; -import "../../../components/ha-list-item"; @customElement("ha-config-flow-card") export class HaConfigFlowCard extends LitElement { @@ -41,24 +41,25 @@ export class HaConfigFlowCard extends LitElement { .domain=${this.flow.handler} .label=${this.flow.localized_title} > + ${DISCOVERY_SOURCES.includes(this.flow.context.source) && + this.flow.context.unique_id + ? html`${this.hass.localize( + "ui.panel.config.integrations.ignore.ignore" + )}` + : ""} + ${this.hass.localize( attention ? "ui.panel.config.integrations.reconfigure" : "ui.common.add" )} - > - ${DISCOVERY_SOURCES.includes(this.flow.context.source) && - this.flow.context.unique_id - ? html`` - : ""} + ${this.flow.context.configuration_url || this.manifest ? html` - + ${this.hass.localize( `ui.panel.config.integrations.config_entry.${ services ? "services" : "devices" @@ -133,7 +133,7 @@ export class HaIntegrationCard extends LitElement { ? html` - + ${this.hass.localize( `ui.panel.config.integrations.config_entry.entities`, { count: entitiesCount } @@ -144,7 +144,7 @@ export class HaIntegrationCard extends LitElement { ? html` - + ${this.hass.localize( `ui.panel.config.integrations.config_entry.entries`, { @@ -309,6 +309,7 @@ export class HaIntegrationCard extends LitElement { display: flex; align-items: center; justify-content: space-between; + padding: 4px 16px 4px 4px; } .debug-logging { --state-color: var(--warning-color); diff --git a/src/resources/theme/color.globals.ts b/src/resources/theme/color.globals.ts index e80932117e..3e69bc671b 100644 --- a/src/resources/theme/color.globals.ts +++ b/src/resources/theme/color.globals.ts @@ -34,6 +34,8 @@ export const colorStyles = css` --scrollbar-thumb-color: rgb(194, 194, 194); --error-color: #db4437; + --error-color-lighter: #ffdedc; + --error-color-darker: #b30532; --warning-color: #ffa600; --success-color: #43a047; --info-color: #039be5;