diff --git a/frontend/src/components/todos/SwipeableRow.tsx b/frontend/src/components/todos/SwipeableRow.tsx new file mode 100644 index 0000000..2a34ee5 --- /dev/null +++ b/frontend/src/components/todos/SwipeableRow.tsx @@ -0,0 +1,77 @@ +import { useRef, useState } from 'react' + +interface SwipeableRowProps { + children: React.ReactNode + rightContent: React.ReactNode // actions révélées par swipe gauche + onSwipeRight?: () => void // callback swipe droit (marquer done) +} + +const THRESHOLD = 80 // pixels pour déclencher une action + +export default function SwipeableRow({ children, rightContent, onSwipeRight }: SwipeableRowProps) { + const [offsetX, setOffsetX] = useState(0) + const startX = useRef(null) + const dragging = useRef(false) + + function onTouchStart(e: React.TouchEvent) { + startX.current = e.touches[0].clientX + dragging.current = false + } + + function onTouchMove(e: React.TouchEvent) { + if (startX.current === null) return + dragging.current = true + const dx = e.touches[0].clientX - startX.current + // Clamp : +120px à droite, -160px à gauche (largeur des boutons) + setOffsetX(Math.max(Math.min(dx, 120), -160)) + } + + function onTouchEnd() { + if (offsetX > THRESHOLD && onSwipeRight) { + onSwipeRight() + } + setOffsetX(0) + startX.current = null + dragging.current = false + } + + const revealActions = offsetX < -(THRESHOLD / 2) + + return ( +
+ {/* Boutons d'action révélés à droite (swipe gauche) */} +
+ {rightContent} +
+ + {/* Rangée principale déplaçable */} +
THRESHOLD / 2 ? 'var(--ok)' : 'var(--bg-3)', + position: 'relative', + zIndex: 1, + }} + > + {children} +
+
+ ) +}