({
enabled,
defaultWidth,
minWidth,
maxWidth,
getMaxWidthPx,
storageKey,
side = "right",
}: UseResizableSidebarOptions)
| 78 | } |
| 79 | |
| 80 | export function useResizableSidebar({ |
| 81 | enabled, |
| 82 | defaultWidth, |
| 83 | minWidth, |
| 84 | maxWidth, |
| 85 | getMaxWidthPx, |
| 86 | storageKey, |
| 87 | side = "right", |
| 88 | }: UseResizableSidebarOptions): UseResizableSidebarResult { |
| 89 | // Load persisted width from localStorage on mount |
| 90 | // Always load persisted value regardless of enabled flag to maintain size across workspace switches |
| 91 | const [width, setWidth] = useState<number>(() => { |
| 92 | const resolvedMaxWidth = (() => { |
| 93 | if (typeof getMaxWidthPx === "function") { |
| 94 | const candidate = getMaxWidthPx(); |
| 95 | if (typeof candidate === "number" && Number.isFinite(candidate)) { |
| 96 | return Math.min(maxWidth, candidate); |
| 97 | } |
| 98 | } |
| 99 | return maxWidth; |
| 100 | })(); |
| 101 | |
| 102 | try { |
| 103 | return resolveInitialResizableSidebarWidth({ |
| 104 | storedValue: localStorage.getItem(storageKey), |
| 105 | defaultWidth, |
| 106 | minWidth, |
| 107 | maxWidth: resolvedMaxWidth, |
| 108 | }); |
| 109 | } catch { |
| 110 | // Ignore storage errors (private browsing, quota exceeded, etc.) |
| 111 | return resolveInitialResizableSidebarWidth({ |
| 112 | storedValue: null, |
| 113 | defaultWidth, |
| 114 | minWidth, |
| 115 | maxWidth: resolvedMaxWidth, |
| 116 | }); |
| 117 | } |
| 118 | }); |
| 119 | |
| 120 | const [isResizing, setIsResizing] = useState(false); |
| 121 | |
| 122 | // Refs to track drag state without causing re-renders |
| 123 | const startXRef = useRef<number>(0); // Mouse X position when drag started |
| 124 | const startWidthRef = useRef<number>(0); // Sidebar width when drag started |
| 125 | |
| 126 | const getMaxWidthPxRef = useRef(getMaxWidthPx); |
| 127 | useEffect(() => { |
| 128 | getMaxWidthPxRef.current = getMaxWidthPx; |
| 129 | }, [getMaxWidthPx]); |
| 130 | |
| 131 | const resolveMaxWidthPx = useCallback(() => { |
| 132 | const candidate = getMaxWidthPxRef.current?.(); |
| 133 | const resolved = |
| 134 | typeof candidate === "number" && Number.isFinite(candidate) |
| 135 | ? Math.min(maxWidth, candidate) |
| 136 | : maxWidth; |
| 137 | return Math.max(minWidth, resolved); |
no test coverage detected