mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
7ad196711e
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.
64 lines
2.0 KiB
TypeScript
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;
|
|
}
|