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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user