'use client'; import { useState, useRef, useCallback, useEffect, ReactNode } from 'react'; import { clsx } from 'clsx'; interface ResizablePanelProps { children: ReactNode; defaultWidth: number; minWidth: number; maxWidth: number; side: 'left' | 'right'; isOpen: boolean; isCollapsed: boolean; collapsedWidth: number; storageKey?: string; className?: string; } export function ResizablePanel({ children, defaultWidth, minWidth, maxWidth, side, isOpen, isCollapsed, collapsedWidth, storageKey, className, }: ResizablePanelProps) { const [width, setWidth] = useState(() => { if (typeof window !== 'undefined' && storageKey) { const stored = localStorage.getItem(storageKey); if (stored) { const parsed = parseInt(stored, 10); if (!isNaN(parsed) && parsed >= minWidth && parsed <= maxWidth) { return parsed; } } } return defaultWidth; }); const [isDragging, setIsDragging] = useState(false); const panelRef = useRef(null); const startXRef = useRef(0); const startWidthRef = useRef(0); const handleMouseDown = useCallback((e: React.MouseEvent) => { e.preventDefault(); setIsDragging(true); startXRef.current = e.clientX; startWidthRef.current = width; }, [width]); const handleMouseMove = useCallback((e: MouseEvent) => { if (!isDragging) return; const diff = side === 'right' ? startXRef.current - e.clientX : e.clientX - startXRef.current; const newWidth = Math.min(maxWidth, Math.max(minWidth, startWidthRef.current + diff)); setWidth(newWidth); }, [isDragging, minWidth, maxWidth, side]); const handleMouseUp = useCallback(() => { if (isDragging) { setIsDragging(false); if (storageKey) { localStorage.setItem(storageKey, width.toString()); } } }, [isDragging, storageKey, width]); useEffect(() => { if (isDragging) { document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); document.body.style.cursor = 'col-resize'; document.body.style.userSelect = 'none'; } return () => { document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); document.body.style.cursor = ''; document.body.style.userSelect = ''; }; }, [isDragging, handleMouseMove, handleMouseUp]); const currentWidth = isCollapsed ? collapsedWidth : width; return (
{children} {!isCollapsed && isOpen && (
)}
); } export function useResizableWidth( defaultWidth: number, minWidth: number, maxWidth: number, storageKey?: string ) { const [width, setWidth] = useState(() => { if (typeof window !== 'undefined' && storageKey) { const stored = localStorage.getItem(storageKey); if (stored) { const parsed = parseInt(stored, 10); if (!isNaN(parsed) && parsed >= minWidth && parsed <= maxWidth) { return parsed; } } } return defaultWidth; }); const updateWidth = useCallback((newWidth: number) => { const clamped = Math.min(maxWidth, Math.max(minWidth, newWidth)); setWidth(clamped); if (storageKey) { localStorage.setItem(storageKey, clamped.toString()); } }, [minWidth, maxWidth, storageKey]); return [width, updateWidth] as const; }