Files
frontend/src/components/ha-button.ts
2025-10-07 16:03:54 +03:00

242 lines
7.8 KiB
TypeScript

import Button from "@home-assistant/webawesome/dist/components/button/button";
import { css, type CSSResultGroup } from "lit";
import { customElement } from "lit/decorators";
export type Appearance = "accent" | "filled" | "outlined" | "plain";
/**
* Home Assistant button component
*
* @element ha-button
* @extends {Button}
*
* @summary
* A stylable button component supporting Home Assistant theming, variants, and appearances based on webawesome button.
*
* @slot - Label of the button
* @slot start - The prefix container (usually for icons).
* @slot end - The suffix container (usually for icons).
*
* @csspart base - The component's base wrapper.
* @csspart start - The container that wraps the prefix.
* @csspart label - The button's label.
* @csspart end - The container that wraps the suffix.
* @csspart caret - The button's caret icon, an `<sl-icon>` element.
* @csspart spinner - The spinner that shows when the button is in the loading state.
*
* @cssprop --ha-button-height - The height of the button.
* @cssprop --ha-button-border-radius - The border radius of the button. defaults to `var(--ha-border-radius-pill)`.
*
* @attr {("small"|"medium")} size - Sets the button size.
* @attr {("brand"|"neutral"|"danger"|"warning"|"success")} variant - Sets the button color variant. "primary" is default.
* @attr {("accent"|"filled"|"plain")} appearance - Sets the button appearance.
* @attr {boolean} loading - shows a loading indicator instead of the buttons label and disable buttons click.
* @attr {boolean} disabled - Disables the button and prevents user interaction.
*/
@customElement("ha-button")
export class HaButton extends Button {
variant: "brand" | "neutral" | "success" | "warning" | "danger" = "brand";
static get styles(): CSSResultGroup {
return [
Button.styles,
css`
:host {
--wa-form-control-padding-inline: 16px;
--wa-font-weight-action: var(--ha-font-weight-medium);
--wa-form-control-border-radius: var(
--ha-button-border-radius,
var(--ha-border-radius-pill)
);
--wa-form-control-height: var(
--ha-button-height,
var(--button-height, 40px)
);
}
.button {
font-size: var(--ha-font-size-m);
line-height: 1;
transition: background-color 0.15s ease-in-out;
}
:host([size="small"]) .button {
--wa-form-control-height: var(
--ha-button-height,
var(--button-height, 32px)
);
font-size: var(--wa-font-size-s, var(--ha-font-size-m));
--wa-form-control-padding-inline: 12px;
}
:host([variant="brand"]) {
--button-color-fill-normal-active: var(
--ha-color-fill-primary-normal-active
);
--button-color-fill-normal-hover: var(
--ha-color-fill-primary-normal-hover
);
--button-color-fill-loud-active: var(
--ha-color-fill-primary-loud-active
);
--button-color-fill-loud-hover: var(
--ha-color-fill-primary-loud-hover
);
}
:host([variant="neutral"]) {
--button-color-fill-normal-active: var(
--ha-color-fill-neutral-normal-active
);
--button-color-fill-normal-hover: var(
--ha-color-fill-neutral-normal-hover
);
--button-color-fill-loud-active: var(
--ha-color-fill-neutral-loud-active
);
--button-color-fill-loud-hover: var(
--ha-color-fill-neutral-loud-hover
);
}
:host([variant="success"]) {
--button-color-fill-normal-active: var(
--ha-color-fill-success-normal-active
);
--button-color-fill-normal-hover: var(
--ha-color-fill-success-normal-hover
);
--button-color-fill-loud-active: var(
--ha-color-fill-success-loud-active
);
--button-color-fill-loud-hover: var(
--ha-color-fill-success-loud-hover
);
}
:host([variant="warning"]) {
--button-color-fill-normal-active: var(
--ha-color-fill-warning-normal-active
);
--button-color-fill-normal-hover: var(
--ha-color-fill-warning-normal-hover
);
--button-color-fill-loud-active: var(
--ha-color-fill-warning-loud-active
);
--button-color-fill-loud-hover: var(
--ha-color-fill-warning-loud-hover
);
}
:host([variant="danger"]) {
--button-color-fill-normal-active: var(
--ha-color-fill-danger-normal-active
);
--button-color-fill-normal-hover: var(
--ha-color-fill-danger-normal-hover
);
--button-color-fill-loud-active: var(
--ha-color-fill-danger-loud-active
);
--button-color-fill-loud-hover: var(
--ha-color-fill-danger-loud-hover
);
}
:host([appearance~="plain"]) .button {
color: var(--wa-color-on-normal);
background-color: transparent;
}
:host([appearance~="plain"]) .button.disabled {
background-color: transparent;
color: var(--ha-color-on-disabled-quiet);
}
:host([appearance~="outlined"]) .button.disabled {
background-color: transparent;
color: var(--ha-color-on-disabled-quiet);
}
@media (hover: hover) {
:host([appearance~="filled"])
.button:not(.disabled):not(.loading):hover {
background-color: var(--button-color-fill-normal-hover);
}
:host([appearance~="accent"])
.button:not(.disabled):not(.loading):hover {
background-color: var(--button-color-fill-loud-hover);
}
:host([appearance~="plain"])
.button:not(.disabled):not(.loading):hover {
color: var(--wa-color-on-normal);
}
}
:host([appearance~="filled"]) .button {
color: var(--wa-color-on-normal);
background-color: var(--wa-color-fill-normal);
border-color: transparent;
}
:host([appearance~="filled"])
.button:not(.disabled):not(.loading):active {
background-color: var(--button-color-fill-normal-active);
}
:host([appearance~="filled"]) .button.disabled {
background-color: var(--ha-color-fill-disabled-normal-resting);
color: var(--ha-color-on-disabled-normal);
}
:host([appearance~="accent"]) .button {
background-color: var(
--wa-color-fill-loud,
var(--wa-color-neutral-fill-loud)
);
}
:host([appearance~="accent"])
.button:not(.disabled):not(.loading):active {
background-color: var(--button-color-fill-loud-active);
}
:host([appearance~="accent"]) .button.disabled {
background-color: var(--ha-color-fill-disabled-loud-resting);
color: var(--ha-color-on-disabled-loud);
}
:host([loading]) {
pointer-events: none;
}
.button.disabled {
opacity: 1;
}
slot[name="start"]::slotted(*) {
margin-inline-end: 4px;
}
slot[name="end"]::slotted(*) {
margin-inline-start: 4px;
}
.button.has-start {
padding-inline-start: 8px;
}
.button.has-end {
padding-inline-end: 8px;
}
.label {
overflow: hidden;
text-overflow: ellipsis;
padding: var(--ha-space-1) 0;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-button": HaButton;
}
}