"use client"; import { useRef, useState, useCallback } from "react"; import { cn } from "@/lib/utils"; interface SwipeAction { label: string; icon?: React.ReactNode; onClick: () => void; className?: string; } interface SwipeableRowProps { children: React.ReactNode; leftActions?: SwipeAction[]; rightActions?: SwipeAction[]; className?: string; /** Width of each action button in px (default 72) */ actionWidth?: number; } /** * Row that reveals swipe actions when the user drags left (right-actions) * or right (left-actions). Used in the sidebar conversation list for * one-swipe delete. */ export function SwipeableRow({ children, leftActions = [], rightActions = [], className, actionWidth = 72, }: SwipeableRowProps) { const [translateX, setTranslateX] = useState(0); const [isDragging, setIsDragging] = useState(false); const startXRef = useRef(null); const currentXRef = useRef(0); const maxLeft = leftActions.length * actionWidth; const maxRight = rightActions.length * actionWidth; const handleTouchStart = useCallback((e: React.TouchEvent) => { startXRef.current = e.touches[0].clientX; setIsDragging(true); }, []); const handleTouchMove = useCallback( (e: React.TouchEvent) => { if (startXRef.current === null) return; const dx = e.touches[0].clientX - startXRef.current + currentXRef.current; const clamped = Math.max(-maxRight, Math.min(maxLeft, dx)); setTranslateX(clamped); }, [maxLeft, maxRight] ); const handleTouchEnd = useCallback(() => { setIsDragging(false); startXRef.current = null; // Snap: if dragged > half an action width, show actions; otherwise reset if (translateX < -(actionWidth / 2) && maxRight > 0) { const snapped = -maxRight; setTranslateX(snapped); currentXRef.current = snapped; } else if (translateX > actionWidth / 2 && maxLeft > 0) { const snapped = maxLeft; setTranslateX(snapped); currentXRef.current = snapped; } else { setTranslateX(0); currentXRef.current = 0; } }, [translateX, actionWidth, maxLeft, maxRight]); const resetPosition = useCallback(() => { setTranslateX(0); currentXRef.current = 0; }, []); return (
{/* Left action buttons (revealed on swipe-right) */} {leftActions.length > 0 && (
{leftActions.map((action) => ( ))}
)} {/* Right action buttons (revealed on swipe-left) */} {rightActions.length > 0 && (
{rightActions.map((action) => ( ))}
)} {/* Content row */}
{children}
); }