import { useCallback, useEffect, useRef, useState } from 'react'; type Direction = 'horizontal' | 'vertical'; interface UseResizableOptions { direction: Direction; initialSize: number; minSize: number; maxSize: number; /** If true, the "size" represents the *second* panel (e.g. right panel width). */ reverse?: boolean; } export default function useResizable({ direction, initialSize, minSize, maxSize, reverse = false, }: UseResizableOptions) { const [size, setSize] = useState(initialSize); const [isDragging, setIsDragging] = useState(false); const startPos = useRef(0); const startSize = useRef(0); const onMouseDown = useCallback( (e: React.MouseEvent) => { e.preventDefault(); setIsDragging(true); startPos.current = direction === 'horizontal' ? e.clientY : e.clientX; startSize.current = size; }, [direction, size], ); useEffect(() => { if (!isDragging) return; const onMouseMove = (e: MouseEvent) => { const currentPos = direction === 'horizontal' ? e.clientY : e.clientX; const delta = currentPos - startPos.current; const adjust = reverse ? -delta : delta; const next = Math.min(maxSize, Math.max(minSize, startSize.current + adjust)); setSize(next); }; const onMouseUp = () => setIsDragging(false); document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); document.body.style.cursor = direction === 'horizontal' ? 'row-resize' : 'col-resize'; document.body.style.userSelect = 'none'; return () => { document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); document.body.style.cursor = ''; document.body.style.userSelect = ''; }; }, [isDragging, direction, minSize, maxSize, reverse]); return { size, isDragging, onMouseDown }; }