Skip to content

Commit 8dc1516

Browse files
authored
Fix "tipsy-ness" level selector on mobile (#32)
* Handle touch inputs appropriately * Add missing dependency
1 parent 4772852 commit 8dc1516

1 file changed

Lines changed: 30 additions & 5 deletions

File tree

frontend/src/tipsyselector/circleselector.tsx

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,13 @@ export function CircleSelector({
8989
pressed.current.lastRegisteredInput = 0;
9090
}
9191

92-
function onMove(event: React.MouseEvent<HTMLDivElement>) {
92+
function onMove(this: HTMLDivElement, event: MouseEvent | TouchEvent) {
93+
function isTouchEvent(event: Event): event is TouchEvent {
94+
return (event as any).touches;
95+
}
96+
97+
event.preventDefault();
98+
9399
/* Debounce and check for mouse press */
94100
const currentTime = new Date().getTime();
95101
// Mouse movement only, so the input must not change the value
@@ -116,10 +122,10 @@ export function CircleSelector({
116122
}
117123

118124
// Rectangle containing all elements. Offsets are relative to the viewport.
119-
const targetDimensionsInViewport: DOMRect = event.currentTarget.getBoundingClientRect();
125+
const targetDimensionsInViewport: DOMRect = this.getBoundingClientRect();
120126
const cursorPosition: Vector = new CartesianPoint(
121-
event.clientX - targetDimensionsInViewport.left,
122-
event.clientY - targetDimensionsInViewport.top
127+
(isTouchEvent(event) ? event.touches[0].clientX : event.clientX) - targetDimensionsInViewport.left,
128+
(isTouchEvent(event) ? event.touches[0].clientY : event.clientY) - targetDimensionsInViewport.top
123129
);
124130

125131
const pointOnCircleCartesian = calculateClosestPointOnCircle(cursorPosition);
@@ -147,7 +153,26 @@ export function CircleSelector({
147153
setValue(percentage);
148154
}
149155

150-
return <div onMouseMove={onMove}
156+
const containerRef = React.useRef<HTMLDivElement>(null);
157+
React.useEffect(() => {
158+
const element = containerRef.current;
159+
if (!element) {
160+
return;
161+
}
162+
163+
// Necessary as event listeners must not be passive (Event#preventDefault fails otherwise)
164+
element.addEventListener("mousemove", onMove, { passive: false });
165+
element.addEventListener("touchmove", onMove, { passive: false });
166+
167+
return () => {
168+
element.removeEventListener("mousemove", onMove);
169+
element.removeEventListener("touchmove", onMove);
170+
}
171+
// eslint demands 'onMove' to be in the dependencies array as well
172+
// eslint-disable-next-line react-hooks/exhaustive-deps
173+
}, [containerRef]);
174+
175+
return <div ref={containerRef}
151176
onTouchStart={onPress} onTouchEnd={onRelease} onMouseDown={onPress} onMouseUp={onRelease}>
152177
<svg width={width} height={width}>
153178
<OpenCircleSvg

0 commit comments

Comments
 (0)