Captures the full output of autodev existing-code Phase A through Step 4 (Code Testability Revision) for the Azaion UI workspace: - Step 1 Document: _docs/02_document/ (FINAL_report, architecture, glossary, components/, modules/, diagrams/, system-flows, module-layout) plus _docs/00_problem/ + _docs/01_solution/ + _docs/legacy/ + _docs/how_to_test + README. - Step 2 Architecture Baseline: architecture_compliance_baseline.md. - Step 3 Test Spec: _docs/02_document/tests/ (environment, test-data, blackbox/performance/resilience/security/ resource-limit tests, traceability-matrix), enum_spec_snapshot, expected_results/results_report.md (98 rows), plus the run-tests.sh + run-performance-tests.sh runners. - Step 4 Code Testability Revision: 01-testability-refactoring/ run dir (list-of-changes C01-C07, deferred_to_refactor, analysis/research_findings + refactoring_roadmap) and the 7 child task specs AZ-448..AZ-454 under _docs/02_tasks/todo/ plus _dependencies_table.md. - _docs/_autodev_state.md pins the cursor at Step 4 / refactor Phase 4 entry so /autodev resumes cleanly. Epic AZ-447 (UI testability gates) tracks the 7 child tasks that will land in subsequent commits. Co-authored-by: Cursor <cursoragent@cursor.com>
2.9 KiB
Module: src/hooks/useResizablePanel.ts
Source:
src/hooks/useResizablePanel.ts(33 lines) Topo batch: B1 (leaf)
Purpose
React hook that backs a draggable splitter between two horizontally-arranged panels. Owns the panel width as state and exposes a mouse handler the host attaches to the splitter element.
Public interface
export function useResizablePanel(
initialWidth: number,
min?: number, // default 100
max?: number, // default 600
): {
width: number
onMouseDown: (e: React.MouseEvent) => void
setWidth: React.Dispatch<React.SetStateAction<number>>
}
width is the current panel width (px). onMouseDown should be wired to the splitter element. setWidth is exposed for programmatic resets — currently unused by callers but kept for parity with the persistence story (see Notes).
Internal logic
useState(initialWidth)forwidth.- Drag bookkeeping is held in three
useRefcells (dragging,startX,startWidth) — kept out of state so the move/up handlers never re-render the host. onMouseDown(memoized viauseCallback([width])) snapshotsclientXandwidth, marksdragging = true, and callse.preventDefault()to avoid triggering text selection.- A
useEffectregisters globalmousemove/mouseuplisteners onwindow. Whiledragging.current === true,mousemoveupdateswidthtoclamp(startWidth + (e.clientX - startX), min, max).mouseupflipsdraggingback tofalse. - The effect cleans up on unmount.
Dependencies
- Internal: none.
- External:
react(useState,useCallback,useRef,useEffect).
Consumers (intra-repo)
src/features/annotations/AnnotationsPage.tsx— left + right panel widths.src/features/dataset/DatasetPage.tsx— left + right panel widths.
Both pages persist their layout server-side via UserSettings.{annotationsLeftPanelWidth, annotationsRightPanelWidth, datasetLeftPanelWidth, datasetRightPanelWidth} (see src/types/index.ts). The persistence read/write happens in the page, not in this hook.
Data models
None.
Configuration
None.
External integrations
DOM only — window.addEventListener('mousemove' / 'mouseup').
Security
None.
Tests
None.
Notes / open questions
- The hook is keyboard- and touch-blind: only mouse drag is supported, so accessibility for non-pointer input is missing. Track for the broader a11y pass; out of scope for
/document. setWidthis exposed but no current caller hydratesinitialWidthfromUserSettingsvia it — they pass the persisted width directly touseResizablePanel(persistedWidth)on mount. If the persisted width arrives after mount (asynchronous load), the panel jumps. Flag for the consumers' module docs (B8).- Default bounds (100 / 600) are arbitrary; consumer pages may want different ceilings (e.g., the annotations sidebar). Currently both consumers accept the defaults.