diff --git a/gallery/src/pages/components/ha-control-circular-slider.ts b/gallery/src/pages/components/ha-control-circular-slider.ts
index 791fff7829..8c6fca52d4 100644
--- a/gallery/src/pages/components/ha-control-circular-slider.ts
+++ b/gallery/src/pages/components/ha-control-circular-slider.ts
@@ -10,23 +10,23 @@ export class DemoHaCircularSlider extends LitElement {
private current = 22;
@state()
- private value = 19;
+ private low = 19;
@state()
private high = 25;
@state()
- private changingValue?: number;
+ private changingLow?: number;
@state()
private changingHigh?: number;
- private _valueChanged(ev) {
- this.value = ev.detail.value;
+ private _lowChanged(ev) {
+ this.low = ev.detail.value;
}
- private _valueChanging(ev) {
- this.changingValue = ev.detail.value;
+ private _lowChanging(ev) {
+ this.changingLow = ev.detail.value;
}
private _highChanged(ev) {
@@ -63,19 +63,40 @@ export class DemoHaCircularSlider extends LitElement {
Single
- Value: ${this.value} °C
+ Low: ${this.low} °C
Changing:
- ${this.changingValue != null ? `${this.changingValue} °C` : "-"}
+ ${this.changingLow != null ? `${this.changingLow} °C` : "-"}
+
+
+
+
- Low value: ${this.value} °C
+ Low value: ${this.low} °C
Low changing:
- ${this.changingValue != null ? `${this.changingValue} °C` : "-"}
+ ${this.changingLow != null ? `${this.changingLow} °C` : "-"}
High value: ${this.high} °C
@@ -132,6 +153,10 @@ export class DemoHaCircularSlider extends LitElement {
--control-circular-slider-background: #ff9800;
--control-circular-slider-background-opacity: 0.3;
}
+ ha-control-circular-slider[inverted] {
+ --control-circular-slider-color: #2196f3;
+ --control-circular-slider-background: #2196f3;
+ }
ha-control-circular-slider[dual] {
--control-circular-slider-high-color: #2196f3;
--control-circular-slider-low-color: #ff9800;
diff --git a/src/components/ha-control-circular-slider.ts b/src/components/ha-control-circular-slider.ts
index 7b7472ddbc..97bc2ea566 100644
--- a/src/components/ha-control-circular-slider.ts
+++ b/src/components/ha-control-circular-slider.ts
@@ -68,6 +68,9 @@ export class HaControlCircularSlider extends LitElement {
@property({ type: Boolean })
public dual?: boolean;
+ @property({ type: Boolean, reflect: true })
+ public inverted?: boolean;
+
@property({ type: String })
public label?: string;
@@ -80,15 +83,15 @@ export class HaControlCircularSlider extends LitElement {
@property({ type: Number })
public value?: number;
- @property({ type: Number })
- public current?: number;
-
@property({ type: Number })
public low?: number;
@property({ type: Number })
public high?: number;
+ @property({ type: Number })
+ public current?: number;
+
@property({ type: Number })
public step = 1;
@@ -98,6 +101,15 @@ export class HaControlCircularSlider extends LitElement {
@property({ type: Number })
public max = 100;
+ @state()
+ public _localValue?: number = this.value;
+
+ @state()
+ public _localLow?: number = this.low;
+
+ @state()
+ public _localHigh?: number = this.high;
+
@state()
public _activeSlider?: ActiveSlider;
@@ -120,17 +132,36 @@ export class HaControlCircularSlider extends LitElement {
private _boundedValue(value: number) {
const min =
- this._activeSlider === "high" ? Math.min(this.low ?? this.max) : this.min;
+ this._activeSlider === "high"
+ ? Math.min(this._localLow ?? this.max)
+ : this.min;
const max =
- this._activeSlider === "low" ? Math.max(this.high ?? this.min) : this.max;
+ this._activeSlider === "low"
+ ? Math.max(this._localHigh ?? this.min)
+ : this.max;
return Math.min(Math.max(value, min), max);
}
- protected firstUpdated(changedProperties: PropertyValues): void {
- super.firstUpdated(changedProperties);
+ protected firstUpdated(changedProps: PropertyValues): void {
+ super.firstUpdated(changedProps);
this._setupListeners();
}
+ protected updated(changedProps: PropertyValues): void {
+ super.updated(changedProps);
+ if (!this._activeSlider) {
+ if (changedProps.has("value")) {
+ this._localValue = this.value;
+ }
+ if (changedProps.has("low")) {
+ this._localLow = this.low;
+ }
+ if (changedProps.has("high")) {
+ this._localHigh = this.high;
+ }
+ }
+ }
+
connectedCallback(): void {
super.connectedCallback();
this._setupListeners();
@@ -164,8 +195,8 @@ export class HaControlCircularSlider extends LitElement {
private _findActiveSlider(value: number): ActiveSlider {
if (!this.dual) return "value";
- const low = Math.max(this.low ?? this.min, this.min);
- const high = Math.min(this.high ?? this.max, this.max);
+ const low = Math.max(this._localLow ?? this.min, this.min);
+ const high = Math.min(this._localHigh ?? this.max, this.max);
if (low >= value) {
return "low";
}
@@ -178,13 +209,29 @@ export class HaControlCircularSlider extends LitElement {
}
private _setActiveValue(value: number) {
- if (!this._activeSlider) return;
- this[this._activeSlider] = value;
+ switch (this._activeSlider) {
+ case "high":
+ this._localHigh = value;
+ break;
+ case "low":
+ this._localLow = value;
+ break;
+ case "value":
+ this._localValue = value;
+ break;
+ }
}
private _getActiveValue(): number | undefined {
- if (!this._activeSlider) return undefined;
- return this[this._activeSlider];
+ switch (this._activeSlider) {
+ case "high":
+ return this._localHigh;
+ case "low":
+ return this._localLow;
+ case "value":
+ return this._localValue;
+ }
+ return undefined;
}
_setupListeners() {
@@ -235,6 +282,7 @@ export class HaControlCircularSlider extends LitElement {
const raw = this._percentageToValue(percentage);
const bounded = this._boundedValue(raw);
const stepped = this._steppedValue(bounded);
+ this._setActiveValue(stepped);
if (this._activeSlider) {
fireEvent(this, `${this._activeSlider}-changing`, {
value: undefined,
@@ -340,23 +388,41 @@ export class HaControlCircularSlider extends LitElement {
}
}
+ private _strokeDashArc(
+ percentage: number,
+ inverted?: boolean
+ ): [string, string] {
+ const maxRatio = MAX_ANGLE / 360;
+ const f = RADIUS * 2 * Math.PI;
+ if (inverted) {
+ const arcLength = (1 - percentage) * f * maxRatio;
+ const strokeDasharray = `${arcLength} ${f - arcLength}`;
+ const strokeDashOffset = `${arcLength + f * (1 - maxRatio)}`;
+ return [strokeDasharray, strokeDashOffset];
+ }
+ const arcLength = percentage * f * maxRatio;
+ const strokeDasharray = `${arcLength} ${f - arcLength}`;
+ const strokeDashOffset = "0";
+ return [strokeDasharray, strokeDashOffset];
+ }
+
protected render(): TemplateResult {
const trackPath = arc({ x: 0, y: 0, start: 0, end: MAX_ANGLE, r: RADIUS });
- const maxRatio = MAX_ANGLE / 360;
-
- const f = RADIUS * 2 * Math.PI;
- const lowValue = this.dual ? this.low : this.value;
- const highValue = this.high;
+ const lowValue = this.dual ? this._localLow : this._localValue;
+ const highValue = this._localHigh;
const lowPercentage = this._valueToPercentage(lowValue ?? this.min);
const highPercentage = this._valueToPercentage(highValue ?? this.max);
- const lowArcLength = lowPercentage * f * maxRatio;
- const lowStrokeDasharray = `${lowArcLength} ${f - lowArcLength}`;
+ const [lowStrokeDasharray, lowStrokeDashOffset] = this._strokeDashArc(
+ lowPercentage,
+ this.inverted
+ );
- const highArcLength = (1 - highPercentage) * f * maxRatio;
- const highStrokeDasharray = `${highArcLength} ${f - highArcLength}`;
- const highStrokeDashOffset = `${highArcLength + f * (1 - maxRatio)}`;
+ const [highStrokeDasharray, highStrokeDashOffset] = this._strokeDashArc(
+ highPercentage,
+ true
+ );
const currentPercentage = this._valueToPercentage(this.current ?? 0);
const currentAngle = currentPercentage * MAX_ANGLE;
@@ -381,27 +447,31 @@ export class HaControlCircularSlider extends LitElement {
-
- ${this.dual
+ ${lowValue != null
+ ? svg`
+
+ `
+ : nothing}
+ ${this.dual && highValue != null
? svg`