import { useState, useRef, useCallback } from 'react'; export default function SwipeableItem({ children, onSwipeLeft, renderRightActions, swipeThreshold = 80, }) { const [isSwiping, setIsSwiping] = useState(false); const [translateX, setTranslateX] = useState(0); const [isRevealed, setIsRevealed] = useState(false); const startXRef = useRef(0); const currentXRef = useRef(0); const itemRef = useRef(null); const handleTouchStart = useCallback((e) => { startXRef.current = e.touches[0].clientX; currentXRef.current = startXRef.current; setIsSwiping(true); }, []); const handleTouchMove = useCallback((e) => { if (!isSwiping) return; currentXRef.current = e.touches[0].clientX; const diff = currentXRef.current - startXRef.current; // Only allow swiping left (negative diff) if (diff < 0) { // Limit the swipe distance const newTranslateX = Math.max(diff, -swipeThreshold - 20); setTranslateX(newTranslateX); } else if (isRevealed) { // If already revealed, allow swiping right to close const newTranslateX = Math.min(0, -swipeThreshold + diff); setTranslateX(newTranslateX); } }, [isSwiping, isRevealed, swipeThreshold]); const handleTouchEnd = useCallback(() => { setIsSwiping(false); const diff = currentXRef.current - startXRef.current; if (diff < -swipeThreshold / 2 || (isRevealed && diff > -swipeThreshold / 2)) { // Reveal actions setTranslateX(-swipeThreshold); setIsRevealed(true); } else { // Snap back setTranslateX(0); setIsRevealed(false); } }, [isRevealed, swipeThreshold]); const handleClose = useCallback(() => { setTranslateX(0); setIsRevealed(false); }, []); const handleActionClick = useCallback((callback) => { callback?.(); handleClose(); }, [handleClose]); return (
{children}
{renderRightActions && (
{renderRightActions(handleActionClick)}
)}
); }