Put color wheel at the root level for more info light (#16909)

This commit is contained in:
Paul Bottein 2023-06-19 13:50:37 +02:00 committed by GitHub
parent 8abb58ae7d
commit 215f5e341a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 640 additions and 413 deletions

View File

@ -186,9 +186,8 @@ class HaHsColorPicker extends LitElement {
}
if (changedProps.has("value")) {
if (
this.value !== undefined &&
(this._localValue?.[0] !== this.value[0] ||
this._localValue?.[1] !== this.value[1])
this._localValue?.[0] !== this.value?.[0] ||
this._localValue?.[1] !== this.value?.[1]
) {
this._resetPosition();
}
@ -243,7 +242,11 @@ class HaHsColorPicker extends LitElement {
}
private _resetPosition() {
if (this.value === undefined) return;
if (this.value === undefined) {
this._cursorPosition = undefined;
this._localValue = undefined;
return;
}
this._cursorPosition = this._getCoordsFromValue(this.value);
this._localValue = this.value;
}
@ -384,6 +387,7 @@ class HaHsColorPicker extends LitElement {
canvas {
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 50%;
cursor: pointer;
}

View File

@ -0,0 +1,38 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement } from "lit/decorators";
@customElement("ha-icon-button-group")
export class HaIconButtonGroup extends LitElement {
protected render(): TemplateResult {
return html`<slot></slot>`;
}
static get styles(): CSSResultGroup {
return css`
:host {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
height: 56px;
border-radius: 28px;
background-color: rgba(139, 145, 151, 0.1);
box-sizing: border-box;
width: auto;
padding: 4px;
gap: 4px;
}
::slotted(.separator) {
background-color: rgba(var(--rgb-primary-text-color), 0.15);
width: 1px;
height: 40px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-icon-button-group": HaIconButtonGroup;
}
}

View File

@ -0,0 +1,52 @@
import { css, CSSResultGroup } from "lit";
import { customElement, property } from "lit/decorators";
import { HaIconButton } from "./ha-icon-button";
@customElement("ha-icon-button-toggle")
export class HaIconButtonToggle extends HaIconButton {
@property({ type: Boolean, reflect: true }) selected = false;
static get styles(): CSSResultGroup {
return css`
:host {
position: relative;
}
mwc-icon-button {
position: relative;
transition: color 180ms ease-in-out;
}
mwc-icon-button::before {
opacity: 0;
transition: opacity 180ms ease-in-out;
background-color: var(--primary-text-color);
border-radius: 20px;
height: 40px;
width: 40px;
content: "";
position: absolute;
top: -10px;
left: -10px;
bottom: -10px;
right: -10px;
margin: auto;
box-sizing: border-box;
}
:host([border-only]) mwc-icon-button::before {
background-color: transparent;
border: 2px solid var(--primary-text-color);
}
:host([selected]) mwc-icon-button {
color: var(--primary-background-color);
}
:host([selected]) mwc-icon-button::before {
opacity: 1;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-icon-button-toggle": HaIconButtonToggle;
}
}

View File

@ -139,7 +139,7 @@ class HaTempColorPicker extends LitElement {
this.setAttribute("aria-valuemax", this.max.toString());
}
if (changedProps.has("value")) {
if (this.value != null && this._localValue !== this.value) {
if (this._localValue !== this.value) {
this._resetPosition();
}
}
@ -197,7 +197,11 @@ class HaTempColorPicker extends LitElement {
}
private _resetPosition() {
if (this.value === undefined) return;
if (this.value === undefined) {
this._cursorPosition = undefined;
this._localValue = undefined;
return;
}
const [, y] = this._getCoordsFromValue(this.value);
const currentX = this._cursorPosition?.[0] ?? 0;
const x =
@ -391,6 +395,7 @@ class HaTempColorPicker extends LitElement {
canvas {
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 50%;
transition: box-shadow 180ms ease-in-out;
cursor: pointer;

View File

@ -159,3 +159,5 @@ export const computeDefaultFavoriteColors = (
return colors;
};
export const formatTempColor = (value: number) => `${value} K`;

View File

@ -1,16 +1,28 @@
import { mdiClose } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../../common/dom/fire_event";
import "../../../../components/ha-button";
import "../../../../components/ha-dialog";
import "../../../../components/ha-dialog-header";
import { EntityRegistryEntry } from "../../../../data/entity_registry";
import { LightColor } from "../../../../data/light";
import "../../../../components/ha-icon-button-toggle";
import type { EntityRegistryEntry } from "../../../../data/entity_registry";
import {
formatTempColor,
LightColor,
LightColorMode,
LightEntity,
lightSupportsColor,
lightSupportsColorMode,
} from "../../../../data/light";
import { haStyleDialog } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import "./light-color-picker";
import { LightColorFavoriteDialogParams } from "./show-dialog-light-color-favorite";
import type { HomeAssistant } from "../../../../types";
import "./light-color-rgb-picker";
import "./light-color-temp-picker";
import type { LightColorFavoriteDialogParams } from "./show-dialog-light-color-favorite";
export type LightPickerMode = "color_temp" | "color";
@customElement("dialog-light-color-favorite")
class DialogLightColorFavorite extends LitElement {
@ -22,11 +34,26 @@ class DialogLightColorFavorite extends LitElement {
@state() _color?: LightColor;
@state() private _mode?: LightPickerMode;
@state() private _modes: LightPickerMode[] = [];
@state() private _currentValue?: string;
private _colorHovered(ev: CustomEvent<HASSDomEvents["color-hovered"]>) {
if (ev.detail && "color_temp_kelvin" in ev.detail) {
this._currentValue = formatTempColor(ev.detail.color_temp_kelvin);
} else {
this._currentValue = undefined;
}
}
public async showDialog(
dialogParams: LightColorFavoriteDialogParams
): Promise<void> {
this._entry = dialogParams.entry;
this._dialogParams = dialogParams;
this._updateModes(dialogParams.defaultMode);
await this.updateComplete;
}
@ -37,10 +64,43 @@ class DialogLightColorFavorite extends LitElement {
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
private _updateModes(defaultMode?: LightPickerMode) {
const supportsTemp = lightSupportsColorMode(
this.stateObj!,
LightColorMode.COLOR_TEMP
);
const supportsColor = lightSupportsColor(this.stateObj!);
const modes: LightPickerMode[] = [];
if (supportsColor) {
modes.push("color");
}
if (supportsTemp) {
modes.push("color_temp");
}
this._modes = modes;
this._mode =
defaultMode ??
(this.stateObj!.attributes.color_mode
? this.stateObj!.attributes.color_mode === LightColorMode.COLOR_TEMP
? LightColorMode.COLOR_TEMP
: "color"
: this._modes[0]);
}
private _colorChanged(ev: CustomEvent) {
this._color = ev.detail;
}
get stateObj() {
return (
this._entry &&
(this.hass.states[this._entry.entity_id] as LightEntity | undefined)
);
}
private async _cancel() {
this._dialogParams?.cancel?.();
this.closeDialog();
@ -55,8 +115,16 @@ class DialogLightColorFavorite extends LitElement {
this.closeDialog();
}
private _modeChanged(ev): void {
const newMode = ev.currentTarget.mode;
if (newMode === this._mode) {
return;
}
this._mode = newMode;
}
protected render() {
if (!this._entry) {
if (!this._entry || !this.stateObj) {
return nothing;
}
@ -76,13 +144,58 @@ class DialogLightColorFavorite extends LitElement {
></ha-icon-button>
<span slot="title">${this._dialogParams?.title}</span>
</ha-dialog-header>
<light-color-picker
.hass=${this.hass}
entityId=${this._entry.entity_id}
.defaultMode=${this._dialogParams?.defaultMode}
@color-changed=${this._colorChanged}
<div class="header">
<span class="value">${this._currentValue}</span>
${this._modes.length > 1
? html`
<div class="modes">
${this._modes.map(
(value) =>
html`
<ha-icon-button-toggle
border-only
.selected=${value === this._mode}
.label=${this.hass.localize(
`ui.dialogs.more_info_control.light.color_picker.mode.${value}`
)}
.mode=${value}
@click=${this._modeChanged}
>
</light-color-picker>
<span
class="wheel ${classMap({ [value]: true })}"
></span>
</ha-icon-button-toggle>
`
)}
</div>
`
: nothing}
</div>
<div class="content">
${this._mode === "color_temp"
? html`
<light-color-temp-picker
.hass=${this.hass}
.stateObj=${this.stateObj}
@color-changed=${this._colorChanged}
@color-hovered=${this._colorHovered}
>
</light-color-temp-picker>
`
: nothing}
${this._mode === "color"
? html`
<light-color-rgb-picker
.hass=${this.hass}
.stateObj=${this.stateObj}
@color-changed=${this._colorChanged}
@color-hovered=${this._colorHovered}
>
</light-color-rgb-picker>
`
: nothing}
</div>
<ha-button slot="secondaryAction" dialogAction="cancel">
${this.hass.localize("ui.common.cancel")}
</ha-button>
@ -101,16 +214,10 @@ class DialogLightColorFavorite extends LitElement {
--dialog-content-padding: 0;
}
light-color-picker {
display: flex;
flex-direction: column;
flex: 1;
}
@media all and (max-width: 450px), all and (max-height: 500px) {
ha-dialog {
--dialog-surface-margin-top: 100px;
--mdc-dialog-min-height: calc(100% - 100px);
--mdc-dialog-min-height: auto;
--mdc-dialog-max-height: calc(100% - 100px);
--ha-dialog-border-radius: var(
--ha-dialog-bottom-sheet-border-radius,
@ -118,6 +225,54 @@ class DialogLightColorFavorite extends LitElement {
);
}
}
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 24px;
flex: 1;
}
.modes {
display: flex;
flex-direction: row;
justify-content: flex-end;
padding: 0 24px;
}
.wheel {
width: 30px;
height: 30px;
flex: none;
border-radius: 15px;
}
.wheel.color {
background-image: url("/static/images/color_wheel.png");
background-size: cover;
}
.wheel.color_temp {
background: linear-gradient(
0,
rgb(166, 209, 255) 0%,
white 50%,
rgb(255, 160, 0) 100%
);
}
.value {
pointer-events: none;
position: absolute;
top: 0;
left: 0;
right: 0;
margin: auto;
font-style: normal;
font-weight: 500;
font-size: 16px;
height: 48px;
line-height: 48px;
letter-spacing: 0.1px;
text-align: center;
}
`,
];
}

View File

@ -30,10 +30,16 @@ import {
} from "../../../../resources/sortable.ondemand";
import { HomeAssistant } from "../../../../types";
import { showConfirmationDialog } from "../../../generic/show-dialog-box";
import type { LightPickerMode } from "./dialog-light-color-favorite";
import "./ha-favorite-color-button";
import type { LightPickerMode } from "./light-color-picker";
import { showLightColorFavoriteDialog } from "./show-dialog-light-color-favorite";
declare global {
interface HASSDomEvents {
"favorite-color-edit-started";
}
}
@customElement("ha-more-info-light-favorite-colors")
export class HaMoreInfoLightFavoriteColors extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@ -147,8 +153,8 @@ export class HaMoreInfoLightFavoriteColors extends LitElement {
private _edit = async (index) => {
// Make sure the current favorite color is set
fireEvent(this, "favorite-color-edit-started");
await this._apply(index);
const defaultMode: LightPickerMode =
"color_temp_kelvin" in this._favoriteColors[index]
? "color_temp"

View File

@ -1,51 +0,0 @@
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { HomeAssistant } from "../../../../types";
import "./light-color-picker";
import { LightColorPickerViewParams } from "./show-view-light-color-picker";
@customElement("ha-more-info-view-light-color-picker")
class MoreInfoViewLightColorPicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public params?: LightColorPickerViewParams;
protected render() {
if (!this.params) {
return nothing;
}
return html`
<light-color-picker
.hass=${this.hass}
.entityId=${this.params.entityId}
.defaultMode=${this.params.defaultMode}
>
</light-color-picker>
`;
}
static get styles(): CSSResultGroup {
return [
css`
:host {
position: relative;
display: flex;
flex-direction: column;
flex: 1;
}
light-color-picker {
display: flex;
flex-direction: column;
flex: 1;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-more-info-view-light-color-picker": MoreInfoViewLightColorPicker;
}
}

View File

@ -23,21 +23,18 @@ import { fireEvent } from "../../../../common/dom/fire_event";
import { throttle } from "../../../../common/util/throttle";
import "../../../../components/ha-button-toggle-group";
import "../../../../components/ha-hs-color-picker";
import "../../../../components/ha-icon";
import "../../../../components/ha-icon-button-prev";
import "../../../../components/ha-labeled-slider";
import "../../../../components/ha-temp-color-picker";
import {
LightColor,
getLightCurrentModeRgbColor,
LightColor,
LightColorMode,
LightEntity,
lightSupportsColor,
lightSupportsColorMode,
} from "../../../../data/light";
import { HomeAssistant } from "../../../../types";
import "../../../../components/ha-icon";
export type LightPickerMode = "color_temp" | "color";
declare global {
interface HASSDomEvents {
@ -45,13 +42,11 @@ declare global {
}
}
@customElement("light-color-picker")
class LightColorPicker extends LitElement {
@customElement("light-color-rgb-picker")
class LightRgbColorPicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property() public entityId!: string;
@property() public defaultMode?: LightPickerMode;
@property({ attribute: false }) public stateObj!: LightEntity;
@state() private _cwSliderValue?: number;
@ -65,16 +60,6 @@ class LightColorPicker extends LitElement {
@state() private _hsPickerValue?: [number, number];
@state() private _ctPickerValue?: number;
@state() private _mode?: LightPickerMode;
@state() private _modes: LightPickerMode[] = [];
get stateObj() {
return this.hass.states[this.entityId] as LightEntity | undefined;
}
protected render() {
if (!this.stateObj) {
return nothing;
@ -100,41 +85,6 @@ class LightColorPicker extends LitElement {
: "";
return html`
${this._modes.length > 1
? html`
<mwc-tab-bar
.activeIndex=${this._mode ? this._modes.indexOf(this._mode) : 0}
@MDCTabBar:activated=${this._handleTabChanged}
>
${this._modes.map(
(value) =>
html`<mwc-tab
.label=${this.hass.localize(
`ui.dialogs.more_info_control.light.color_picker.mode.${value}`
)}
></mwc-tab>`
)}
</mwc-tab-bar>
`
: nothing}
<div class="content">
${this._mode === LightColorMode.COLOR_TEMP
? html`
<p class="color-temp-value">
${this._ctPickerValue ? `${this._ctPickerValue} K` : nothing}
</p>
<ha-temp-color-picker
@value-changed=${this._ctColorChanged}
@cursor-moved=${this._ctColorCursorMoved}
.min=${this.stateObj.attributes.min_color_temp_kelvin!}
.max=${this.stateObj.attributes.max_color_temp_kelvin!}
.value=${this._ctPickerValue}
>
</ha-temp-color-picker>
`
: nothing}
${this._mode === "color"
? html`
<div class="color-container">
<label class="native-color-picker">
<input
@ -168,9 +118,7 @@ class LightColorPicker extends LitElement {
</div>
${supportsRgbw || supportsRgbww
? html`<ha-labeled-slider
.caption=${this.hass.localize(
"ui.card.light.color_brightness"
)}
.caption=${this.hass.localize("ui.card.light.color_brightness")}
icon="hass:brightness-7"
max="100"
.value=${this._colorBrightnessSliderValue}
@ -181,9 +129,7 @@ class LightColorPicker extends LitElement {
${supportsRgbw
? html`
<ha-labeled-slider
.caption=${this.hass.localize(
"ui.card.light.white_value"
)}
.caption=${this.hass.localize("ui.card.light.white_value")}
icon="hass:file-word-box"
max="100"
.name=${"wv"}
@ -196,9 +142,7 @@ class LightColorPicker extends LitElement {
${supportsRgbww
? html`
<ha-labeled-slider
.caption=${this.hass.localize(
"ui.card.light.cold_white_value"
)}
.caption=${this.hass.localize("ui.card.light.cold_white_value")}
icon="hass:file-word-box-outline"
max="100"
.name=${"cw"}
@ -207,9 +151,7 @@ class LightColorPicker extends LitElement {
pin
></ha-labeled-slider>
<ha-labeled-slider
.caption=${this.hass.localize(
"ui.card.light.warm_white_value"
)}
.caption=${this.hass.localize("ui.card.light.warm_white_value")}
icon="hass:file-word-box"
max="100"
.name=${"ww"}
@ -219,16 +161,13 @@ class LightColorPicker extends LitElement {
></ha-labeled-slider>
`
: nothing}
`
: nothing}
</div>
`;
}
public _updateSliderValues() {
const stateObj = this.stateObj;
if (stateObj?.state === "on") {
if (stateObj.state === "on") {
this._brightnessAdjusted = undefined;
if (
stateObj.attributes.color_mode === LightColorMode.RGB &&
@ -242,10 +181,6 @@ class LightColorPicker extends LitElement {
this._brightnessAdjusted = maxVal;
}
}
this._ctPickerValue =
stateObj.attributes.color_mode === LightColorMode.COLOR_TEMP
? stateObj.attributes.color_temp_kelvin
: undefined;
this._wvSliderValue =
stateObj.attributes.color_mode === LightColorMode.RGBW &&
@ -273,8 +208,7 @@ class LightColorPicker extends LitElement {
? rgb2hs(currentRgbColor.slice(0, 3) as [number, number, number])
: undefined;
} else {
this._hsPickerValue = [0, 0];
this._ctPickerValue = undefined;
this._hsPickerValue = undefined;
this._wvSliderValue = undefined;
this._cwSliderValue = undefined;
this._wwSliderValue = undefined;
@ -288,43 +222,9 @@ class LightColorPicker extends LitElement {
return;
}
if (changedProps.has("entityId")) {
const supportsTemp = lightSupportsColorMode(
this.stateObj!,
LightColorMode.COLOR_TEMP
);
const supportsColor = lightSupportsColor(this.stateObj!);
const modes: LightPickerMode[] = [];
if (supportsColor) {
modes.push("color");
}
if (supportsTemp) {
modes.push("color_temp");
}
this._modes = modes;
this._mode =
this.defaultMode ??
(this.stateObj!.attributes.color_mode
? this.stateObj!.attributes.color_mode === LightColorMode.COLOR_TEMP
? LightColorMode.COLOR_TEMP
: "color"
: this._modes[0]);
}
this._updateSliderValues();
}
private _handleTabChanged(ev: CustomEvent): void {
const newMode = this._modes[ev.detail.index];
if (newMode === this._mode) {
return;
}
this._mode = newMode;
}
private _hsColorCursorMoved(ev: CustomEvent) {
if (!ev.detail.value) {
return;
@ -404,40 +304,6 @@ class LightColorPicker extends LitElement {
this._updateColor();
}
private _ctColorCursorMoved(ev: CustomEvent) {
const ct = ev.detail.value;
if (isNaN(ct) || this._ctPickerValue === ct) {
return;
}
this._ctPickerValue = ct;
this._throttleUpdateColorTemp();
}
private _throttleUpdateColorTemp = throttle(() => {
this._updateColorTemp();
}, 500);
private _ctColorChanged(ev: CustomEvent) {
const ct = ev.detail.value;
if (isNaN(ct) || this._ctPickerValue === ct) {
return;
}
this._ctPickerValue = ct;
this._updateColorTemp();
}
private _updateColorTemp() {
const color_temp_kelvin = this._ctPickerValue!;
this._applyColor({ color_temp_kelvin });
}
private _wvSliderChanged(ev: CustomEvent) {
const target = ev.target as any;
let wv = Number(target.value);
@ -574,19 +440,12 @@ class LightColorPicker extends LitElement {
display: flex;
flex-direction: column;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 24px;
flex: 1;
}
.native-color-picker {
position: absolute;
top: 0;
right: 0;
z-index: 1;
}
.native-color-picker ha-svg-icon {
@ -639,37 +498,18 @@ class LightColorPicker extends LitElement {
.color-container {
position: relative;
max-width: 300px;
min-width: 200px;
margin: 0 0 44px 0;
padding-top: 44px;
}
ha-hs-color-picker {
width: 100%;
}
ha-temp-color-picker {
max-width: 300px;
min-width: 200px;
margin: 20px 0 44px 0;
height: 45vh;
max-height: 320px;
min-height: 200px;
}
ha-labeled-slider {
width: 100%;
}
.color-temp-value {
font-style: normal;
font-weight: 500;
font-size: 16px;
height: 24px;
line-height: 24px;
letter-spacing: 0.1px;
margin: 0;
direction: ltr;
}
hr {
border-color: var(--divider-color);
border-bottom: none;
@ -682,6 +522,6 @@ class LightColorPicker extends LitElement {
declare global {
interface HTMLElementTagNameMap {
"light-color-picker": LightColorPicker;
"light-color-rgb-picker": LightRgbColorPicker;
}
}

View File

@ -0,0 +1,146 @@
import {
css,
CSSResultGroup,
html,
LitElement,
nothing,
PropertyValues,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import { throttle } from "../../../../common/util/throttle";
import "../../../../components/ha-temp-color-picker";
import {
LightColor,
LightColorMode,
LightEntity,
} from "../../../../data/light";
import { HomeAssistant } from "../../../../types";
declare global {
interface HASSDomEvents {
"color-changed": LightColor;
"color-hovered": LightColor | undefined;
}
}
@customElement("light-color-temp-picker")
class LightColorTempPicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public stateObj!: LightEntity;
@state() private _ctPickerValue?: number;
protected render() {
if (!this.stateObj) {
return nothing;
}
return html`
<ha-temp-color-picker
@value-changed=${this._ctColorChanged}
@cursor-moved=${this._ctColorCursorMoved}
.min=${this.stateObj.attributes.min_color_temp_kelvin!}
.max=${this.stateObj.attributes.max_color_temp_kelvin!}
.value=${this._ctPickerValue}
>
</ha-temp-color-picker>
`;
}
public _updateSliderValues() {
const stateObj = this.stateObj;
if (stateObj.state === "on") {
this._ctPickerValue =
stateObj.attributes.color_mode === LightColorMode.COLOR_TEMP
? stateObj.attributes.color_temp_kelvin
: undefined;
} else {
this._ctPickerValue = undefined;
}
}
public willUpdate(changedProps: PropertyValues) {
super.willUpdate(changedProps);
if (!changedProps.has("stateObj")) {
return;
}
this._updateSliderValues();
}
private _ctColorCursorMoved(ev: CustomEvent) {
const ct = ev.detail.value;
if (isNaN(ct) || this._ctPickerValue === ct) {
return;
}
this._ctPickerValue = ct;
fireEvent(this, "color-hovered", {
color_temp_kelvin: ct,
});
this._throttleUpdateColorTemp();
}
private _throttleUpdateColorTemp = throttle(() => {
this._updateColorTemp();
}, 500);
private _ctColorChanged(ev: CustomEvent) {
const ct = ev.detail.value;
fireEvent(this, "color-hovered", undefined);
if (isNaN(ct) || this._ctPickerValue === ct) {
return;
}
this._ctPickerValue = ct;
this._updateColorTemp();
}
private _updateColorTemp() {
const color_temp_kelvin = this._ctPickerValue!;
this._applyColor({ color_temp_kelvin });
}
private _applyColor(color: LightColor, params?: Record<string, any>) {
fireEvent(this, "color-changed", color);
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
...color,
...params,
});
}
static get styles(): CSSResultGroup {
return [
css`
:host {
display: flex;
flex-direction: column;
}
ha-temp-color-picker {
height: 45vh;
max-height: 320px;
min-height: 200px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"light-color-temp-picker": LightColorTempPicker;
}
}

View File

@ -1,7 +1,7 @@
import { fireEvent } from "../../../../common/dom/fire_event";
import { ExtEntityRegistryEntry } from "../../../../data/entity_registry";
import { LightColor } from "../../../../data/light";
import type { LightPickerMode } from "./light-color-picker";
import type { LightPickerMode } from "./dialog-light-color-favorite";
export interface LightColorFavoriteDialogParams {
entry: ExtEntityRegistryEntry;

View File

@ -1,23 +0,0 @@
import { fireEvent } from "../../../../common/dom/fire_event";
import type { LightPickerMode } from "./light-color-picker";
export interface LightColorPickerViewParams {
entityId: string;
defaultMode: LightPickerMode;
}
export const loadLightColorPickerView = () =>
import("./ha-more-info-view-light-color-picker");
export const showLightColorPickerView = (
element: HTMLElement,
title: string,
params: LightColorPickerViewParams
): void => {
fireEvent(element, "show-child-view", {
viewTag: "ha-more-info-view-light-color-picker",
viewImport: loadLightColorPickerView,
viewTitle: title,
viewParams: params,
});
};

View File

@ -1,5 +1,6 @@
import "@material/mwc-list/mwc-list-item";
import {
mdiBrightness6,
mdiCreation,
mdiFileWordBox,
mdiLightbulb,
@ -24,6 +25,8 @@ import { supportsFeature } from "../../../common/entity/supports-feature";
import { blankBeforePercent } from "../../../common/translations/blank_before_percent";
import "../../../components/ha-attributes";
import "../../../components/ha-button-menu";
import "../../../components/ha-icon-button-group";
import "../../../components/ha-icon-button-toggle";
import "../../../components/ha-outlined-button";
import "../../../components/ha-outlined-icon-button";
import "../../../components/ha-select";
@ -31,6 +34,7 @@ import { UNAVAILABLE } from "../../../data/entity";
import { ExtEntityRegistryEntry } from "../../../data/entity_registry";
import { forwardHaptic } from "../../../data/haptics";
import {
formatTempColor,
LightColorMode,
LightEntity,
LightEntityFeature,
@ -46,7 +50,10 @@ import "../components/ha-more-info-toggle";
import "../components/lights/ha-favorite-color-button";
import "../components/lights/ha-more-info-light-brightness";
import "../components/lights/ha-more-info-light-favorite-colors";
import { showLightColorPickerView } from "../components/lights/show-view-light-color-picker";
import "../components/lights/light-color-rgb-picker";
import "../components/lights/light-color-temp-picker";
type MainControl = "brightness" | "color_temp" | "color";
@customElement("more-info-light")
class MoreInfoLight extends LitElement {
@ -62,12 +69,24 @@ class MoreInfoLight extends LitElement {
@state() private _selectedBrightness?: number;
@state() private _colorTempPreview?: number;
@state() private _mainControl: MainControl = "brightness";
private _brightnessChanged(ev) {
const value = (ev.detail as any).value;
if (isNaN(value)) return;
this._selectedBrightness = value;
}
private _tempColorHovered(ev: CustomEvent<HASSDomEvents["color-hovered"]>) {
if (ev.detail && "color_temp_kelvin" in ev.detail) {
this._colorTempPreview = ev.detail.color_temp_kelvin;
} else {
this._colorTempPreview = undefined;
}
}
protected updated(changedProps: PropertyValues<typeof this>): void {
if (changedProps.has("stateObj")) {
this._selectedBrightness = this.stateObj?.attributes.brightness
@ -77,6 +96,28 @@ class MoreInfoLight extends LitElement {
}
}
private _setMainControl(ev: any) {
ev.stopPropagation();
this._mainControl = ev.currentTarget.control;
}
private _resetMainControl(ev: any) {
ev.stopPropagation();
this._mainControl = "brightness";
}
private get _stateOverride() {
if (this._colorTempPreview) {
return formatTempColor(this._colorTempPreview);
}
if (this._selectedBrightness) {
return `${Math.round(this._selectedBrightness)}${blankBeforePercent(
this.hass!.locale
)}%`;
}
return undefined;
}
protected render() {
if (!this.hass || !this.stateObj) {
return nothing;
@ -106,20 +147,26 @@ class MoreInfoLight extends LitElement {
(this.entry.options?.light?.favorite_colors == null ||
this.entry.options.light.favorite_colors.length > 0);
const stateOverride = this._selectedBrightness
? `${Math.round(this._selectedBrightness)}${blankBeforePercent(
this.hass!.locale
)}%`
: undefined;
return html`
<ha-more-info-state-header
.hass=${this.hass}
.stateObj=${this.stateObj}
.stateOverride=${stateOverride}
.stateOverride=${this._stateOverride}
></ha-more-info-state-header>
<div class="controls">
${supportsBrightness
${!supportsBrightness
? html`
<ha-more-info-toggle
.stateObj=${this.stateObj}
.hass=${this.hass}
.iconPathOn=${mdiLightbulb}
.iconPathOff=${mdiLightbulbOff}
></ha-more-info-toggle>
`
: nothing}
${supportsColorTemp || supportsColor || supportsBrightness
? html`
${supportsBrightness && this._mainControl === "brightness"
? html`
<ha-more-info-light-brightness
.stateObj=${this.stateObj}
@ -128,25 +175,32 @@ class MoreInfoLight extends LitElement {
>
</ha-more-info-light-brightness>
`
: html`
<ha-more-info-toggle
.stateObj=${this.stateObj}
.hass=${this.hass}
.iconPathOn=${mdiLightbulb}
.iconPathOff=${mdiLightbulbOff}
></ha-more-info-toggle>
`}
${supportsColorTemp || supportsColor || supportsBrightness
: nothing}
${supportsColor && this._mainControl === "color"
? html`
<div class="button-bar">
<light-color-rgb-picker
.hass=${this.hass}
.stateObj=${this.stateObj}
>
</light-color-rgb-picker>
`
: nothing}
${supportsColorTemp && this._mainControl === "color_temp"
? html`
<light-color-temp-picker
.hass=${this.hass}
.stateObj=${this.stateObj}
@color-hovered=${this._tempColorHovered}
>
</light-color-temp-picker>
`
: nothing}
<ha-icon-button-group>
${supportsBrightness
? html`
<ha-icon-button
.disabled=${this.stateObj!.state === UNAVAILABLE}
.title=${this.hass.localize(
"ui.dialogs.more_info_control.light.toggle"
)}
.ariaLabel=${this.hass.localize(
.label=${this.hass.localize(
"ui.dialogs.more_info_control.light.toggle"
)}
@click=${this._toggle}
@ -155,49 +209,60 @@ class MoreInfoLight extends LitElement {
</ha-icon-button>
`
: nothing}
${supportsColor || supportsColorTemp
? html`
<div class="separator"></div>
<ha-icon-button-toggle
.selected=${this._mainControl === "brightness"}
.disabled=${this.stateObj!.state === UNAVAILABLE}
.label=${this.hass.localize(
"ui.dialogs.more_info_control.light.brightness"
)}
.control=${"brightness"}
@click=${this._setMainControl}
>
<ha-svg-icon .path=${mdiBrightness6}></ha-svg-icon>
</ha-icon-button-toggle>
`
: nothing}
${supportsColor
? html`
<ha-icon-button
class="color-mode"
<ha-icon-button-toggle
border-only
.selected=${this._mainControl === "color"}
.disabled=${this.stateObj!.state === UNAVAILABLE}
.title=${this.hass.localize(
"ui.dialogs.more_info_control.light.change_color"
.label=${this.hass.localize(
"ui.dialogs.more_info_control.light.color"
)}
.ariaLabel=${this.hass.localize(
"ui.dialogs.more_info_control.light.change_color"
)}
.mode=${"color"}
@click=${this._showLightColorPickerView}
.control=${"color"}
@click=${this._setMainControl}
>
<span class="wheel color"></span>
</ha-icon-button>
</ha-icon-button-toggle>
`
: nothing}
${supportsColorTemp
? html`
<ha-icon-button
<ha-icon-button-toggle
border-only
.selected=${this._mainControl === "color_temp"}
.disabled=${this.stateObj!.state === UNAVAILABLE}
.title=${this.hass.localize(
"ui.dialogs.more_info_control.light.change_color_temp"
.label=${this.hass.localize(
"ui.dialogs.more_info_control.light.color_temp"
)}
.ariaLabel=${this.hass.localize(
"ui.dialogs.more_info_control.light.change_color_temp"
)}
.mode=${"color_temp"}
@click=${this._showLightColorPickerView}
.control=${"color_temp"}
@click=${this._setMainControl}
>
<span class="wheel color-temp"></span>
</ha-icon-button>
</ha-icon-button-toggle>
`
: nothing}
${supportsWhite
? html`
<div class="separator"></div>
<ha-icon-button
.disabled=${this.stateObj!.state === UNAVAILABLE}
.title=${this.hass.localize(
"ui.dialogs.more_info_control.light.set_white"
)}
.ariaLabel=${this.hass.localize(
.label=${this.hass.localize(
"ui.dialogs.more_info_control.light.set_white"
)}
@click=${this._setWhite}
@ -206,7 +271,7 @@ class MoreInfoLight extends LitElement {
</ha-icon-button>
`
: nothing}
</div>
</ha-icon-button-group>
${this.entry &&
lightSupportsFavoriteColors(this.stateObj) &&
(this.editMode || hasFavoriteColors)
@ -216,6 +281,7 @@ class MoreInfoLight extends LitElement {
.stateObj=${this.stateObj}
.entry=${this.entry}
.editMode=${this.editMode}
@favorite-color-edit-started=${this._resetMainControl}
>
</ha-more-info-light-favorite-colors>
`
@ -291,19 +357,6 @@ class MoreInfoLight extends LitElement {
});
};
private _showLightColorPickerView = (ev) => {
showLightColorPickerView(
this,
this.hass.localize(
"ui.dialogs.more_info_control.light.color_picker.title"
),
{
entityId: this.stateObj!.entity_id,
defaultMode: ev.currentTarget.mode,
}
);
};
private _setWhite = () => {
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
@ -347,9 +400,6 @@ class MoreInfoLight extends LitElement {
flex: none;
border-radius: 15px;
}
ha-icon-button[disabled] .wheel {
filter: grayscale(1) opacity(0.5);
}
.wheel.color {
background-image: url("/static/images/color_wheel.png");
background-size: cover;
@ -362,6 +412,9 @@ class MoreInfoLight extends LitElement {
rgb(255, 160, 0) 100%
);
}
*[disabled] .wheel {
filter: grayscale(1) opacity(0.5);
}
.buttons {
flex-wrap: wrap;
max-width: 250px;

View File

@ -929,8 +929,8 @@
"light": {
"edit_mode": "Edit favorite colors",
"toggle": "Toggle",
"change_color": "Change color",
"change_color_temp": "Change color temperature",
"color": "Color",
"color_temp": "Temperature",
"set_white": "Set white",
"select_effect": "Select effect",
"brightness": "Brightness",