Reduce sensitivity of the circular slider on touch devices (#18921)

This commit is contained in:
Paul Bottein 2023-12-06 14:13:19 +01:00 committed by GitHub
parent 15becf9ef6
commit b4ab0fc10b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 86 additions and 10 deletions

View File

@ -2,6 +2,7 @@ import {
DIRECTION_ALL, DIRECTION_ALL,
Manager, Manager,
Pan, Pan,
Press,
Tap, Tap,
TouchMouseInput, TouchMouseInput,
} from "@egjs/hammerjs"; } from "@egjs/hammerjs";
@ -108,6 +109,9 @@ export class HaControlCircularSlider extends LitElement {
@property({ type: Number }) @property({ type: Number })
public max = 100; public max = 100;
@property({ type: Boolean, attribute: "prevent-interaction-on-scroll" })
public preventInteractionOnScroll?: boolean;
@state() @state()
public _localValue?: number = this.value; public _localValue?: number = this.value;
@ -246,16 +250,62 @@ export class HaControlCircularSlider extends LitElement {
this._mc = new Manager(this._interaction, { this._mc = new Manager(this._interaction, {
inputClass: TouchMouseInput, inputClass: TouchMouseInput,
}); });
const pressToActivate =
this.preventInteractionOnScroll && "ontouchstart" in window;
// If press to activate is true, a 60ms press is required to activate the slider
this._mc.add( this._mc.add(
new Pan({ new Press({
direction: DIRECTION_ALL, enable: pressToActivate,
enable: true, pointers: 1,
threshold: 0, time: 60,
}) })
); );
const panRecognizer = new Pan({
direction: DIRECTION_ALL,
enable: !pressToActivate,
threshold: 0,
});
this._mc.add(panRecognizer);
this._mc.add(new Tap({ event: "singletap" })); this._mc.add(new Tap({ event: "singletap" }));
this._mc.on("press", (e) => {
e.srcEvent.stopPropagation();
e.srcEvent.preventDefault();
if (this.disabled || this.readonly) return;
const percentage = this._getPercentageFromEvent(e);
const raw = this._percentageToValue(percentage);
this._activeSlider = this._findActiveSlider(raw);
const bounded = this._boundedValue(raw);
this._setActiveValue(bounded);
const stepped = this._steppedValue(bounded);
if (this._activeSlider) {
fireEvent(this, `${this._activeSlider}-changing`, { value: stepped });
}
panRecognizer.set({ enable: true });
});
this._mc.on("pressup", (e) => {
e.srcEvent.stopPropagation();
e.srcEvent.preventDefault();
const percentage = this._getPercentageFromEvent(e);
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,
});
fireEvent(this, `${this._activeSlider}-changed`, { value: stepped });
}
this._activeSlider = undefined;
});
this._mc.on("pan", (e) => { this._mc.on("pan", (e) => {
e.srcEvent.stopPropagation(); e.srcEvent.stopPropagation();
e.srcEvent.preventDefault(); e.srcEvent.preventDefault();
@ -271,6 +321,9 @@ export class HaControlCircularSlider extends LitElement {
this._mc.on("pancancel", () => { this._mc.on("pancancel", () => {
if (this.disabled || this.readonly) return; if (this.disabled || this.readonly) return;
this._activeSlider = undefined; this._activeSlider = undefined;
if (pressToActivate) {
panRecognizer.set({ enable: false });
}
}); });
this._mc.on("panmove", (e) => { this._mc.on("panmove", (e) => {
if (this.disabled || this.readonly) return; if (this.disabled || this.readonly) return;
@ -297,6 +350,9 @@ export class HaControlCircularSlider extends LitElement {
fireEvent(this, `${this._activeSlider}-changed`, { value: stepped }); fireEvent(this, `${this._activeSlider}-changed`, { value: stepped });
} }
this._activeSlider = undefined; this._activeSlider = undefined;
if (pressToActivate) {
panRecognizer.set({ enable: false });
}
}); });
this._mc.on("singletap", (e) => { this._mc.on("singletap", (e) => {
if (this.disabled || this.readonly) return; if (this.disabled || this.readonly) return;
@ -315,6 +371,9 @@ export class HaControlCircularSlider extends LitElement {
this._lastSlider = this._activeSlider; this._lastSlider = this._activeSlider;
this.shadowRoot?.getElementById("#slider")?.focus(); this.shadowRoot?.getElementById("#slider")?.focus();
this._activeSlider = undefined; this._activeSlider = undefined;
if (pressToActivate) {
panRecognizer.set({ enable: false });
}
}); });
} }
} }

View File

@ -127,6 +127,7 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
<ha-card> <ha-card>
<p class="title">${name}</p> <p class="title">${name}</p>
<ha-state-control-humidifier-humidity <ha-state-control-humidifier-humidity
prevent-interaction-on-scroll
show-current show-current
.hass=${this.hass} .hass=${this.hass}
.stateObj=${stateObj} .stateObj=${stateObj}
@ -183,7 +184,6 @@ export class HuiHumidifierCard extends LitElement implements LovelaceCard {
max-width: 344px; /* 12px + 12px + 320px */ max-width: 344px; /* 12px + 12px + 320px */
padding: 0 12px 12px 12px; padding: 0 12px 12px 12px;
box-sizing: border-box; box-sizing: border-box;
--interaction-margin: 0px;
} }
.more-info { .more-info {

View File

@ -119,6 +119,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
<ha-card> <ha-card>
<p class="title">${name}</p> <p class="title">${name}</p>
<ha-state-control-climate-temperature <ha-state-control-climate-temperature
prevent-interaction-on-scroll
show-current show-current
.hass=${this.hass} .hass=${this.hass}
.stateObj=${stateObj} .stateObj=${stateObj}
@ -175,7 +176,6 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
max-width: 344px; /* 12px + 12px + 320px */ max-width: 344px; /* 12px + 12px + 320px */
padding: 0 12px 12px 12px; padding: 0 12px 12px 12px;
box-sizing: border-box; box-sizing: border-box;
--interaction-margin: 0px;
} }
.more-info { .more-info {

View File

@ -30,6 +30,9 @@ export class HaStateControlClimateHumidity extends LitElement {
@property({ attribute: "show-current", type: Boolean }) @property({ attribute: "show-current", type: Boolean })
public showCurrent?: boolean; public showCurrent?: boolean;
@property({ type: Boolean, attribute: "prevent-interaction-on-scroll" })
public preventInteractionOnScroll?: boolean;
@state() private _targetHumidity?: number; @state() private _targetHumidity?: number;
private _sizeController = createStateControlCircularSliderController(this); private _sizeController = createStateControlCircularSliderController(this);
@ -192,6 +195,7 @@ export class HaStateControlClimateHumidity extends LitElement {
})} })}
> >
<ha-control-circular-slider <ha-control-circular-slider
.preventInteractionOnScroll=${this.preventInteractionOnScroll}
.inactive=${!active} .inactive=${!active}
.value=${this._targetHumidity} .value=${this._targetHumidity}
.min=${this._min} .min=${this._min}
@ -216,6 +220,7 @@ export class HaStateControlClimateHumidity extends LitElement {
return html` return html`
<div class="container${classMap(containerSizeClass)}"> <div class="container${classMap(containerSizeClass)}">
<ha-control-circular-slider <ha-control-circular-slider
.preventInteractionOnScroll=${this.preventInteractionOnScroll}
.current=${this.stateObj.attributes.current_humidity} .current=${this.stateObj.attributes.current_humidity}
.min=${this._min} .min=${this._min}
.max=${this._max} .max=${this._max}

View File

@ -48,6 +48,9 @@ export class HaStateControlClimateTemperature extends LitElement {
@property({ attribute: "show-current", type: Boolean }) @property({ attribute: "show-current", type: Boolean })
public showCurrent?: boolean; public showCurrent?: boolean;
@property({ type: Boolean, attribute: "prevent-interaction-on-scroll" })
public preventInteractionOnScroll?: boolean;
@state() private _targetTemperature: Partial<Record<Target, number>> = {}; @state() private _targetTemperature: Partial<Record<Target, number>> = {};
@state() private _selectTargetTemperature: Target = "low"; @state() private _selectTargetTemperature: Target = "low";
@ -318,6 +321,7 @@ export class HaStateControlClimateTemperature extends LitElement {
})} })}
> >
<ha-control-circular-slider <ha-control-circular-slider
.preventInteractionOnScroll=${this.preventInteractionOnScroll}
.inactive=${!active} .inactive=${!active}
.mode=${sliderMode} .mode=${sliderMode}
.value=${this._targetTemperature.value} .value=${this._targetTemperature.value}
@ -357,6 +361,7 @@ export class HaStateControlClimateTemperature extends LitElement {
})} })}
> >
<ha-control-circular-slider <ha-control-circular-slider
.preventInteractionOnScroll=${this.preventInteractionOnScroll}
.inactive=${!active} .inactive=${!active}
dual dual
.low=${this._targetTemperature.low} .low=${this._targetTemperature.low}
@ -412,6 +417,7 @@ export class HaStateControlClimateTemperature extends LitElement {
})} })}
> >
<ha-control-circular-slider <ha-control-circular-slider
.preventInteractionOnScroll=${this.preventInteractionOnScroll}
mode="full" mode="full"
.current=${this.stateObj.attributes.current_temperature} .current=${this.stateObj.attributes.current_temperature}
.min=${this._min} .min=${this._min}

View File

@ -32,6 +32,9 @@ export class HaStateControlHumidifierHumidity extends LitElement {
@property({ attribute: "show-current", type: Boolean }) @property({ attribute: "show-current", type: Boolean })
public showCurrent?: boolean = false; public showCurrent?: boolean = false;
@property({ type: Boolean, attribute: "prevent-interaction-on-scroll" })
public preventInteractionOnScroll?: boolean;
@state() private _targetHumidity?: number; @state() private _targetHumidity?: number;
private _sizeController = createStateControlCircularSliderController(this); private _sizeController = createStateControlCircularSliderController(this);
@ -202,6 +205,7 @@ export class HaStateControlHumidifierHumidity extends LitElement {
})} })}
> >
<ha-control-circular-slider <ha-control-circular-slider
.preventInteractionOnScroll=${this.preventInteractionOnScroll}
.inactive=${!active} .inactive=${!active}
.mode=${inverted ? "end" : "start"} .mode=${inverted ? "end" : "start"}
.value=${targetHumidity} .value=${targetHumidity}
@ -232,6 +236,7 @@ export class HaStateControlHumidifierHumidity extends LitElement {
})} })}
> >
<ha-control-circular-slider <ha-control-circular-slider
.preventInteractionOnScroll=${this.preventInteractionOnScroll}
.current=${currentHumidity} .current=${currentHumidity}
.min=${this._min} .min=${this._min}
.max=${this._max} .max=${this._max}

View File

@ -121,10 +121,6 @@ export const stateControlCircularSliderStyle = css`
ha-control-circular-slider { ha-control-circular-slider {
width: 100%; width: 100%;
--control-circular-slider-color: var(--state-color, var(--disabled-color)); --control-circular-slider-color: var(--state-color, var(--disabled-color));
--control-circular-slider-interaction-margin: var(
--interaction-margin,
12px
);
} }
ha-control-circular-slider::after { ha-control-circular-slider::after {
display: block; display: block;

View File

@ -33,6 +33,9 @@ export class HaStateControlWaterHeaterTemperature extends LitElement {
@property({ attribute: "show-current", type: Boolean }) @property({ attribute: "show-current", type: Boolean })
public showCurrent?: boolean; public showCurrent?: boolean;
@property({ type: Boolean, attribute: "prevent-interaction-on-scroll" })
public preventInteractionOnScroll?: boolean;
@state() private _targetTemperature?: number; @state() private _targetTemperature?: number;
private _sizeController = createStateControlCircularSliderController(this); private _sizeController = createStateControlCircularSliderController(this);
@ -197,6 +200,7 @@ export class HaStateControlWaterHeaterTemperature extends LitElement {
})} })}
> >
<ha-control-circular-slider <ha-control-circular-slider
.preventInteractionOnScroll=${this.preventInteractionOnScroll}
.inactive=${!active} .inactive=${!active}
.value=${this._targetTemperature} .value=${this._targetTemperature}
.min=${this._min} .min=${this._min}
@ -227,6 +231,7 @@ export class HaStateControlWaterHeaterTemperature extends LitElement {
})} })}
> >
<ha-control-circular-slider <ha-control-circular-slider
.preventInteractionOnScroll=${this.preventInteractionOnScroll}
mode="full" mode="full"
.current=${this.stateObj.attributes.current_temperature} .current=${this.stateObj.attributes.current_temperature}
.min=${this._min} .min=${this._min}