MCPcopy Create free account
hub / github.com/bytebase/bytebase / useColumnWidths

Function useColumnWidths

frontend/src/react/hooks/useColumnWidths.ts:62–161  ·  view source on GitHub ↗
(columns: T[])

Source from the content-addressed store, hash-verified

60 * rather than NaN.
61 */
62export function useColumnWidths<T extends ColumnWithWidth>(columns: T[]) {
63 const [widths, setWidths] = useState<number[]>(() =>
64 columns.map((c) => c.defaultWidth)
65 );
66 const dragRef = useRef<{
67 colIndex: number;
68 startX: number;
69 startWidth: number;
70 minWidth: number;
71 } | null>(null);
72 // Holds the teardown function for an active drag so an unmount-mid-drag
73 // (route change, modal close, etc.) tears down document-level listeners
74 // and resets the body cursor/userSelect overrides.
75 const dragCleanupRef = useRef<() => void>(() => {});
76 // Closures should capture state eagerly via refs, not via React deps.
77 // Putting `widths` in onResizeStart's dep array would rebind the callback
78 // on every mousemove (since each tick calls setWidths), which would
79 // re-render every header consumer mid-drag. Mirror the latest values
80 // into refs via useLayoutEffect (runs synchronously after commit, before
81 // paint and before any subsequent event handler can fire) so onResizeStart
82 // stays referentially stable AND avoids render-phase ref writes that
83 // React 19 concurrent mode could expose from a discarded render.
84 const widthsRef = useRef(widths);
85 const columnsRef = useRef(columns);
86 useLayoutEffect(() => {
87 widthsRef.current = widths;
88 columnsRef.current = columns;
89 });
90
91 const onResizeStart = useCallback((colIndex: number, e: React.MouseEvent) => {
92 e.preventDefault();
93 e.stopPropagation();
94 // Defensively tear down any in-flight drag before starting a new one.
95 // A second onResizeStart without an intervening mouseup (rapid
96 // sequential mousedowns, missed mouseup outside the document,
97 // multi-touch trackpad, programmatic dispatch) would otherwise leak
98 // the prior drag's document listeners and orphan its cleanup.
99 dragCleanupRef.current();
100 // Defensive: the user clicked a header that was just rendered, so the
101 // column at this index should be defined. Guard against the edge case
102 // where columnsRef has been synced to a shorter array via a commit
103 // that's interleaved with this click.
104 const col = columnsRef.current[colIndex];
105 if (!col) return;
106 dragRef.current = {
107 colIndex,
108 startX: e.clientX,
109 // Fall back to defaultWidth if widths state hasn't grown to include
110 // a column added after mount (widths state is sized at first render
111 // and never auto-extends). Without this, startWidth would be
112 // undefined and the drag's newWidth math would produce NaN.
113 startWidth: widthsRef.current[colIndex] ?? col.defaultWidth,
114 minWidth: col.minWidth ?? 40,
115 };
116
117 const onMouseMove = (ev: MouseEvent) => {
118 if (!dragRef.current) return;
119 // Read only the snapshot — see "Eager-capture contract" in JSDoc.

Callers 9

AuditLogTableFunction · 0.90
DatabaseTableViewFunction · 0.90
RulesSectionFunction · 0.90
TableColumnEditorFunction · 0.90
TableListFunction · 0.90
PlanTableFunction · 0.90
ProjectAccessGrantsPageFunction · 0.90
InstancesPageFunction · 0.90
HarnessFunction · 0.90

Calls

no outgoing calls

Tested by 1

HarnessFunction · 0.72