mirror of
https://github.com/home-assistant/frontend.git
synced 2026-08-19 13:07:21 +00:00
Compare commits
2
Commits
dev
...
dualaxis-lights
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89869fbd5c | ||
|
|
2495976fca |
@@ -57,6 +57,7 @@ export default [
|
||||
"ha-control-select",
|
||||
"ha-control-select-menu",
|
||||
"ha-hs-color-picker",
|
||||
"ha-color-temp-brightness-picker",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
title: Color temperature and brightness picker
|
||||
subtitle: Dual-axis pad controlling color temperature and brightness together
|
||||
---
|
||||
@@ -0,0 +1,102 @@
|
||||
import "../../../../src/components/ha-color-temp-brightness-picker";
|
||||
|
||||
import type { TemplateResult } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
|
||||
import "../../../../src/components/ha-card";
|
||||
import "../../../../src/components/ha-slider";
|
||||
|
||||
const MIN_KELVIN = 2000;
|
||||
const MAX_KELVIN = 6500;
|
||||
|
||||
@customElement("demo-components-ha-color-temp-brightness-picker")
|
||||
export class DemoHaColorTempBrightnessPicker extends LitElement {
|
||||
@state()
|
||||
value: [number, number] = [3000, 50];
|
||||
|
||||
@state()
|
||||
liveValue?: [number, number];
|
||||
|
||||
private _valueCursor(ev) {
|
||||
this.liveValue = ev.detail.value;
|
||||
}
|
||||
|
||||
private _valueChanged(ev) {
|
||||
this.value = ev.detail.value;
|
||||
}
|
||||
|
||||
private _kelvinChanged(ev) {
|
||||
this.value = [Number(ev.target.value), this.value[1]];
|
||||
}
|
||||
|
||||
private _brightnessChanged(ev) {
|
||||
this.value = [this.value[0], Number(ev.target.value)];
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const [kelvin, brightness] = this.liveValue ?? this.value;
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="card-content">
|
||||
<p class="value">${kelvin} K - ${brightness}%</p>
|
||||
<ha-color-temp-brightness-picker
|
||||
.value=${this.value}
|
||||
.minKelvin=${MIN_KELVIN}
|
||||
.maxKelvin=${MAX_KELVIN}
|
||||
@value-changed=${this._valueChanged}
|
||||
@cursor-moved=${this._valueCursor}
|
||||
></ha-color-temp-brightness-picker>
|
||||
<p>Color temperature: ${this.value[0]} K</p>
|
||||
<ha-slider
|
||||
labeled
|
||||
step="1"
|
||||
.min=${MIN_KELVIN}
|
||||
.max=${MAX_KELVIN}
|
||||
.value=${this.value[0]}
|
||||
@change=${this._kelvinChanged}
|
||||
>
|
||||
</ha-slider>
|
||||
<p>Brightness: ${this.value[1]}%</p>
|
||||
<ha-slider
|
||||
labeled
|
||||
step="1"
|
||||
min="1"
|
||||
max="100"
|
||||
.value=${this.value[1]}
|
||||
@change=${this._brightnessChanged}
|
||||
>
|
||||
</ha-slider>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
ha-card {
|
||||
max-width: 600px;
|
||||
margin: 24px auto;
|
||||
}
|
||||
.card-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
ha-color-temp-brightness-picker {
|
||||
width: 320px;
|
||||
height: 320px;
|
||||
}
|
||||
.value {
|
||||
font-size: var(--ha-font-size-xl);
|
||||
font-weight: var(--ha-font-weight-bold);
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"demo-components-ha-color-temp-brightness-picker": DemoHaColorTempBrightnessPicker;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
import { DIRECTION_ALL, Manager, Pan, Tap } from "@egjs/hammerjs";
|
||||
import type { PropertyValues } from "lit";
|
||||
import { css, html, LitElement } from "lit";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { rgb2hex } from "../common/color/convert-color";
|
||||
import {
|
||||
DEFAULT_MAX_KELVIN,
|
||||
DEFAULT_MIN_KELVIN,
|
||||
temperature2rgb,
|
||||
} from "../common/color/convert-light-color";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { mainWindow } from "../common/dom/get_main_window";
|
||||
import { generateColorTemperatureGradient } from "../dialogs/more-info/components/lights/light-color-temp-picker";
|
||||
|
||||
const clamp = (value: number, min: number, max: number) =>
|
||||
Math.min(max, Math.max(min, value));
|
||||
|
||||
// Dark end of the vertical brightness gradient; not fully black so the
|
||||
// temperature hue stays visible at the bottom of the pad
|
||||
const PAD_DARK_COLOR = "rgb(26, 26, 26)";
|
||||
|
||||
// The marker color's luminance scales with brightness down to this floor,
|
||||
// so the marker never renders fully black at minimum brightness
|
||||
const MARKER_LUMINANCE_FLOOR = 0.2;
|
||||
|
||||
@customElement("ha-color-temp-brightness-picker")
|
||||
export class HaColorTempBrightnessPicker extends LitElement {
|
||||
@property({ type: Boolean, reflect: true })
|
||||
public disabled = false;
|
||||
|
||||
/** [color temperature in kelvin, brightness percentage] */
|
||||
@property({ attribute: false })
|
||||
public value?: [number, number];
|
||||
|
||||
@property({ attribute: false })
|
||||
public minKelvin?: number;
|
||||
|
||||
@property({ attribute: false })
|
||||
public maxKelvin?: number;
|
||||
|
||||
@query("#pad") private _pad?: HTMLElement;
|
||||
|
||||
private _mc?: HammerManager;
|
||||
|
||||
@state()
|
||||
private _pressed?: string;
|
||||
|
||||
// Normalized physical position on the pad, [0, 1] left-to-right/top-to-bottom
|
||||
@state()
|
||||
private _cursorPosition?: [number, number];
|
||||
|
||||
@state()
|
||||
private _localValue?: [number, number];
|
||||
|
||||
private get _minKelvin() {
|
||||
return this.minKelvin ?? DEFAULT_MIN_KELVIN;
|
||||
}
|
||||
|
||||
private get _maxKelvin() {
|
||||
return this.maxKelvin ?? DEFAULT_MAX_KELVIN;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues<this>): void {
|
||||
super.firstUpdated(changedProps);
|
||||
this._setupListeners();
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this._setupListeners();
|
||||
}
|
||||
|
||||
disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this._destroyListeners();
|
||||
}
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues<this>): void {
|
||||
super.willUpdate(changedProps);
|
||||
const rangeChanged =
|
||||
changedProps.has("minKelvin") || changedProps.has("maxKelvin");
|
||||
if (
|
||||
rangeChanged ||
|
||||
(changedProps.has("value") &&
|
||||
(this._localValue?.[0] !== this.value?.[0] ||
|
||||
this._localValue?.[1] !== this.value?.[1]))
|
||||
) {
|
||||
this._resetPosition();
|
||||
}
|
||||
}
|
||||
|
||||
private _setupListeners() {
|
||||
if (this._pad && !this._mc) {
|
||||
this._mc = new Manager(this._pad);
|
||||
this._mc.add(
|
||||
new Pan({
|
||||
direction: DIRECTION_ALL,
|
||||
enable: true,
|
||||
})
|
||||
);
|
||||
|
||||
this._mc.add(new Tap({ event: "singletap" }));
|
||||
|
||||
let savedPosition;
|
||||
this._mc.on("panstart", (e) => {
|
||||
if (this.disabled) return;
|
||||
this._pressed = e.pointerType;
|
||||
savedPosition = this._cursorPosition;
|
||||
});
|
||||
this._mc.on("pancancel", () => {
|
||||
if (this.disabled) return;
|
||||
this._pressed = undefined;
|
||||
this._cursorPosition = savedPosition;
|
||||
});
|
||||
this._mc.on("panmove", (e) => {
|
||||
if (this.disabled) return;
|
||||
this._cursorPosition = this._getPositionFromEvent(e);
|
||||
this._localValue = this._getValueFromCoord(...this._cursorPosition);
|
||||
fireEvent(this, "cursor-moved", { value: this._localValue });
|
||||
});
|
||||
this._mc.on("panend", (e) => {
|
||||
if (this.disabled) return;
|
||||
this._pressed = undefined;
|
||||
this._cursorPosition = this._getPositionFromEvent(e);
|
||||
this._localValue = this._getValueFromCoord(...this._cursorPosition);
|
||||
fireEvent(this, "cursor-moved", { value: undefined });
|
||||
fireEvent(this, "value-changed", { value: this._localValue });
|
||||
});
|
||||
|
||||
this._mc.on("singletap", (e) => {
|
||||
if (this.disabled) return;
|
||||
this._cursorPosition = this._getPositionFromEvent(e);
|
||||
this._localValue = this._getValueFromCoord(...this._cursorPosition);
|
||||
fireEvent(this, "value-changed", { value: this._localValue });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _destroyListeners() {
|
||||
if (this._mc) {
|
||||
this._mc.destroy();
|
||||
this._mc = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private _resetPosition() {
|
||||
if (this.value === undefined) {
|
||||
this._cursorPosition = undefined;
|
||||
this._localValue = undefined;
|
||||
return;
|
||||
}
|
||||
this._cursorPosition = this._getCoordsFromValue(this.value);
|
||||
this._localValue = this.value;
|
||||
}
|
||||
|
||||
private get _rtl() {
|
||||
return mainWindow.document.dir === "rtl";
|
||||
}
|
||||
|
||||
private _getCoordsFromValue = (value: [number, number]): [number, number] => {
|
||||
const xValue =
|
||||
(clamp(value[0], this._minKelvin, this._maxKelvin) - this._minKelvin) /
|
||||
(this._maxKelvin - this._minKelvin);
|
||||
const x = this._rtl ? 1 - xValue : xValue;
|
||||
const y = 1 - clamp(value[1], 1, 100) / 100;
|
||||
return [x, y];
|
||||
};
|
||||
|
||||
private _getValueFromCoord = (x: number, y: number): [number, number] => {
|
||||
const xValue = this._rtl ? 1 - x : x;
|
||||
const kelvin = Math.round(
|
||||
this._minKelvin + xValue * (this._maxKelvin - this._minKelvin)
|
||||
);
|
||||
const brightness = clamp(Math.round((1 - y) * 100), 1, 100);
|
||||
return [kelvin, brightness];
|
||||
};
|
||||
|
||||
private _getPositionFromEvent = (e: HammerInput): [number, number] => {
|
||||
const boundingRect = this._pad!.getBoundingClientRect();
|
||||
const x = clamp(
|
||||
(e.center.x - boundingRect.left) / boundingRect.width,
|
||||
0,
|
||||
1
|
||||
);
|
||||
const y = clamp(
|
||||
(e.center.y - boundingRect.top) / boundingRect.height,
|
||||
0,
|
||||
1
|
||||
);
|
||||
return [x, y];
|
||||
};
|
||||
|
||||
private _generateTemperatureGradient = memoizeOne(
|
||||
(min: number, max: number) => generateColorTemperatureGradient(min, max)
|
||||
);
|
||||
|
||||
render() {
|
||||
const gradient = this._generateTemperatureGradient(
|
||||
this._minKelvin,
|
||||
this._maxKelvin
|
||||
);
|
||||
|
||||
const [x, y] = this._cursorPosition ?? [0, 0];
|
||||
|
||||
const markerScale = this._pressed
|
||||
? this._pressed === "touch"
|
||||
? "2.5"
|
||||
: "1.5"
|
||||
: "1";
|
||||
const markerOffset = this._pressed === "touch" ? "0px, -24px" : "0px, 0px";
|
||||
|
||||
let markerColor = "#ffffff";
|
||||
if (this._localValue) {
|
||||
const rgb = temperature2rgb(this._localValue[0]);
|
||||
const factor =
|
||||
MARKER_LUMINANCE_FLOOR +
|
||||
(1 - MARKER_LUMINANCE_FLOOR) * (this._localValue[1] / 100);
|
||||
markerColor = rgb2hex([
|
||||
Math.round(rgb[0] * factor),
|
||||
Math.round(rgb[1] * factor),
|
||||
Math.round(rgb[2] * factor),
|
||||
]);
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="container ${classMap({ pressed: Boolean(this._pressed) })}">
|
||||
<div
|
||||
id="pad"
|
||||
style=${styleMap({
|
||||
"background-image": `linear-gradient(to top, ${PAD_DARK_COLOR}, white), linear-gradient(to ${this._rtl ? "left" : "right"}, ${gradient})`,
|
||||
})}
|
||||
></div>
|
||||
<div
|
||||
class="cursor"
|
||||
style=${styleMap({
|
||||
left: `${x * 100}%`,
|
||||
top: `${y * 100}%`,
|
||||
visibility: this._cursorPosition ? undefined : "hidden",
|
||||
})}
|
||||
>
|
||||
<div
|
||||
class="marker"
|
||||
style=${styleMap({
|
||||
"background-color": markerColor,
|
||||
transform: `translate(${markerOffset}) scale(${markerScale})`,
|
||||
})}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
outline: none;
|
||||
}
|
||||
.container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
#pad {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
clip-path: inset(0 round var(--ha-border-radius-6xl));
|
||||
background-blend-mode: multiply;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
touch-action: none;
|
||||
}
|
||||
:host([disabled]) #pad {
|
||||
cursor: initial;
|
||||
filter: grayscale(1) opacity(0.5);
|
||||
}
|
||||
.cursor {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
.marker {
|
||||
position: absolute;
|
||||
top: -16px;
|
||||
left: -16px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: var(--ha-border-radius-circle);
|
||||
border: 2px solid white;
|
||||
box-shadow:
|
||||
0 1px 4px rgba(0, 0, 0, 0.3),
|
||||
0 1px 6px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.container:not(.pressed) .marker {
|
||||
transition:
|
||||
transform var(--ha-animation-duration-instant) ease-in-out,
|
||||
background-color var(--ha-animation-duration-instant) ease-in-out;
|
||||
}
|
||||
.container:not(.pressed) .cursor {
|
||||
transition:
|
||||
left var(--ha-animation-duration-fast) ease-in-out,
|
||||
top var(--ha-animation-duration-fast) ease-in-out;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-color-temp-brightness-picker": HaColorTempBrightnessPicker;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
import { consume, type ContextType } from "@lit/context";
|
||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||
import { LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { throttle } from "../../../../common/util/throttle";
|
||||
import "../../../../components/ha-color-temp-brightness-picker";
|
||||
import { apiContext } from "../../../../data/context";
|
||||
import { UNAVAILABLE } from "../../../../data/entity/entity";
|
||||
import type { LightEntity } from "../../../../data/light";
|
||||
import { LightColorMode } from "../../../../data/light";
|
||||
|
||||
@customElement("light-color-temp-brightness-picker")
|
||||
class LightColorTempBrightnessPicker extends LitElement {
|
||||
@state()
|
||||
@consume({ context: apiContext, subscribe: true })
|
||||
private _api!: ContextType<typeof apiContext>;
|
||||
|
||||
@property({ attribute: false }) public stateObj!: LightEntity;
|
||||
|
||||
@state() private _value?: [number, number];
|
||||
|
||||
@state() private _isInteracting?: boolean;
|
||||
|
||||
protected render() {
|
||||
if (!this.stateObj) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-color-temp-brightness-picker
|
||||
.value=${this._value}
|
||||
.minKelvin=${this.stateObj.attributes.min_color_temp_kelvin}
|
||||
.maxKelvin=${this.stateObj.attributes.max_color_temp_kelvin}
|
||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||
@cursor-moved=${this._cursorMoved}
|
||||
@value-changed=${this._valueChanged}
|
||||
>
|
||||
</ha-color-temp-brightness-picker>
|
||||
`;
|
||||
}
|
||||
|
||||
private _updateValues() {
|
||||
const stateObj = this.stateObj;
|
||||
|
||||
if (stateObj.state !== "on") {
|
||||
this._value = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
const kelvin =
|
||||
stateObj.attributes.color_mode === LightColorMode.COLOR_TEMP
|
||||
? stateObj.attributes.color_temp_kelvin
|
||||
: undefined;
|
||||
const brightness = stateObj.attributes.brightness;
|
||||
|
||||
this._value =
|
||||
kelvin != null && brightness != null
|
||||
? [kelvin, Math.max(Math.round((brightness * 100) / 255), 1)]
|
||||
: undefined;
|
||||
}
|
||||
|
||||
public willUpdate(changedProps: PropertyValues<this>) {
|
||||
super.willUpdate(changedProps);
|
||||
|
||||
if (this._isInteracting || !changedProps.has("stateObj")) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateValues();
|
||||
}
|
||||
|
||||
private _cursorMoved(ev: CustomEvent) {
|
||||
const value = ev.detail.value;
|
||||
|
||||
this._isInteracting = value !== undefined;
|
||||
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._value = value;
|
||||
|
||||
this._throttleUpdateLight();
|
||||
}
|
||||
|
||||
private _throttleUpdateLight = throttle(() => {
|
||||
this._updateLight();
|
||||
}, 500);
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
const value = ev.detail.value;
|
||||
|
||||
if (value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._value = value;
|
||||
|
||||
this._throttleUpdateLight.cancel();
|
||||
this._updateLight();
|
||||
}
|
||||
|
||||
private _updateLight() {
|
||||
const [color_temp_kelvin, brightness_pct] = this._value!;
|
||||
|
||||
this._api.callService("light", "turn_on", {
|
||||
entity_id: this.stateObj!.entity_id,
|
||||
color_temp_kelvin,
|
||||
brightness_pct,
|
||||
});
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
css`
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
ha-color-temp-brightness-picker {
|
||||
height: 45vh;
|
||||
max-height: 320px;
|
||||
min-height: 200px;
|
||||
aspect-ratio: 1;
|
||||
max-width: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"light-color-temp-brightness-picker": LightColorTempBrightnessPicker;
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,13 @@ import "../components/ha-more-info-state-header";
|
||||
import "../components/lights/ha-favorite-color-button";
|
||||
import "../components/lights/ha-more-info-light-favorite-colors";
|
||||
import "../components/lights/light-color-rgb-picker";
|
||||
import "../components/lights/light-color-temp-brightness-picker";
|
||||
import "../components/lights/light-color-temp-picker";
|
||||
import { moreInfoControlStyle } from "../components/more-info-control-style";
|
||||
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
|
||||
|
||||
type MainControl = "brightness" | "color_temp" | "color";
|
||||
type MainControl =
|
||||
"brightness" | "color_temp" | "color" | "color_temp_brightness";
|
||||
|
||||
@customElement("more-info-light")
|
||||
class MoreInfoLight extends LitElement {
|
||||
@@ -78,12 +80,32 @@ class MoreInfoLight extends LitElement {
|
||||
.attributeValue=${value}
|
||||
></ha-attribute-icon>`;
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues<this>): void {
|
||||
super.willUpdate(changedProps);
|
||||
if (
|
||||
changedProps.has("stateObj") &&
|
||||
this.stateObj &&
|
||||
this._mainControl === "color_temp_brightness" &&
|
||||
!this._supportsDualAxis(this.stateObj)
|
||||
) {
|
||||
this._mainControl = "brightness";
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues<this>): void {
|
||||
if (changedProps.has("stateObj")) {
|
||||
this._effect = this.stateObj?.attributes.effect;
|
||||
}
|
||||
}
|
||||
|
||||
private _supportsDualAxis(stateObj: LightEntity): boolean {
|
||||
return (
|
||||
lightSupportsBrightness(stateObj) &&
|
||||
lightSupportsColorMode(stateObj, LightColorMode.COLOR_TEMP) &&
|
||||
!lightSupportsColor(stateObj)
|
||||
);
|
||||
}
|
||||
|
||||
private _setMainControl(ev: any) {
|
||||
ev.stopPropagation();
|
||||
this._mainControl = ev.currentTarget.control;
|
||||
@@ -128,6 +150,8 @@ class MoreInfoLight extends LitElement {
|
||||
LightEntityFeature.EFFECT
|
||||
);
|
||||
|
||||
const supportsDualAxis = this._supportsDualAxis(this.stateObj);
|
||||
|
||||
const showFavoriteColors = Boolean(
|
||||
this.entry &&
|
||||
(this.editMode ||
|
||||
@@ -183,6 +207,17 @@ class MoreInfoLight extends LitElement {
|
||||
`
|
||||
: nothing
|
||||
}
|
||||
${
|
||||
supportsDualAxis &&
|
||||
this._mainControl === "color_temp_brightness"
|
||||
? html`
|
||||
<light-color-temp-brightness-picker
|
||||
.stateObj=${this.stateObj}
|
||||
>
|
||||
</light-color-temp-brightness-picker>
|
||||
`
|
||||
: nothing
|
||||
}
|
||||
<ha-icon-button-group>
|
||||
${
|
||||
supportsBrightness
|
||||
@@ -254,6 +289,26 @@ class MoreInfoLight extends LitElement {
|
||||
`
|
||||
: nothing
|
||||
}
|
||||
${
|
||||
supportsDualAxis
|
||||
? html`
|
||||
<ha-icon-button-toggle
|
||||
border-only
|
||||
.selected=${
|
||||
this._mainControl === "color_temp_brightness"
|
||||
}
|
||||
.disabled=${this.stateObj!.state === UNAVAILABLE}
|
||||
.label=${this._localize(
|
||||
"ui.dialogs.more_info_control.light.color_temp_brightness"
|
||||
)}
|
||||
.control=${"color_temp_brightness"}
|
||||
@click=${this._setMainControl}
|
||||
>
|
||||
<span class="wheel color-temp-brightness"></span>
|
||||
</ha-icon-button-toggle>
|
||||
`
|
||||
: nothing
|
||||
}
|
||||
${
|
||||
supportsWhite
|
||||
? html`
|
||||
@@ -384,6 +439,15 @@ class MoreInfoLight extends LitElement {
|
||||
rgb(255, 160, 0) 100%
|
||||
);
|
||||
}
|
||||
.wheel.color-temp-brightness {
|
||||
background-image:
|
||||
linear-gradient(to top, rgb(102, 102, 102), white),
|
||||
linear-gradient(90deg, rgb(255, 160, 0), white, rgb(166, 209, 255));
|
||||
background-blend-mode: multiply;
|
||||
/* Mask the blended result as one layer; radius clipping bleeds
|
||||
per background layer at the edge in Gecko */
|
||||
clip-path: inset(0 round var(--ha-border-radius-circle));
|
||||
}
|
||||
*[disabled] .wheel {
|
||||
filter: grayscale(1) opacity(0.5);
|
||||
}
|
||||
|
||||
@@ -1816,6 +1816,7 @@
|
||||
"toggle": "Toggle",
|
||||
"color": "Color",
|
||||
"color_temp": "Temperature",
|
||||
"color_temp_brightness": "Temperature and brightness",
|
||||
"set_white": "Set white",
|
||||
"select_effect": "Select effect",
|
||||
"color_picker": {
|
||||
|
||||
Reference in New Issue
Block a user