@tanstack/react-virtual
Version:
Headless UI for virtualizing scrollable elements in React
133 lines (132 loc) • 4.63 kB
JavaScript
import * as React from "react";
import { flushSync } from "react-dom";
import { Virtualizer, elementScroll, observeElementOffset, observeElementRect, windowScroll, observeWindowOffset, observeWindowRect } from "@tanstack/virtual-core";
export * from "@tanstack/virtual-core";
const useIsomorphicLayoutEffect = typeof document !== "undefined" ? React.useLayoutEffect : React.useEffect;
function useVirtualizerBase({
useFlushSync = true,
directDomUpdates = false,
directDomUpdatesMode = "transform",
...options
}) {
const rerender = React.useReducer((x) => x + 1, 0)[1];
const directRef = React.useRef({
enabled: directDomUpdates,
mode: directDomUpdatesMode,
container: null,
lastSize: null,
// Keyed by the element itself so a remounted node (same key, new DOM
// node — e.g. when `enabled` is toggled off then on) is treated as fresh
// and gets its style written.
lastPositions: /* @__PURE__ */ new WeakMap(),
prevRange: null
});
directRef.current.enabled = directDomUpdates;
directRef.current.mode = directDomUpdatesMode;
const applyDirectStyles = (instance2) => {
const state = directRef.current;
if (!state.enabled) return;
const totalSize = instance2.getTotalSize();
if (state.container && totalSize !== state.lastSize) {
state.lastSize = totalSize;
const sizeAxis = instance2.options.horizontal ? "width" : "height";
state.container.style[sizeAxis] = `${totalSize}px`;
}
const horizontal = !!instance2.options.horizontal;
const useTransform = state.mode === "transform";
const posAxis = horizontal ? "left" : "top";
const scrollMargin = instance2.options.scrollMargin;
const items = instance2.getVirtualItems();
for (const item of items) {
const next = item.start - scrollMargin;
const el = instance2.elementsCache.get(item.key);
if (!el) continue;
if (state.lastPositions.get(el) === next) continue;
state.lastPositions.set(el, next);
if (useTransform) {
el.style.transform = horizontal ? `translate3d(${next}px, 0, 0)` : `translate3d(0, ${next}px, 0)`;
} else {
el.style[posAxis] = `${next}px`;
}
}
};
const resolvedOptions = {
...options,
onChange: (instance2, sync) => {
var _a;
const state = directRef.current;
let shouldRerender = true;
if (state.enabled) {
applyDirectStyles(instance2);
const range = instance2.range;
const prev = state.prevRange;
shouldRerender = !prev || prev.isScrolling !== instance2.isScrolling || prev.startIndex !== (range == null ? void 0 : range.startIndex) || prev.endIndex !== (range == null ? void 0 : range.endIndex);
if (shouldRerender) {
state.prevRange = range ? {
startIndex: range.startIndex,
endIndex: range.endIndex,
isScrolling: instance2.isScrolling
} : null;
}
}
if (shouldRerender) {
if (useFlushSync && sync) {
flushSync(rerender);
} else {
rerender();
}
}
(_a = options.onChange) == null ? void 0 : _a.call(options, instance2, sync);
}
};
const [instance] = React.useState(() => {
const v = new Virtualizer(resolvedOptions);
return Object.assign(v, {
containerRef: (node) => {
const state = directRef.current;
state.container = node;
state.lastSize = null;
if (node && state.enabled) {
const total = v.getTotalSize();
state.lastSize = total;
const axis = v.options.horizontal ? "width" : "height";
node.style[axis] = `${total}px`;
}
}
});
});
instance.setOptions(resolvedOptions);
useIsomorphicLayoutEffect(() => {
return instance._didMount();
}, []);
useIsomorphicLayoutEffect(() => {
return instance._willUpdate();
});
useIsomorphicLayoutEffect(() => {
applyDirectStyles(instance);
});
return instance;
}
function useVirtualizer(options) {
return useVirtualizerBase({
observeElementRect,
observeElementOffset,
scrollToFn: elementScroll,
...options
});
}
function useWindowVirtualizer(options) {
return useVirtualizerBase({
getScrollElement: () => typeof document !== "undefined" ? window : null,
observeElementRect: observeWindowRect,
observeElementOffset: observeWindowOffset,
scrollToFn: windowScroll,
initialOffset: () => typeof document !== "undefined" ? window.scrollY : 0,
...options
});
}
export {
useVirtualizer,
useWindowVirtualizer
};
//# sourceMappingURL=index.js.map