1
0
mirror of https://github.com/home-assistant/frontend.git synced 2026-04-02 00:27:49 +01:00

Refactor light color favorites card feature and button (#30281)

This commit is contained in:
Paul Bottein
2026-03-23 16:29:48 +01:00
committed by GitHub
parent f03eee6cb2
commit 25a1c14523
2 changed files with 106 additions and 139 deletions

View File

@@ -1,4 +1,3 @@
import type { CSSResultGroup } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, query } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
@@ -10,17 +9,10 @@ import {
temperature2rgb,
} from "../../../../common/color/convert-light-color";
import { luminosity } from "../../../../common/color/rgb";
import "../../../../components/ha-outlined-icon-button";
import type { HaOutlinedIconButton } from "../../../../components/ha-outlined-icon-button";
import "../../../../components/ha-svg-icon";
import type { LightColor, LightEntity } from "../../../../data/light";
@customElement("ha-favorite-color-button")
class MoreInfoViewLightColorPicker extends LitElement {
public override focus() {
this._button?.focus();
}
class HaFavoriteColorButton extends LitElement {
@property({ attribute: false }) label?: string;
@property({ type: Boolean, reflect: true }) disabled = false;
@@ -29,10 +21,12 @@ class MoreInfoViewLightColorPicker extends LitElement {
@property({ attribute: false }) color!: LightColor;
@property({ type: Boolean, reflect: true }) wide = false;
@query("button", true)
private _button!: HTMLButtonElement;
@query("ha-outlined-icon-button", true)
private _button?: HaOutlinedIconButton;
public override focus() {
this._button?.focus();
}
private get _rgbColor(): [number, number, number] {
if (this.color) {
@@ -63,83 +57,64 @@ class MoreInfoViewLightColorPicker extends LitElement {
protected render() {
const backgroundColor = rgb2hex(this._rgbColor);
const isLight = luminosity(this._rgbColor) > 0.8;
const iconColor = isLight
? ([33, 33, 33] as [number, number, number])
: ([255, 255, 255] as [number, number, number]);
const hexIconColor = rgb2hex(iconColor);
const rgbIconColor = iconColor.join(", ");
const borderColor = isLight ? "var(--divider-color)" : "transparent";
return html`
<ha-outlined-icon-button
no-ripple
<button
.disabled=${this.disabled}
title=${ifDefined(this.label)}
aria-label=${ifDefined(this.label)}
style=${styleMap({
"background-color": backgroundColor,
"--icon-color": hexIconColor,
"--rgb-icon-color": rgbIconColor,
"border-color": borderColor,
"--focus-color": isLight ? borderColor : backgroundColor,
})}
></ha-outlined-icon-button>
></button>
`;
}
static get styles(): CSSResultGroup {
return [
css`
:host {
display: block;
}
ha-outlined-icon-button {
--ha-icon-display: block;
--md-sys-color-on-surface: var(
--icon-color,
var(--secondary-text-color)
);
--md-sys-color-on-surface-variant: var(
--icon-color,
var(--secondary-text-color)
);
--md-sys-color-on-surface-rgb: var(
--rgb-icon-color,
var(--rgb-secondary-text-color)
);
--md-sys-color-outline: var(--divider-color);
--md-ripple-focus-color: 0;
--md-ripple-hover-opacity: 0;
--md-ripple-pressed-opacity: 0;
border-radius: var(--ha-border-radius-pill);
}
:host([wide]) ha-outlined-icon-button {
width: 100%;
border-radius: var(--ha-favorite-color-button-border-radius);
--_container-shape: var(--ha-favorite-color-button-border-radius);
--_container-shape-start-start: var(
--ha-favorite-color-button-border-radius
);
--_container-shape-start-end: var(
--ha-favorite-color-button-border-radius
);
--_container-shape-end-start: var(
--ha-favorite-color-button-border-radius
);
--_container-shape-end-end: var(
--ha-favorite-color-button-border-radius
);
}
:host([disabled]) {
pointer-events: none;
}
ha-outlined-icon-button[disabled] {
filter: grayscale(1) opacity(0.5);
}
`,
];
}
static readonly styles = css`
:host {
display: block;
width: var(--ha-favorite-color-button-size, 40px);
height: var(--ha-favorite-color-button-size, 40px);
}
button {
background-color: var(--color);
position: relative;
display: block;
width: 100%;
height: 100%;
border: 1px solid transparent;
border-radius: var(
--ha-favorite-color-button-border-radius,
var(--ha-border-radius-pill)
);
padding: 0;
margin: 0;
cursor: pointer;
outline: none;
transition:
box-shadow 180ms ease-in-out,
transform 180ms ease-in-out;
}
button:focus-visible {
box-shadow: 0 0 0 2px var(--focus-color);
}
button:active {
transform: scale(1.1);
}
:host([disabled]) {
pointer-events: none;
}
button:disabled {
filter: grayscale(1) opacity(0.5);
}
`;
}
declare global {
interface HTMLElementTagNameMap {
"ha-favorite-color-button": MoreInfoViewLightColorPicker;
"ha-favorite-color-button": HaFavoriteColorButton;
}
}

View File

@@ -1,6 +1,7 @@
import { ResizeController } from "@lit-labs/observers/resize-controller";
import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state, query } from "lit/decorators";
import { css, html, LitElement, nothing, unsafeCSS } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import { computeDomain } from "../../../common/entity/compute_domain";
import { UNAVAILABLE } from "../../../data/entity/entity";
@@ -23,7 +24,9 @@ import {
} from "../../../data/entity/entity_registry";
import "../../../dialogs/more-info/components/lights/ha-favorite-color-button";
import { actionHandler } from "../common/directives/action-handler-directive";
import { debounce } from "../../../common/util/debounce";
const PILL_GAP = 8;
const PILL_MIN_SIZE = 32;
export const supportsLightColorFavoritesCardFeature = (
hass: HomeAssistant,
@@ -52,24 +55,26 @@ class HuiLightColorFavoritesCardFeature
@state() private _favoriteColors: LightColor[] = [];
@state() private _maxVisible = 0;
@query(".container") private _container!: HTMLDivElement;
private _resizeObserver?: ResizeObserver;
private _unsubEntityRegistry?: UnsubscribeFunc;
private _resizeController = new ResizeController(this, {
callback: (entries) => {
const width = entries[0]?.contentRect.width;
if (width) {
return Math.floor((width + PILL_GAP) / (PILL_MIN_SIZE + PILL_GAP));
}
return 0;
},
});
public connectedCallback() {
super.connectedCallback();
this._subscribeEntityEntry();
this.updateComplete.then(() => this._attachObserver());
}
public disconnectedCallback() {
super.disconnectedCallback();
this._unsubscribeEntityRegistry();
this._resizeObserver?.disconnect();
}
private _unsubscribeEntityRegistry() {
@@ -98,19 +103,8 @@ class HuiLightColorFavoritesCardFeature
}
}
private _measure() {
const w = this._container.clientWidth;
const pillMin = 32 + 8;
this._maxVisible = Math.floor(w / pillMin);
}
private _attachObserver(): void {
if (!this._resizeObserver) {
this._resizeObserver = new ResizeObserver(
debounce(() => this._measure(), 250, false)
);
}
this._resizeObserver.observe(this._container);
private get _maxVisible() {
return this._resizeController.value ?? 0;
}
private get _stateObj() {
@@ -126,19 +120,13 @@ class HuiLightColorFavoritesCardFeature
this._subscribeEntityEntry();
}
if (changedProps.has("_entry") || changedProps.has("_maxVisible")) {
if (this._entry) {
if (this._entry.options?.light?.favorite_colors) {
this._favoriteColors =
this._entry.options.light.favorite_colors.slice(
0,
this._maxVisible
);
} else if (this._stateObj) {
this._favoriteColors = computeDefaultFavoriteColors(
this._stateObj
).slice(0, this._maxVisible);
}
if (changedProps.has("_entry")) {
if (this._entry?.options?.light?.favorite_colors) {
this._favoriteColors = this._entry.options.light.favorite_colors;
} else if (this._entry && this._stateObj) {
this._favoriteColors = computeDefaultFavoriteColors(this._stateObj);
} else {
this._favoriteColors = [];
}
}
}
@@ -167,27 +155,26 @@ class HuiLightColorFavoritesCardFeature
return nothing;
}
const visibleColors = this._favoriteColors.slice(0, this._maxVisible);
return html`
<div class="container">
${this._favoriteColors.map(
${visibleColors.map(
(color, index) => html`
<div class="color">
<ha-favorite-color-button
wide
.label=${this.hass!.localize(
`ui.dialogs.more_info_control.light.favorite_color.set`,
{ number: index }
)}
.disabled=${this._stateObj!.state === UNAVAILABLE}
.color=${color}
.index=${index}
.actionHandler=${actionHandler({
disabled: this._stateObj!.state === UNAVAILABLE,
})}
@action=${this._handleColorAction}
>
</ha-favorite-color-button>
</div>
<ha-favorite-color-button
.label=${this.hass!.localize(
`ui.dialogs.more_info_control.light.favorite_color.set`,
{ number: index }
)}
.disabled=${this._stateObj!.state === UNAVAILABLE}
.color=${color}
.index=${index}
.actionHandler=${actionHandler({
disabled: this._stateObj!.state === UNAVAILABLE,
})}
@action=${this._handleColorAction}
>
</ha-favorite-color-button>
`
)}
</div>
@@ -209,24 +196,29 @@ class HuiLightColorFavoritesCardFeature
return [
cardFeatureStyles,
css`
:host {
display: block;
--min-width: ${unsafeCSS(PILL_MIN_SIZE)}px;
--gap: ${unsafeCSS(PILL_GAP)}px;
}
.container {
position: relative;
display: flex;
user-select: none;
flex-wrap: nowrap;
align-items: center;
gap: 8px;
gap: var(--gap);
height: var(--feature-height);
}
.color {
position: relative;
display: block;
flex: 1 1 32px;
min-width: 32px;
height: 40px;
}
ha-favorite-color-button {
--ha-favorite-color-button-border-radius: var(--ha-border-radius-md);
--ha-favorite-color-button-border-radius: var(
--feature-border-radius
);
height: 100%;
min-width: var(--min-width);
width: 100%;
flex: 1 1 var(--min-width);
}
`,
];