feat(mobile): add swipe-from-left-edge back gesture

This commit is contained in:
Jannis Braun
2026-03-17 15:37:27 +01:00
parent e45bf1927e
commit 0222946f04
2 changed files with 79 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
import { useRef, useCallback, useEffect } from 'react';
interface SwipeGestureOptions {
onSwipeRight?: () => void;
edgeThreshold?: number; // px from left edge to start detection (default: 20)
swipeThreshold?: number; // px of horizontal movement to trigger (default: 50)
enabled?: boolean;
}
export function useSwipeGesture({
onSwipeRight,
edgeThreshold = 20,
swipeThreshold = 50,
enabled = true,
}: SwipeGestureOptions) {
const touchStartRef = useRef<{ x: number; y: number } | null>(null);
const swipingRef = useRef(false);
const handleTouchStart = useCallback((e: TouchEvent) => {
if (!enabled) return;
const touch = e.touches[0];
if (touch.clientX <= edgeThreshold) {
touchStartRef.current = { x: touch.clientX, y: touch.clientY };
swipingRef.current = false;
}
}, [enabled, edgeThreshold]);
const handleTouchMove = useCallback((e: TouchEvent) => {
if (!touchStartRef.current) return;
const touch = e.touches[0];
const dx = touch.clientX - touchStartRef.current.x;
const dy = touch.clientY - touchStartRef.current.y;
// If vertical movement exceeds horizontal, cancel swipe detection
if (Math.abs(dy) > Math.abs(dx) && !swipingRef.current) {
touchStartRef.current = null;
return;
}
if (dx > swipeThreshold) {
swipingRef.current = true;
e.preventDefault();
}
}, [swipeThreshold]);
const handleTouchEnd = useCallback(() => {
if (swipingRef.current && onSwipeRight) {
onSwipeRight();
}
touchStartRef.current = null;
swipingRef.current = false;
}, [onSwipeRight]);
useEffect(() => {
if (!enabled) return;
document.addEventListener('touchstart', handleTouchStart, { passive: true });
document.addEventListener('touchmove', handleTouchMove, { passive: false });
document.addEventListener('touchend', handleTouchEnd);
document.addEventListener('touchcancel', handleTouchEnd);
return () => {
document.removeEventListener('touchstart', handleTouchStart);
document.removeEventListener('touchmove', handleTouchMove);
document.removeEventListener('touchend', handleTouchEnd);
document.removeEventListener('touchcancel', handleTouchEnd);
};
}, [enabled, handleTouchStart, handleTouchMove, handleTouchEnd]);
}