From 4c2c99b7871199210ed1ddc8a4ae0480a25746b5 Mon Sep 17 00:00:00 2001 From: Yuval Adam <_@yuv.al> Date: Wed, 24 Sep 2025 10:36:56 +0200 Subject: Handle mobile touch events in slider --- src/components/Slider.tsx | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/components/Slider.tsx b/src/components/Slider.tsx index bdca22c..22a8f8f 100644 --- a/src/components/Slider.tsx +++ b/src/components/Slider.tsx @@ -13,41 +13,54 @@ export function Slider({ playback, slider, setSlider }: SliderProps) { const max = playback ? PLAYBACK_SLOTS : 9; - const handleMouseDown = useCallback((e: React.MouseEvent) => { + const handleStart = useCallback((e: React.MouseEvent | React.TouchEvent) => { setIsDragging(true); updateSliderValue(e); }, []); - const updateSliderValue = useCallback((e: MouseEvent | React.MouseEvent) => { + const updateSliderValue = useCallback((e: MouseEvent | React.MouseEvent | TouchEvent | React.TouchEvent) => { if (!sliderRef.current) return; const rect = sliderRef.current.getBoundingClientRect(); - const percentage = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width)); + let clientX: number; + + if ('touches' in e) { + clientX = e.touches?.[0]?.clientX || (e as TouchEvent).changedTouches?.[0]?.clientX || 0; + } else { + clientX = e.clientX; + } + + const percentage = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width)); const value = Math.round(percentage * max); setSlider(value); }, [max, setSlider]); - const handleMouseMove = useCallback((e: MouseEvent) => { + const handleMove = useCallback((e: MouseEvent | TouchEvent) => { if (isDragging) { + e.preventDefault(); updateSliderValue(e); } }, [isDragging, updateSliderValue]); - const handleMouseUp = useCallback(() => { + const handleEnd = useCallback(() => { setIsDragging(false); }, []); React.useEffect(() => { if (isDragging) { - document.addEventListener('mousemove', handleMouseMove); - document.addEventListener('mouseup', handleMouseUp); + document.addEventListener('mousemove', handleMove); + document.addEventListener('mouseup', handleEnd); + document.addEventListener('touchmove', handleMove, { passive: false }); + document.addEventListener('touchend', handleEnd); return () => { - document.removeEventListener('mousemove', handleMouseMove); - document.removeEventListener('mouseup', handleMouseUp); + document.removeEventListener('mousemove', handleMove); + document.removeEventListener('mouseup', handleEnd); + document.removeEventListener('touchmove', handleMove); + document.removeEventListener('touchend', handleEnd); }; } - }, [isDragging, handleMouseMove, handleMouseUp]); + }, [isDragging, handleMove, handleEnd]); const percentage = (slider / max) * 100; @@ -55,8 +68,9 @@ export function Slider({ playback, slider, setSlider }: SliderProps) {