Files
Psychotoxical-psysonic/src/lib/hooks/useResizeClientHeight.ts
T
Psychotoxical 7ad196711e refactor(lib): move generic hooks, DnD engine, shortcut contract to lib
Continue M4 lib/ de-flattening with domain-agnostic infra:
- lib/hooks/: 9 pure generic hooks (useDebouncedValue, useIsMobile,
  useLongPressAction, useRangeSelection, useResizeClientHeight,
  useWindowVisibility, useSystemPrefersDark, useVirtualizerScrollMargin,
  useRemeasureGridVirtualizer) — all PURE (only react/zustand/tanstack deps).
- lib/dnd/DragDropContext.tsx: the generic mouse-event DnD engine (WebKitGTK
  HTML5-DnD workaround, ~24 importers, self-contained) — empties src/contexts/.
- lib/shortcuts/: shortcutActions + shortcutTypes (the action-id + binding
  contract; registry/dispatch/bindings stay app-level and import the contract,
  app->lib direction).

useWindowFullscreenState deliberately NOT moved — kept in hooks/ as app-shell
per the handoff iron-rule list (overrides its pure-helper appearance).

Pure move via deep @/lib/* specifiers. tsc 0, lint 0/0, full suite 319 files /
2353 tests green.
2026-06-30 08:20:11 +02:00

64 lines
2.0 KiB
TypeScript

import { type RefObject, useLayoutEffect, useState } from 'react';
/**
* Track an element's `clientHeight` (ResizeObserver). Used so virtualizers can
* set `overscan` to roughly one viewport of rows beyond the visible range.
*/
export function useElementClientHeightById(elementId: string, fallback = 800): number {
const [h, setH] = useState(fallback);
useLayoutEffect(() => {
const el = typeof document !== 'undefined' ? document.getElementById(elementId) : null;
if (!el) {
// React Compiler set-state-in-effect rule: state set from a DOM/layout measurement.
// eslint-disable-next-line react-hooks/set-state-in-effect
setH(fallback);
return;
}
const update = () => setH(el.clientHeight);
const ro = new ResizeObserver(update);
ro.observe(el);
update();
return () => ro.disconnect();
}, [elementId, fallback]);
return h;
}
export function useRefElementClientHeight(
ref: RefObject<HTMLElement | null>,
fallback = 600,
): number {
const [h, setH] = useState(fallback);
useLayoutEffect(() => {
const el = ref.current;
if (!el) return;
const update = () => setH(el.clientHeight);
const ro = new ResizeObserver(update);
ro.observe(el);
update();
return () => ro.disconnect();
}, [ref, fallback]);
return h;
}
/** ResizeObserver on a concrete element (e.g. callback-ref state for in-page scrollers). */
export function useElementClientHeightForElement(
element: HTMLElement | null,
fallback = 600,
): number {
const [h, setH] = useState(fallback);
useLayoutEffect(() => {
if (!element) {
// React Compiler set-state-in-effect rule: state set from a DOM/layout measurement.
// eslint-disable-next-line react-hooks/set-state-in-effect
setH(fallback);
return;
}
const update = () => setH(element.clientHeight);
const ro = new ResizeObserver(update);
ro.observe(element);
update();
return () => ro.disconnect();
}, [element, fallback]);
return h;
}