@wordpress/compose
Version:
WordPress higher-order components (HOCs).
134 lines (133 loc) • 4.04 kB
JavaScript
// packages/compose/src/hooks/use-fixed-window-list/index.ts
import { useState, useLayoutEffect, useRef } from "@wordpress/element";
import { getScrollContainer } from "@wordpress/dom";
import { PAGEUP, PAGEDOWN, HOME, END } from "@wordpress/keycodes";
import useEvent from "../use-event/index.mjs";
var DEFAULT_INIT_WINDOW_SIZE = 30;
function useFixedWindowList(elementRef, itemHeight, totalItems, options) {
const {
windowOverscan,
useWindowing = true,
initWindowSize = DEFAULT_INIT_WINDOW_SIZE,
expandedState
} = options ?? {};
const [fixedListWindow, setFixedListWindow] = useState(
{
visibleItems: initWindowSize,
start: 0,
end: initWindowSize,
itemInView: (index) => {
return index >= 0 && index <= initWindowSize;
}
}
);
const visibleItemsRef = useRef(initWindowSize);
const isFirstMeasurementRef = useRef(true);
const measureWindow = useEvent((initRender) => {
const scrollContainer = getScrollContainer(elementRef.current);
if (!scrollContainer) {
return;
}
const visibleItems = Math.ceil(
scrollContainer.clientHeight / itemHeight
);
visibleItemsRef.current = visibleItems;
const isFirstMeasurement = isFirstMeasurementRef.current;
isFirstMeasurementRef.current = false;
const overscan = initRender ? visibleItems : windowOverscan ?? visibleItems;
const firstViewableIndex = Math.floor(
scrollContainer.scrollTop / itemHeight
);
const start = Math.max(0, firstViewableIndex - overscan);
const end = Math.min(
totalItems - 1,
firstViewableIndex + visibleItems + overscan
);
setFixedListWindow((lastWindow) => {
if (lastWindow.start <= start && lastWindow.end >= end) {
return lastWindow;
}
if (isFirstMeasurement && lastWindow.start <= firstViewableIndex && lastWindow.end >= firstViewableIndex + visibleItems) {
return lastWindow;
}
return {
visibleItems,
start,
end,
itemInView: (index) => {
return start <= index && index <= end;
}
};
});
});
const handleKeyDown = useEvent(
(event, scrollContainer) => {
switch (event.keyCode) {
case HOME: {
return scrollContainer.scrollTo({ top: 0 });
}
case END: {
return scrollContainer.scrollTo({
top: totalItems * itemHeight
});
}
case PAGEUP: {
return scrollContainer.scrollTo({
top: scrollContainer.scrollTop - visibleItemsRef.current * itemHeight
});
}
case PAGEDOWN: {
return scrollContainer.scrollTo({
top: scrollContainer.scrollTop + visibleItemsRef.current * itemHeight
});
}
}
}
);
useLayoutEffect(() => {
if (!useWindowing) {
return;
}
measureWindow(true);
}, [
useWindowing,
measureWindow,
itemHeight,
totalItems,
windowOverscan,
expandedState
]);
useLayoutEffect(() => {
if (!useWindowing) {
return;
}
const scrollContainer = getScrollContainer(elementRef.current);
if (!scrollContainer) {
return;
}
const { defaultView } = scrollContainer.ownerDocument;
const onMeasure = () => measureWindow();
const onKeyDown = (event) => handleKeyDown(event, scrollContainer);
scrollContainer.addEventListener("scroll", onMeasure);
defaultView?.addEventListener("resize", onMeasure);
defaultView?.addEventListener("keydown", onKeyDown);
return () => {
scrollContainer.removeEventListener("scroll", onMeasure);
defaultView?.removeEventListener("resize", onMeasure);
defaultView?.removeEventListener("keydown", onKeyDown);
};
}, [
useWindowing,
elementRef,
itemHeight,
totalItems,
expandedState,
measureWindow,
handleKeyDown
]);
return [fixedListWindow, setFixedListWindow];
}
export {
useFixedWindowList as default
};
//# sourceMappingURL=index.mjs.map