import { useEffect, RefObject, useRef } from 'react'; interface UseSwipeNavigationProps { containerRef: RefObject; onSwipeLeft: () => void; onSwipeRight: () => void; onSwipeUp?: () => void; onSwipeDown?: () => void; disabled?: boolean; threshold?: number; axis?: 'horizontal' | 'vertical'; } /** * A hook that enables swipe-based navigation on a container element. * In horizontal mode: swipe left → next, swipe right → previous. * In vertical mode: swipe up → next, swipe down → previous. */ const useSwipeNavigation = ({ containerRef, onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown, disabled = false, threshold = 50, axis = 'horizontal', }: UseSwipeNavigationProps) => { const onSwipeLeftRef = useRef(onSwipeLeft); const onSwipeRightRef = useRef(onSwipeRight); const onSwipeUpRef = useRef(onSwipeUp); const onSwipeDownRef = useRef(onSwipeDown); useEffect(() => { onSwipeLeftRef.current = onSwipeLeft; onSwipeRightRef.current = onSwipeRight; onSwipeUpRef.current = onSwipeUp; onSwipeDownRef.current = onSwipeDown; }, [onSwipeLeft, onSwipeRight, onSwipeUp, onSwipeDown]); useEffect(() => { const container = containerRef.current; if (!container || disabled) return; let startX: number | null = null; let startY: number | null = null; const handleTouchStart = (e: TouchEvent) => { startX = e.touches[0].clientX; startY = e.touches[0].clientY; }; const handleTouchEnd = (e: TouchEvent) => { if (startX === null || startY === null) return; const endX = e.changedTouches[0].clientX; const endY = e.changedTouches[0].clientY; if (axis === 'vertical') { const diffY = startY - endY; // positive = swiped up if (Math.abs(diffY) > threshold) { if (diffY > 0) { // Swiped up → next (onSwipeUpRef.current ?? onSwipeLeftRef.current)(); } else { // Swiped down → previous (onSwipeDownRef.current ?? onSwipeRightRef.current)(); } } } else { const diffX = startX - endX; // positive = swiped left if (Math.abs(diffX) > threshold) { if (diffX > 0) { onSwipeLeftRef.current(); } else { onSwipeRightRef.current(); } } } startX = null; startY = null; }; container.addEventListener('touchstart', handleTouchStart, { passive: true }); container.addEventListener('touchend', handleTouchEnd); return () => { container.removeEventListener('touchstart', handleTouchStart); container.removeEventListener('touchend', handleTouchEnd); }; }, [containerRef, disabled, threshold, axis]); }; export default useSwipeNavigation;