diff --git a/gallery/src/pages/components/ha-temp-color-picker.markdown b/gallery/src/pages/components/ha-temp-color-picker.markdown
deleted file mode 100644
index 3221e55ee8..0000000000
--- a/gallery/src/pages/components/ha-temp-color-picker.markdown
+++ /dev/null
@@ -1,3 +0,0 @@
----
-title: Temp Color Picker
----
diff --git a/gallery/src/pages/components/ha-temp-color-picker.ts b/gallery/src/pages/components/ha-temp-color-picker.ts
deleted file mode 100644
index d924e745bf..0000000000
--- a/gallery/src/pages/components/ha-temp-color-picker.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-import "../../../../src/components/ha-temp-color-picker";
-
-import { css, html, LitElement, TemplateResult } from "lit";
-import { customElement, state } from "lit/decorators";
-
-import "../../../../src/components/ha-card";
-import "../../../../src/components/ha-slider";
-
-@customElement("demo-components-ha-temp-color-picker")
-export class DemoHaTempColorPicker extends LitElement {
- @state()
- min = 3000;
-
- @state()
- max = 7000;
-
- @state()
- value = 4000;
-
- @state()
- liveValue?: number;
-
- private _minChanged(ev) {
- this.min = Number(ev.target.value);
- }
-
- private _maxChanged(ev) {
- this.max = Number(ev.target.value);
- }
-
- private _valueChanged(ev) {
- this.value = Number(ev.target.value);
- }
-
- private _tempColorCursor(ev) {
- this.liveValue = ev.detail.value;
- }
-
- private _tempColorChanged(ev) {
- this.value = ev.detail.value;
- }
-
- protected render(): TemplateResult {
- return html`
-
-
-
${this.liveValue ?? this.value} K
-
-
Min temp : ${this.min} K
-
-
-
Max temp : ${this.max} K
-
-
-
Value : ${this.value} K
-
-
-
-
- `;
- }
-
- static get styles() {
- return css`
- ha-card {
- max-width: 600px;
- margin: 24px auto;
- }
- .card-content {
- display: flex;
- align-items: center;
- flex-direction: column;
- }
- ha-temp-color-picker {
- width: 400px;
- }
- .value {
- font-size: 22px;
- font-weight: bold;
- margin: 0 0 12px 0;
- }
- `;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "demo-components-ha-temp-color-picker": DemoHaTempColorPicker;
- }
-}
diff --git a/src/components/ha-hs-color-picker.ts b/src/components/ha-hs-color-picker.ts
index cef11cfc38..f492be2d7c 100644
--- a/src/components/ha-hs-color-picker.ts
+++ b/src/components/ha-hs-color-picker.ts
@@ -7,6 +7,12 @@ import { hsv2rgb, rgb2hex } from "../common/color/convert-color";
import { rgbw2rgb, rgbww2rgb } from "../common/color/convert-light-color";
import { fireEvent } from "../common/dom/fire_event";
+declare global {
+ interface HASSDomEvents {
+ "cursor-moved": { value?: any };
+ }
+}
+
function xy2polar(x: number, y: number) {
const r = Math.sqrt(x * x + y * y);
const phi = Math.atan2(y, x);
diff --git a/src/components/ha-temp-color-picker.ts b/src/components/ha-temp-color-picker.ts
deleted file mode 100644
index ce471c207f..0000000000
--- a/src/components/ha-temp-color-picker.ts
+++ /dev/null
@@ -1,440 +0,0 @@
-import { DIRECTION_ALL, Manager, Pan, Tap } from "@egjs/hammerjs";
-import { LitElement, PropertyValues, css, html, svg } from "lit";
-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";
-
-const SAFE_ZONE_FACTOR = 0.9;
-
-declare global {
- interface HASSDomEvents {
- "cursor-moved": { value?: any };
- }
-}
-
-const A11Y_KEY_CODES = new Set([
- "ArrowRight",
- "ArrowUp",
- "ArrowLeft",
- "ArrowDown",
- "PageUp",
- "PageDown",
- "Home",
- "End",
-]);
-
-function xy2polar(x: number, y: number) {
- const r = Math.sqrt(x * x + y * y);
- const phi = Math.atan2(y, x);
- return [r, phi];
-}
-
-function polar2xy(r: number, phi: number) {
- const x = Math.cos(phi) * r;
- const y = Math.sin(phi) * r;
- return [x, y];
-}
-
-function drawColorWheel(
- ctx: CanvasRenderingContext2D,
- minTemp: number,
- maxTemp: number
-) {
- ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
- const radius = ctx.canvas.width / 2;
-
- const min = Math.max(minTemp, 2000);
- const max = Math.min(maxTemp, 40000);
-
- for (let y = -radius; y < radius; y += 1) {
- const x = radius * Math.sqrt(1 - (y / radius) ** 2);
-
- const fraction = (y / (radius * SAFE_ZONE_FACTOR) + 1) / 2;
-
- const temperature = Math.max(
- Math.min(min + fraction * (max - min), max),
- min
- );
-
- const color = rgb2hex(temperature2rgb(temperature));
-
- ctx.fillStyle = color;
- ctx.fillRect(radius - x, radius + y - 0.5, 2 * x, 2);
- ctx.fill();
- }
-}
-
-@customElement("ha-temp-color-picker")
-class HaTempColorPicker extends LitElement {
- @property({ type: Boolean, reflect: true })
- public disabled = false;
-
- @property({ type: Number, attribute: false })
- public renderSize?: number;
-
- @property({ type: Number })
- public value?: number;
-
- @property({ type: Number })
- public min = DEFAULT_MIN_KELVIN;
-
- @property({ type: Number })
- public max = DEFAULT_MAX_KELVIN;
-
- @query("#canvas") private _canvas!: HTMLCanvasElement;
-
- private _mc?: HammerManager;
-
- @state()
- private _pressed?: string;
-
- @state()
- private _cursorPosition?: [number, number];
-
- @state()
- private _localValue?: number;
-
- protected firstUpdated(changedProps: PropertyValues): void {
- super.firstUpdated(changedProps);
- this._setupListeners();
- this._generateColorWheel();
- this.setAttribute("role", "slider");
- this.setAttribute("aria-orientation", "vertical");
- if (!this.hasAttribute("tabindex")) {
- this.setAttribute("tabindex", "0");
- }
- }
-
- private _generateColorWheel() {
- const ctx = this._canvas.getContext("2d")!;
- drawColorWheel(ctx, this.min, this.max);
- }
-
- connectedCallback(): void {
- super.connectedCallback();
- this._setupListeners();
- }
-
- disconnectedCallback(): void {
- super.disconnectedCallback();
- this._destroyListeners();
- }
-
- protected updated(changedProps: PropertyValues): void {
- super.updated(changedProps);
- if (changedProps.has("_localValue")) {
- this.setAttribute("aria-valuenow", this._localValue?.toString() ?? "");
- }
- if (changedProps.has("min") || changedProps.has("max")) {
- this._generateColorWheel();
- this._resetPosition();
- }
- if (changedProps.has("min")) {
- this.setAttribute("aria-valuemin", this.min.toString());
- }
- if (changedProps.has("max")) {
- this.setAttribute("aria-valuemax", this.max.toString());
- }
- if (changedProps.has("value")) {
- if (this._localValue !== this.value) {
- this._resetPosition();
- }
- }
- }
-
- private _setupListeners() {
- if (this._canvas && !this._mc) {
- this._mc = new Manager(this._canvas);
- this._mc.add(
- new Pan({
- direction: DIRECTION_ALL,
- enable: true,
- threshold: 0,
- })
- );
-
- 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 });
- });
-
- this.addEventListener("keydown", this._handleKeyDown);
- this.addEventListener("keyup", this._handleKeyUp);
- }
- }
-
- private _resetPosition() {
- 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 =
- Math.sign(currentX) * Math.min(Math.sqrt(1 - y ** 2), Math.abs(currentX));
- this._cursorPosition = [x, y];
- this._localValue = this.value;
- }
-
- private _getCoordsFromValue = (temperature: number): [number, number] => {
- if (this.value === this.min) {
- return [0, -1];
- }
- if (this.value === this.max) {
- return [0, 1];
- }
- const fraction = (temperature - this.min) / (this.max - this.min);
- const y = (2 * fraction - 1) * SAFE_ZONE_FACTOR;
- return [0, y];
- };
-
- private _getValueFromCoord = (_x: number, y: number): number => {
- const fraction = (y / SAFE_ZONE_FACTOR + 1) / 2;
- const temperature = Math.max(
- Math.min(this.min + fraction * (this.max - this.min), this.max),
- this.min
- );
- return Math.round(temperature);
- };
-
- private _getPositionFromEvent = (e: HammerInput): [number, number] => {
- const x = e.center.x;
- const y = e.center.y;
- const boundingRect = e.target.getBoundingClientRect();
- const offsetX = boundingRect.left;
- const offsetY = boundingRect.top;
- const maxX = e.target.clientWidth;
- const maxY = e.target.clientHeight;
-
- const _x = (2 * (x - offsetX)) / maxX - 1;
- const _y = (2 * (y - offsetY)) / maxY - 1;
-
- const [r, phi] = xy2polar(_x, _y);
- const [__x, __y] = polar2xy(Math.min(1, r), phi);
- return [__x, __y];
- };
-
- private _destroyListeners() {
- if (this._mc) {
- this._mc.destroy();
- this._mc = undefined;
- }
- this.removeEventListener("keydown", this._handleKeyDown);
- this.removeEventListener("keyup", this._handleKeyDown);
- }
-
- _handleKeyDown(e: KeyboardEvent) {
- if (!A11Y_KEY_CODES.has(e.code)) return;
- e.preventDefault();
-
- const step = 1;
- const tenPercentStep = Math.max(step, (this.max - this.min) / 10);
- const currentValue =
- this._localValue ?? Math.round((this.max + this.min) / 2);
- switch (e.code) {
- case "ArrowRight":
- case "ArrowUp":
- this._localValue = Math.round(Math.min(currentValue + step, this.max));
- break;
- case "ArrowLeft":
- case "ArrowDown":
- this._localValue = Math.round(Math.max(currentValue - step, this.min));
- break;
- case "PageUp":
- this._localValue = Math.round(
- Math.min(currentValue + tenPercentStep, this.max)
- );
- break;
- case "PageDown":
- this._localValue = Math.round(
- Math.max(currentValue - tenPercentStep, this.min)
- );
- break;
- case "Home":
- this._localValue = this.min;
- break;
- case "End":
- this._localValue = this.max;
- break;
- }
- if (this._localValue != null) {
- const [_, y] = this._getCoordsFromValue(this._localValue);
- const currentX = this._cursorPosition?.[0] ?? 0;
- const x =
- Math.sign(currentX) *
- Math.min(Math.sqrt(1 - y ** 2), Math.abs(currentX));
- this._cursorPosition = [x, y];
- fireEvent(this, "cursor-moved", { value: this._localValue });
- }
- }
-
- _handleKeyUp(e: KeyboardEvent) {
- if (!A11Y_KEY_CODES.has(e.code)) return;
- e.preventDefault();
- this.value = this._localValue;
- fireEvent(this, "value-changed", { value: this._localValue });
- }
-
- render() {
- const size = this.renderSize || 400;
- const canvasSize = size * window.devicePixelRatio;
-
- const rgb = temperature2rgb(
- this._localValue ?? Math.round((this.max + this.min) / 2)
- );
-
- const [x, y] = this._cursorPosition ?? [0, 0];
-
- const cx = ((x + 1) * size) / 2;
- const cy = ((y + 1) * size) / 2;
-
- const markerPosition = `${cx}px, ${cy}px`;
- const markerScale = this._pressed
- ? this._pressed === "touch"
- ? "2.5"
- : "1.5"
- : "1";
- const markerOffset =
- this._pressed === "touch" ? `0px, -${size / 16}px` : "0px, 0px";
-
- return html`
-
-
-
-
- `;
- }
-
- renderSVGFilter() {
- return svg`
-
-
-
-
- `;
- }
-
- static get styles() {
- return css`
- :host {
- display: block;
- outline: none;
- }
- .container {
- position: relative;
- width: 100%;
- height: 100%;
- display: flex;
- }
- canvas {
- width: 100%;
- height: 100%;
- object-fit: contain;
- border-radius: 50%;
- transition: box-shadow 180ms ease-in-out;
- cursor: pointer;
- }
- :host(:focus-visible) canvas {
- box-shadow: 0 0 0 2px rgb(255, 160, 0);
- }
- svg {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- pointer-events: none;
- }
- circle {
- fill: black;
- stroke: white;
- stroke-width: 2;
- filter: url(#marker-shadow);
- }
- .container:not(.pressed) circle {
- transition:
- transform 100ms ease-in-out,
- fill 100ms ease-in-out;
- }
- .container:not(.pressed) .cursor {
- transition: transform 200ms ease-in-out;
- }
- `;
- }
-}
-
-declare global {
- interface HTMLElementTagNameMap {
- "ha-temp-color-picker": HaTempColorPicker;
- }
-}
diff --git a/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts b/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts
index 90b18f4223..0a66aabcf6 100644
--- a/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts
+++ b/src/dialogs/more-info/components/lights/light-color-rgb-picker.ts
@@ -26,7 +26,6 @@ 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 {
getLightCurrentModeRgbColor,
LightColor,
diff --git a/src/dialogs/more-info/components/lights/light-color-temp-picker.ts b/src/dialogs/more-info/components/lights/light-color-temp-picker.ts
index cd69b7ba7d..0331d24b31 100644
--- a/src/dialogs/more-info/components/lights/light-color-temp-picker.ts
+++ b/src/dialogs/more-info/components/lights/light-color-temp-picker.ts
@@ -1,25 +1,31 @@
import {
- css,
CSSResultGroup,
- html,
LitElement,
- nothing,
PropertyValues,
+ css,
+ html,
+ nothing,
} from "lit";
import { customElement, property, state } from "lit/decorators";
+import { styleMap } from "lit/directives/style-map";
+import memoizeOne from "memoize-one";
+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 { stateColorCss } from "../../../../common/entity/state_color";
import { throttle } from "../../../../common/util/throttle";
-import "../../../../components/ha-temp-color-picker";
+import "../../../../components/ha-control-slider";
+import { UNAVAILABLE } from "../../../../data/entity";
import {
LightColor,
LightColorMode,
LightEntity,
} from "../../../../data/light";
import { HomeAssistant } from "../../../../types";
-import {
- DEFAULT_MAX_KELVIN,
- DEFAULT_MIN_KELVIN,
-} from "../../../../common/color/convert-light-color";
declare global {
interface HASSDomEvents {
@@ -28,6 +34,26 @@ declare global {
}
}
+export const generateColorTemperatureGradient = (min: number, max: number) => {
+ const count = 10;
+
+ const gradient: [number, string][] = [];
+
+ const step = (max - min) / count;
+ const percentageStep = 1 / count;
+
+ for (let i = 0; i < count + 1; i++) {
+ const value = min + step * i;
+
+ const hex = rgb2hex(temperature2rgb(value));
+ gradient.push([percentageStep * i, hex]);
+ }
+
+ return gradient
+ .map(([stop, color]) => `${color} ${(stop as number) * 100}%`)
+ .join(", ");
+};
+
@customElement("light-color-temp-picker")
class LightColorTempPicker extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -46,18 +72,36 @@ class LightColorTempPicker extends LitElement {
const maxKelvin =
this.stateObj.attributes.max_color_temp_kelvin ?? DEFAULT_MAX_KELVIN;
+ const gradient = this._generateTemperatureGradient(minKelvin!, maxKelvin);
+ const color = stateColorCss(this.stateObj);
+
return html`
-
-
+
`;
}
+ private _generateTemperatureGradient = memoizeOne(
+ (min: number, max: number) => generateColorTemperatureGradient(min, max)
+ );
+
public _updateSliderValues() {
const stateObj = this.stateObj;
@@ -138,10 +182,18 @@ class LightColorTempPicker extends LitElement {
flex-direction: column;
}
- ha-temp-color-picker {
+ ha-control-slider {
height: 45vh;
max-height: 320px;
min-height: 200px;
+ --control-slider-thickness: 100px;
+ --control-slider-border-radius: 24px;
+ --control-slider-color: var(--primary-color);
+ --control-slider-background: -webkit-linear-gradient(
+ top,
+ var(--gradient)
+ );
+ --control-slider-background-opacity: 1;
}
`,
];
diff --git a/src/panels/lovelace/tile-features/hui-light-color-temp-tile-feature.ts b/src/panels/lovelace/tile-features/hui-light-color-temp-tile-feature.ts
index 43f16a4655..2c35e33eae 100644
--- a/src/panels/lovelace/tile-features/hui-light-color-temp-tile-feature.ts
+++ b/src/panels/lovelace/tile-features/hui-light-color-temp-tile-feature.ts
@@ -3,17 +3,16 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
-import { rgb2hex } from "../../../common/color/convert-color";
import {
DEFAULT_MAX_KELVIN,
DEFAULT_MIN_KELVIN,
- temperature2rgb,
} from "../../../common/color/convert-light-color";
import { computeDomain } from "../../../common/entity/compute_domain";
import { stateActive } from "../../../common/entity/state_active";
import "../../../components/ha-control-slider";
import { UNAVAILABLE } from "../../../data/entity";
import { LightColorMode, lightSupportsColorMode } from "../../../data/light";
+import { generateColorTemperatureGradient } from "../../../dialogs/more-info/components/lights/light-color-temp-picker";
import { HomeAssistant } from "../../../types";
import { LovelaceTileFeature } from "../types";
import { LightColorTempTileFeatureConfig } from "./types";
@@ -70,7 +69,7 @@ class HuiLightColorTempTileFeature
const maxKelvin =
this.stateObj.attributes.max_color_temp_kelvin ?? DEFAULT_MAX_KELVIN;
- const gradient = this.generateTemperatureGradient(minKelvin!, maxKelvin);
+ const gradient = this._generateTemperatureGradient(minKelvin!, maxKelvin);
return html`
@@ -91,26 +90,8 @@ class HuiLightColorTempTileFeature
`;
}
- private generateTemperatureGradient = memoizeOne(
- (min: number, max: number) => {
- const count = 10;
-
- const gradient: [number, string][] = [];
-
- const step = (max - min) / count;
- const percentageStep = 1 / count;
-
- for (let i = 0; i < count + 1; i++) {
- const value = min + step * i;
-
- const hex = rgb2hex(temperature2rgb(value));
- gradient.push([percentageStep * i, hex]);
- }
-
- return gradient
- .map(([stop, color]) => `${color} ${(stop as number) * 100}%`)
- .join(", ");
- }
+ private _generateTemperatureGradient = memoizeOne(
+ (min: number, max: number) => generateColorTemperatureGradient(min, max)
);
private _valueChanged(ev: CustomEvent) {