1082 lines
34 KiB
JavaScript
1082 lines
34 KiB
JavaScript
import {
|
|
require_react_dom
|
|
} from "./chunk-T4ZNA42A.js";
|
|
import {
|
|
require_react
|
|
} from "./chunk-3ZNQHJRW.js";
|
|
import {
|
|
__toESM
|
|
} from "./chunk-4MBMRILA.js";
|
|
|
|
// node_modules/@tanstack/react-virtual/dist/esm/index.js
|
|
var React = __toESM(require_react());
|
|
var import_react_dom = __toESM(require_react_dom());
|
|
|
|
// node_modules/@tanstack/virtual-core/dist/esm/utils.js
|
|
function memo(getDeps, fn, opts) {
|
|
let deps = opts.initialDeps ?? [];
|
|
let result;
|
|
let isInitial = true;
|
|
function memoizedFunction() {
|
|
var _a, _b, _c;
|
|
let depTime;
|
|
if (opts.key && ((_a = opts.debug) == null ? void 0 : _a.call(opts))) depTime = Date.now();
|
|
const newDeps = getDeps();
|
|
const depsChanged = newDeps.length !== deps.length || newDeps.some((dep, index) => deps[index] !== dep);
|
|
if (!depsChanged) {
|
|
return result;
|
|
}
|
|
deps = newDeps;
|
|
let resultTime;
|
|
if (opts.key && ((_b = opts.debug) == null ? void 0 : _b.call(opts))) resultTime = Date.now();
|
|
result = fn(...newDeps);
|
|
if (opts.key && ((_c = opts.debug) == null ? void 0 : _c.call(opts))) {
|
|
const depEndTime = Math.round((Date.now() - depTime) * 100) / 100;
|
|
const resultEndTime = Math.round((Date.now() - resultTime) * 100) / 100;
|
|
const resultFpsPercentage = resultEndTime / 16;
|
|
const pad = (str, num) => {
|
|
str = String(str);
|
|
while (str.length < num) {
|
|
str = " " + str;
|
|
}
|
|
return str;
|
|
};
|
|
console.info(
|
|
`%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`,
|
|
`
|
|
font-size: .6rem;
|
|
font-weight: bold;
|
|
color: hsl(${Math.max(
|
|
0,
|
|
Math.min(120 - 120 * resultFpsPercentage, 120)
|
|
)}deg 100% 31%);`,
|
|
opts == null ? void 0 : opts.key
|
|
);
|
|
}
|
|
if ((opts == null ? void 0 : opts.onChange) && !(isInitial && opts.skipInitialOnChange)) {
|
|
opts.onChange(result);
|
|
}
|
|
isInitial = false;
|
|
return result;
|
|
}
|
|
memoizedFunction.updateDeps = (newDeps) => {
|
|
deps = newDeps;
|
|
};
|
|
return memoizedFunction;
|
|
}
|
|
function notUndefined(value, msg) {
|
|
if (value === void 0) {
|
|
throw new Error(`Unexpected undefined${msg ? `: ${msg}` : ""}`);
|
|
} else {
|
|
return value;
|
|
}
|
|
}
|
|
var approxEqual = (a, b) => Math.abs(a - b) < 1.01;
|
|
var debounce = (targetWindow, fn, ms) => {
|
|
let timeoutId;
|
|
return function(...args) {
|
|
targetWindow.clearTimeout(timeoutId);
|
|
timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms);
|
|
};
|
|
};
|
|
|
|
// node_modules/@tanstack/virtual-core/dist/esm/index.js
|
|
var getRect = (element) => {
|
|
const { offsetWidth, offsetHeight } = element;
|
|
return { width: offsetWidth, height: offsetHeight };
|
|
};
|
|
var defaultKeyExtractor = (index) => index;
|
|
var defaultRangeExtractor = (range) => {
|
|
const start = Math.max(range.startIndex - range.overscan, 0);
|
|
const end = Math.min(range.endIndex + range.overscan, range.count - 1);
|
|
const arr = [];
|
|
for (let i = start; i <= end; i++) {
|
|
arr.push(i);
|
|
}
|
|
return arr;
|
|
};
|
|
var observeElementRect = (instance, cb) => {
|
|
const element = instance.scrollElement;
|
|
if (!element) {
|
|
return;
|
|
}
|
|
const targetWindow = instance.targetWindow;
|
|
if (!targetWindow) {
|
|
return;
|
|
}
|
|
const handler = (rect) => {
|
|
const { width, height } = rect;
|
|
cb({ width: Math.round(width), height: Math.round(height) });
|
|
};
|
|
handler(getRect(element));
|
|
if (!targetWindow.ResizeObserver) {
|
|
return () => {
|
|
};
|
|
}
|
|
const observer = new targetWindow.ResizeObserver((entries) => {
|
|
const run = () => {
|
|
const entry = entries[0];
|
|
if (entry == null ? void 0 : entry.borderBoxSize) {
|
|
const box = entry.borderBoxSize[0];
|
|
if (box) {
|
|
handler({ width: box.inlineSize, height: box.blockSize });
|
|
return;
|
|
}
|
|
}
|
|
handler(getRect(element));
|
|
};
|
|
instance.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
|
|
});
|
|
observer.observe(element, { box: "border-box" });
|
|
return () => {
|
|
observer.unobserve(element);
|
|
};
|
|
};
|
|
var addEventListenerOptions = {
|
|
passive: true
|
|
};
|
|
var observeWindowRect = (instance, cb) => {
|
|
const element = instance.scrollElement;
|
|
if (!element) {
|
|
return;
|
|
}
|
|
const handler = () => {
|
|
cb({ width: element.innerWidth, height: element.innerHeight });
|
|
};
|
|
handler();
|
|
element.addEventListener("resize", handler, addEventListenerOptions);
|
|
return () => {
|
|
element.removeEventListener("resize", handler);
|
|
};
|
|
};
|
|
var supportsScrollend = typeof window == "undefined" ? true : "onscrollend" in window;
|
|
var observeElementOffset = (instance, cb) => {
|
|
const element = instance.scrollElement;
|
|
if (!element) {
|
|
return;
|
|
}
|
|
const targetWindow = instance.targetWindow;
|
|
if (!targetWindow) {
|
|
return;
|
|
}
|
|
let offset = 0;
|
|
const fallback = instance.options.useScrollendEvent && supportsScrollend ? () => void 0 : debounce(
|
|
targetWindow,
|
|
() => {
|
|
cb(offset, false);
|
|
},
|
|
instance.options.isScrollingResetDelay
|
|
);
|
|
const createHandler = (isScrolling) => () => {
|
|
const { horizontal, isRtl } = instance.options;
|
|
offset = horizontal ? element["scrollLeft"] * (isRtl && -1 || 1) : element["scrollTop"];
|
|
fallback();
|
|
cb(offset, isScrolling);
|
|
};
|
|
const handler = createHandler(true);
|
|
const endHandler = createHandler(false);
|
|
element.addEventListener("scroll", handler, addEventListenerOptions);
|
|
const registerScrollendEvent = instance.options.useScrollendEvent && supportsScrollend;
|
|
if (registerScrollendEvent) {
|
|
element.addEventListener("scrollend", endHandler, addEventListenerOptions);
|
|
}
|
|
return () => {
|
|
element.removeEventListener("scroll", handler);
|
|
if (registerScrollendEvent) {
|
|
element.removeEventListener("scrollend", endHandler);
|
|
}
|
|
};
|
|
};
|
|
var observeWindowOffset = (instance, cb) => {
|
|
const element = instance.scrollElement;
|
|
if (!element) {
|
|
return;
|
|
}
|
|
const targetWindow = instance.targetWindow;
|
|
if (!targetWindow) {
|
|
return;
|
|
}
|
|
let offset = 0;
|
|
const fallback = instance.options.useScrollendEvent && supportsScrollend ? () => void 0 : debounce(
|
|
targetWindow,
|
|
() => {
|
|
cb(offset, false);
|
|
},
|
|
instance.options.isScrollingResetDelay
|
|
);
|
|
const createHandler = (isScrolling) => () => {
|
|
offset = element[instance.options.horizontal ? "scrollX" : "scrollY"];
|
|
fallback();
|
|
cb(offset, isScrolling);
|
|
};
|
|
const handler = createHandler(true);
|
|
const endHandler = createHandler(false);
|
|
element.addEventListener("scroll", handler, addEventListenerOptions);
|
|
const registerScrollendEvent = instance.options.useScrollendEvent && supportsScrollend;
|
|
if (registerScrollendEvent) {
|
|
element.addEventListener("scrollend", endHandler, addEventListenerOptions);
|
|
}
|
|
return () => {
|
|
element.removeEventListener("scroll", handler);
|
|
if (registerScrollendEvent) {
|
|
element.removeEventListener("scrollend", endHandler);
|
|
}
|
|
};
|
|
};
|
|
var measureElement = (element, entry, instance) => {
|
|
if (entry == null ? void 0 : entry.borderBoxSize) {
|
|
const box = entry.borderBoxSize[0];
|
|
if (box) {
|
|
const size = Math.round(
|
|
box[instance.options.horizontal ? "inlineSize" : "blockSize"]
|
|
);
|
|
return size;
|
|
}
|
|
}
|
|
return element[instance.options.horizontal ? "offsetWidth" : "offsetHeight"];
|
|
};
|
|
var windowScroll = (offset, {
|
|
adjustments = 0,
|
|
behavior
|
|
}, instance) => {
|
|
var _a, _b;
|
|
const toOffset = offset + adjustments;
|
|
(_b = (_a = instance.scrollElement) == null ? void 0 : _a.scrollTo) == null ? void 0 : _b.call(_a, {
|
|
[instance.options.horizontal ? "left" : "top"]: toOffset,
|
|
behavior
|
|
});
|
|
};
|
|
var elementScroll = (offset, {
|
|
adjustments = 0,
|
|
behavior
|
|
}, instance) => {
|
|
var _a, _b;
|
|
const toOffset = offset + adjustments;
|
|
(_b = (_a = instance.scrollElement) == null ? void 0 : _a.scrollTo) == null ? void 0 : _b.call(_a, {
|
|
[instance.options.horizontal ? "left" : "top"]: toOffset,
|
|
behavior
|
|
});
|
|
};
|
|
var Virtualizer = class {
|
|
constructor(opts) {
|
|
this.unsubs = [];
|
|
this.scrollElement = null;
|
|
this.targetWindow = null;
|
|
this.isScrolling = false;
|
|
this.scrollState = null;
|
|
this.measurementsCache = [];
|
|
this.itemSizeCache = /* @__PURE__ */ new Map();
|
|
this.laneAssignments = /* @__PURE__ */ new Map();
|
|
this.pendingMeasuredCacheIndexes = [];
|
|
this.prevLanes = void 0;
|
|
this.lanesChangedFlag = false;
|
|
this.lanesSettling = false;
|
|
this.scrollRect = null;
|
|
this.scrollOffset = null;
|
|
this.scrollDirection = null;
|
|
this.scrollAdjustments = 0;
|
|
this.elementsCache = /* @__PURE__ */ new Map();
|
|
this.now = () => {
|
|
var _a, _b, _c;
|
|
return ((_c = (_b = (_a = this.targetWindow) == null ? void 0 : _a.performance) == null ? void 0 : _b.now) == null ? void 0 : _c.call(_b)) ?? Date.now();
|
|
};
|
|
this.observer = /* @__PURE__ */ (() => {
|
|
let _ro = null;
|
|
const get = () => {
|
|
if (_ro) {
|
|
return _ro;
|
|
}
|
|
if (!this.targetWindow || !this.targetWindow.ResizeObserver) {
|
|
return null;
|
|
}
|
|
return _ro = new this.targetWindow.ResizeObserver((entries) => {
|
|
entries.forEach((entry) => {
|
|
const run = () => {
|
|
const node = entry.target;
|
|
const index = this.indexFromElement(node);
|
|
if (!node.isConnected) {
|
|
this.observer.unobserve(node);
|
|
return;
|
|
}
|
|
if (this.shouldMeasureDuringScroll(index)) {
|
|
this.resizeItem(
|
|
index,
|
|
this.options.measureElement(node, entry, this)
|
|
);
|
|
}
|
|
};
|
|
this.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
|
|
});
|
|
});
|
|
};
|
|
return {
|
|
disconnect: () => {
|
|
var _a;
|
|
(_a = get()) == null ? void 0 : _a.disconnect();
|
|
_ro = null;
|
|
},
|
|
observe: (target) => {
|
|
var _a;
|
|
return (_a = get()) == null ? void 0 : _a.observe(target, { box: "border-box" });
|
|
},
|
|
unobserve: (target) => {
|
|
var _a;
|
|
return (_a = get()) == null ? void 0 : _a.unobserve(target);
|
|
}
|
|
};
|
|
})();
|
|
this.range = null;
|
|
this.setOptions = (opts2) => {
|
|
Object.entries(opts2).forEach(([key, value]) => {
|
|
if (typeof value === "undefined") delete opts2[key];
|
|
});
|
|
this.options = {
|
|
debug: false,
|
|
initialOffset: 0,
|
|
overscan: 1,
|
|
paddingStart: 0,
|
|
paddingEnd: 0,
|
|
scrollPaddingStart: 0,
|
|
scrollPaddingEnd: 0,
|
|
horizontal: false,
|
|
getItemKey: defaultKeyExtractor,
|
|
rangeExtractor: defaultRangeExtractor,
|
|
onChange: () => {
|
|
},
|
|
measureElement,
|
|
initialRect: { width: 0, height: 0 },
|
|
scrollMargin: 0,
|
|
gap: 0,
|
|
indexAttribute: "data-index",
|
|
initialMeasurementsCache: [],
|
|
lanes: 1,
|
|
isScrollingResetDelay: 150,
|
|
enabled: true,
|
|
isRtl: false,
|
|
useScrollendEvent: false,
|
|
useAnimationFrameWithResizeObserver: false,
|
|
laneAssignmentMode: "estimate",
|
|
...opts2
|
|
};
|
|
};
|
|
this.notify = (sync) => {
|
|
var _a, _b;
|
|
(_b = (_a = this.options).onChange) == null ? void 0 : _b.call(_a, this, sync);
|
|
};
|
|
this.maybeNotify = memo(
|
|
() => {
|
|
this.calculateRange();
|
|
return [
|
|
this.isScrolling,
|
|
this.range ? this.range.startIndex : null,
|
|
this.range ? this.range.endIndex : null
|
|
];
|
|
},
|
|
(isScrolling) => {
|
|
this.notify(isScrolling);
|
|
},
|
|
{
|
|
key: "maybeNotify",
|
|
debug: () => this.options.debug,
|
|
initialDeps: [
|
|
this.isScrolling,
|
|
this.range ? this.range.startIndex : null,
|
|
this.range ? this.range.endIndex : null
|
|
]
|
|
}
|
|
);
|
|
this.cleanup = () => {
|
|
this.unsubs.filter(Boolean).forEach((d) => d());
|
|
this.unsubs = [];
|
|
this.observer.disconnect();
|
|
if (this.rafId != null && this.targetWindow) {
|
|
this.targetWindow.cancelAnimationFrame(this.rafId);
|
|
this.rafId = null;
|
|
}
|
|
this.scrollState = null;
|
|
this.scrollElement = null;
|
|
this.targetWindow = null;
|
|
};
|
|
this._didMount = () => {
|
|
return () => {
|
|
this.cleanup();
|
|
};
|
|
};
|
|
this._willUpdate = () => {
|
|
var _a;
|
|
const scrollElement = this.options.enabled ? this.options.getScrollElement() : null;
|
|
if (this.scrollElement !== scrollElement) {
|
|
this.cleanup();
|
|
if (!scrollElement) {
|
|
this.maybeNotify();
|
|
return;
|
|
}
|
|
this.scrollElement = scrollElement;
|
|
if (this.scrollElement && "ownerDocument" in this.scrollElement) {
|
|
this.targetWindow = this.scrollElement.ownerDocument.defaultView;
|
|
} else {
|
|
this.targetWindow = ((_a = this.scrollElement) == null ? void 0 : _a.window) ?? null;
|
|
}
|
|
this.elementsCache.forEach((cached) => {
|
|
this.observer.observe(cached);
|
|
});
|
|
this.unsubs.push(
|
|
this.options.observeElementRect(this, (rect) => {
|
|
this.scrollRect = rect;
|
|
this.maybeNotify();
|
|
})
|
|
);
|
|
this.unsubs.push(
|
|
this.options.observeElementOffset(this, (offset, isScrolling) => {
|
|
this.scrollAdjustments = 0;
|
|
this.scrollDirection = isScrolling ? this.getScrollOffset() < offset ? "forward" : "backward" : null;
|
|
this.scrollOffset = offset;
|
|
this.isScrolling = isScrolling;
|
|
if (this.scrollState) {
|
|
this.scheduleScrollReconcile();
|
|
}
|
|
this.maybeNotify();
|
|
})
|
|
);
|
|
this._scrollToOffset(this.getScrollOffset(), {
|
|
adjustments: void 0,
|
|
behavior: void 0
|
|
});
|
|
}
|
|
};
|
|
this.rafId = null;
|
|
this.getSize = () => {
|
|
if (!this.options.enabled) {
|
|
this.scrollRect = null;
|
|
return 0;
|
|
}
|
|
this.scrollRect = this.scrollRect ?? this.options.initialRect;
|
|
return this.scrollRect[this.options.horizontal ? "width" : "height"];
|
|
};
|
|
this.getScrollOffset = () => {
|
|
if (!this.options.enabled) {
|
|
this.scrollOffset = null;
|
|
return 0;
|
|
}
|
|
this.scrollOffset = this.scrollOffset ?? (typeof this.options.initialOffset === "function" ? this.options.initialOffset() : this.options.initialOffset);
|
|
return this.scrollOffset;
|
|
};
|
|
this.getFurthestMeasurement = (measurements, index) => {
|
|
const furthestMeasurementsFound = /* @__PURE__ */ new Map();
|
|
const furthestMeasurements = /* @__PURE__ */ new Map();
|
|
for (let m = index - 1; m >= 0; m--) {
|
|
const measurement = measurements[m];
|
|
if (furthestMeasurementsFound.has(measurement.lane)) {
|
|
continue;
|
|
}
|
|
const previousFurthestMeasurement = furthestMeasurements.get(
|
|
measurement.lane
|
|
);
|
|
if (previousFurthestMeasurement == null || measurement.end > previousFurthestMeasurement.end) {
|
|
furthestMeasurements.set(measurement.lane, measurement);
|
|
} else if (measurement.end < previousFurthestMeasurement.end) {
|
|
furthestMeasurementsFound.set(measurement.lane, true);
|
|
}
|
|
if (furthestMeasurementsFound.size === this.options.lanes) {
|
|
break;
|
|
}
|
|
}
|
|
return furthestMeasurements.size === this.options.lanes ? Array.from(furthestMeasurements.values()).sort((a, b) => {
|
|
if (a.end === b.end) {
|
|
return a.index - b.index;
|
|
}
|
|
return a.end - b.end;
|
|
})[0] : void 0;
|
|
};
|
|
this.getMeasurementOptions = memo(
|
|
() => [
|
|
this.options.count,
|
|
this.options.paddingStart,
|
|
this.options.scrollMargin,
|
|
this.options.getItemKey,
|
|
this.options.enabled,
|
|
this.options.lanes,
|
|
this.options.laneAssignmentMode
|
|
],
|
|
(count, paddingStart, scrollMargin, getItemKey, enabled, lanes, laneAssignmentMode) => {
|
|
const lanesChanged = this.prevLanes !== void 0 && this.prevLanes !== lanes;
|
|
if (lanesChanged) {
|
|
this.lanesChangedFlag = true;
|
|
}
|
|
this.prevLanes = lanes;
|
|
this.pendingMeasuredCacheIndexes = [];
|
|
return {
|
|
count,
|
|
paddingStart,
|
|
scrollMargin,
|
|
getItemKey,
|
|
enabled,
|
|
lanes,
|
|
laneAssignmentMode
|
|
};
|
|
},
|
|
{
|
|
key: false
|
|
}
|
|
);
|
|
this.getMeasurements = memo(
|
|
() => [this.getMeasurementOptions(), this.itemSizeCache],
|
|
({
|
|
count,
|
|
paddingStart,
|
|
scrollMargin,
|
|
getItemKey,
|
|
enabled,
|
|
lanes,
|
|
laneAssignmentMode
|
|
}, itemSizeCache) => {
|
|
if (!enabled) {
|
|
this.measurementsCache = [];
|
|
this.itemSizeCache.clear();
|
|
this.laneAssignments.clear();
|
|
return [];
|
|
}
|
|
if (this.laneAssignments.size > count) {
|
|
for (const index of this.laneAssignments.keys()) {
|
|
if (index >= count) {
|
|
this.laneAssignments.delete(index);
|
|
}
|
|
}
|
|
}
|
|
if (this.lanesChangedFlag) {
|
|
this.lanesChangedFlag = false;
|
|
this.lanesSettling = true;
|
|
this.measurementsCache = [];
|
|
this.itemSizeCache.clear();
|
|
this.laneAssignments.clear();
|
|
this.pendingMeasuredCacheIndexes = [];
|
|
}
|
|
if (this.measurementsCache.length === 0 && !this.lanesSettling) {
|
|
this.measurementsCache = this.options.initialMeasurementsCache;
|
|
this.measurementsCache.forEach((item) => {
|
|
this.itemSizeCache.set(item.key, item.size);
|
|
});
|
|
}
|
|
const min = this.lanesSettling ? 0 : this.pendingMeasuredCacheIndexes.length > 0 ? Math.min(...this.pendingMeasuredCacheIndexes) : 0;
|
|
this.pendingMeasuredCacheIndexes = [];
|
|
if (this.lanesSettling && this.measurementsCache.length === count) {
|
|
this.lanesSettling = false;
|
|
}
|
|
const measurements = this.measurementsCache.slice(0, min);
|
|
const laneLastIndex = new Array(lanes).fill(
|
|
void 0
|
|
);
|
|
for (let m = 0; m < min; m++) {
|
|
const item = measurements[m];
|
|
if (item) {
|
|
laneLastIndex[item.lane] = m;
|
|
}
|
|
}
|
|
for (let i = min; i < count; i++) {
|
|
const key = getItemKey(i);
|
|
const cachedLane = this.laneAssignments.get(i);
|
|
let lane;
|
|
let start;
|
|
const shouldCacheLane = laneAssignmentMode === "estimate" || itemSizeCache.has(key);
|
|
if (cachedLane !== void 0 && this.options.lanes > 1) {
|
|
lane = cachedLane;
|
|
const prevIndex = laneLastIndex[lane];
|
|
const prevInLane = prevIndex !== void 0 ? measurements[prevIndex] : void 0;
|
|
start = prevInLane ? prevInLane.end + this.options.gap : paddingStart + scrollMargin;
|
|
} else {
|
|
const furthestMeasurement = this.options.lanes === 1 ? measurements[i - 1] : this.getFurthestMeasurement(measurements, i);
|
|
start = furthestMeasurement ? furthestMeasurement.end + this.options.gap : paddingStart + scrollMargin;
|
|
lane = furthestMeasurement ? furthestMeasurement.lane : i % this.options.lanes;
|
|
if (this.options.lanes > 1 && shouldCacheLane) {
|
|
this.laneAssignments.set(i, lane);
|
|
}
|
|
}
|
|
const measuredSize = itemSizeCache.get(key);
|
|
const size = typeof measuredSize === "number" ? measuredSize : this.options.estimateSize(i);
|
|
const end = start + size;
|
|
measurements[i] = {
|
|
index: i,
|
|
start,
|
|
size,
|
|
end,
|
|
key,
|
|
lane
|
|
};
|
|
laneLastIndex[lane] = i;
|
|
}
|
|
this.measurementsCache = measurements;
|
|
return measurements;
|
|
},
|
|
{
|
|
key: "getMeasurements",
|
|
debug: () => this.options.debug
|
|
}
|
|
);
|
|
this.calculateRange = memo(
|
|
() => [
|
|
this.getMeasurements(),
|
|
this.getSize(),
|
|
this.getScrollOffset(),
|
|
this.options.lanes
|
|
],
|
|
(measurements, outerSize, scrollOffset, lanes) => {
|
|
return this.range = measurements.length > 0 && outerSize > 0 ? calculateRange({
|
|
measurements,
|
|
outerSize,
|
|
scrollOffset,
|
|
lanes
|
|
}) : null;
|
|
},
|
|
{
|
|
key: "calculateRange",
|
|
debug: () => this.options.debug
|
|
}
|
|
);
|
|
this.getVirtualIndexes = memo(
|
|
() => {
|
|
let startIndex = null;
|
|
let endIndex = null;
|
|
const range = this.calculateRange();
|
|
if (range) {
|
|
startIndex = range.startIndex;
|
|
endIndex = range.endIndex;
|
|
}
|
|
this.maybeNotify.updateDeps([this.isScrolling, startIndex, endIndex]);
|
|
return [
|
|
this.options.rangeExtractor,
|
|
this.options.overscan,
|
|
this.options.count,
|
|
startIndex,
|
|
endIndex
|
|
];
|
|
},
|
|
(rangeExtractor, overscan, count, startIndex, endIndex) => {
|
|
return startIndex === null || endIndex === null ? [] : rangeExtractor({
|
|
startIndex,
|
|
endIndex,
|
|
overscan,
|
|
count
|
|
});
|
|
},
|
|
{
|
|
key: "getVirtualIndexes",
|
|
debug: () => this.options.debug
|
|
}
|
|
);
|
|
this.indexFromElement = (node) => {
|
|
const attributeName = this.options.indexAttribute;
|
|
const indexStr = node.getAttribute(attributeName);
|
|
if (!indexStr) {
|
|
console.warn(
|
|
`Missing attribute name '${attributeName}={index}' on measured element.`
|
|
);
|
|
return -1;
|
|
}
|
|
return parseInt(indexStr, 10);
|
|
};
|
|
this.shouldMeasureDuringScroll = (index) => {
|
|
var _a;
|
|
if (!this.scrollState || this.scrollState.behavior !== "smooth") {
|
|
return true;
|
|
}
|
|
const scrollIndex = this.scrollState.index ?? ((_a = this.getVirtualItemForOffset(this.scrollState.lastTargetOffset)) == null ? void 0 : _a.index);
|
|
if (scrollIndex !== void 0 && this.range) {
|
|
const bufferSize = Math.max(
|
|
this.options.overscan,
|
|
Math.ceil((this.range.endIndex - this.range.startIndex) / 2)
|
|
);
|
|
const minIndex = Math.max(0, scrollIndex - bufferSize);
|
|
const maxIndex = Math.min(
|
|
this.options.count - 1,
|
|
scrollIndex + bufferSize
|
|
);
|
|
return index >= minIndex && index <= maxIndex;
|
|
}
|
|
return true;
|
|
};
|
|
this.measureElement = (node) => {
|
|
if (!node) {
|
|
this.elementsCache.forEach((cached, key2) => {
|
|
if (!cached.isConnected) {
|
|
this.observer.unobserve(cached);
|
|
this.elementsCache.delete(key2);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
const index = this.indexFromElement(node);
|
|
const key = this.options.getItemKey(index);
|
|
const prevNode = this.elementsCache.get(key);
|
|
if (prevNode !== node) {
|
|
if (prevNode) {
|
|
this.observer.unobserve(prevNode);
|
|
}
|
|
this.observer.observe(node);
|
|
this.elementsCache.set(key, node);
|
|
}
|
|
if ((!this.isScrolling || this.scrollState) && this.shouldMeasureDuringScroll(index)) {
|
|
this.resizeItem(index, this.options.measureElement(node, void 0, this));
|
|
}
|
|
};
|
|
this.resizeItem = (index, size) => {
|
|
var _a;
|
|
const item = this.measurementsCache[index];
|
|
if (!item) return;
|
|
const itemSize = this.itemSizeCache.get(item.key) ?? item.size;
|
|
const delta = size - itemSize;
|
|
if (delta !== 0) {
|
|
if (((_a = this.scrollState) == null ? void 0 : _a.behavior) !== "smooth" && (this.shouldAdjustScrollPositionOnItemSizeChange !== void 0 ? this.shouldAdjustScrollPositionOnItemSizeChange(item, delta, this) : item.start < this.getScrollOffset() + this.scrollAdjustments)) {
|
|
if (this.options.debug) {
|
|
console.info("correction", delta);
|
|
}
|
|
this._scrollToOffset(this.getScrollOffset(), {
|
|
adjustments: this.scrollAdjustments += delta,
|
|
behavior: void 0
|
|
});
|
|
}
|
|
this.pendingMeasuredCacheIndexes.push(item.index);
|
|
this.itemSizeCache = new Map(this.itemSizeCache.set(item.key, size));
|
|
this.notify(false);
|
|
}
|
|
};
|
|
this.getVirtualItems = memo(
|
|
() => [this.getVirtualIndexes(), this.getMeasurements()],
|
|
(indexes, measurements) => {
|
|
const virtualItems = [];
|
|
for (let k = 0, len = indexes.length; k < len; k++) {
|
|
const i = indexes[k];
|
|
const measurement = measurements[i];
|
|
virtualItems.push(measurement);
|
|
}
|
|
return virtualItems;
|
|
},
|
|
{
|
|
key: "getVirtualItems",
|
|
debug: () => this.options.debug
|
|
}
|
|
);
|
|
this.getVirtualItemForOffset = (offset) => {
|
|
const measurements = this.getMeasurements();
|
|
if (measurements.length === 0) {
|
|
return void 0;
|
|
}
|
|
return notUndefined(
|
|
measurements[findNearestBinarySearch(
|
|
0,
|
|
measurements.length - 1,
|
|
(index) => notUndefined(measurements[index]).start,
|
|
offset
|
|
)]
|
|
);
|
|
};
|
|
this.getMaxScrollOffset = () => {
|
|
if (!this.scrollElement) return 0;
|
|
if ("scrollHeight" in this.scrollElement) {
|
|
return this.options.horizontal ? this.scrollElement.scrollWidth - this.scrollElement.clientWidth : this.scrollElement.scrollHeight - this.scrollElement.clientHeight;
|
|
} else {
|
|
const doc = this.scrollElement.document.documentElement;
|
|
return this.options.horizontal ? doc.scrollWidth - this.scrollElement.innerWidth : doc.scrollHeight - this.scrollElement.innerHeight;
|
|
}
|
|
};
|
|
this.getOffsetForAlignment = (toOffset, align, itemSize = 0) => {
|
|
if (!this.scrollElement) return 0;
|
|
const size = this.getSize();
|
|
const scrollOffset = this.getScrollOffset();
|
|
if (align === "auto") {
|
|
align = toOffset >= scrollOffset + size ? "end" : "start";
|
|
}
|
|
if (align === "center") {
|
|
toOffset += (itemSize - size) / 2;
|
|
} else if (align === "end") {
|
|
toOffset -= size;
|
|
}
|
|
const maxOffset = this.getMaxScrollOffset();
|
|
return Math.max(Math.min(maxOffset, toOffset), 0);
|
|
};
|
|
this.getOffsetForIndex = (index, align = "auto") => {
|
|
index = Math.max(0, Math.min(index, this.options.count - 1));
|
|
const size = this.getSize();
|
|
const scrollOffset = this.getScrollOffset();
|
|
const item = this.measurementsCache[index];
|
|
if (!item) return;
|
|
if (align === "auto") {
|
|
if (item.end >= scrollOffset + size - this.options.scrollPaddingEnd) {
|
|
align = "end";
|
|
} else if (item.start <= scrollOffset + this.options.scrollPaddingStart) {
|
|
align = "start";
|
|
} else {
|
|
return [scrollOffset, align];
|
|
}
|
|
}
|
|
if (align === "end" && index === this.options.count - 1) {
|
|
return [this.getMaxScrollOffset(), align];
|
|
}
|
|
const toOffset = align === "end" ? item.end + this.options.scrollPaddingEnd : item.start - this.options.scrollPaddingStart;
|
|
return [
|
|
this.getOffsetForAlignment(toOffset, align, item.size),
|
|
align
|
|
];
|
|
};
|
|
this.scrollToOffset = (toOffset, { align = "start", behavior = "auto" } = {}) => {
|
|
const offset = this.getOffsetForAlignment(toOffset, align);
|
|
const now = this.now();
|
|
this.scrollState = {
|
|
index: null,
|
|
align,
|
|
behavior,
|
|
startedAt: now,
|
|
lastTargetOffset: offset,
|
|
stableFrames: 0
|
|
};
|
|
this._scrollToOffset(offset, { adjustments: void 0, behavior });
|
|
this.scheduleScrollReconcile();
|
|
};
|
|
this.scrollToIndex = (index, {
|
|
align: initialAlign = "auto",
|
|
behavior = "auto"
|
|
} = {}) => {
|
|
index = Math.max(0, Math.min(index, this.options.count - 1));
|
|
const offsetInfo = this.getOffsetForIndex(index, initialAlign);
|
|
if (!offsetInfo) {
|
|
return;
|
|
}
|
|
const [offset, align] = offsetInfo;
|
|
const now = this.now();
|
|
this.scrollState = {
|
|
index,
|
|
align,
|
|
behavior,
|
|
startedAt: now,
|
|
lastTargetOffset: offset,
|
|
stableFrames: 0
|
|
};
|
|
this._scrollToOffset(offset, { adjustments: void 0, behavior });
|
|
this.scheduleScrollReconcile();
|
|
};
|
|
this.scrollBy = (delta, { behavior = "auto" } = {}) => {
|
|
const offset = this.getScrollOffset() + delta;
|
|
const now = this.now();
|
|
this.scrollState = {
|
|
index: null,
|
|
align: "start",
|
|
behavior,
|
|
startedAt: now,
|
|
lastTargetOffset: offset,
|
|
stableFrames: 0
|
|
};
|
|
this._scrollToOffset(offset, { adjustments: void 0, behavior });
|
|
this.scheduleScrollReconcile();
|
|
};
|
|
this.getTotalSize = () => {
|
|
var _a;
|
|
const measurements = this.getMeasurements();
|
|
let end;
|
|
if (measurements.length === 0) {
|
|
end = this.options.paddingStart;
|
|
} else if (this.options.lanes === 1) {
|
|
end = ((_a = measurements[measurements.length - 1]) == null ? void 0 : _a.end) ?? 0;
|
|
} else {
|
|
const endByLane = Array(this.options.lanes).fill(null);
|
|
let endIndex = measurements.length - 1;
|
|
while (endIndex >= 0 && endByLane.some((val) => val === null)) {
|
|
const item = measurements[endIndex];
|
|
if (endByLane[item.lane] === null) {
|
|
endByLane[item.lane] = item.end;
|
|
}
|
|
endIndex--;
|
|
}
|
|
end = Math.max(...endByLane.filter((val) => val !== null));
|
|
}
|
|
return Math.max(
|
|
end - this.options.scrollMargin + this.options.paddingEnd,
|
|
0
|
|
);
|
|
};
|
|
this._scrollToOffset = (offset, {
|
|
adjustments,
|
|
behavior
|
|
}) => {
|
|
this.options.scrollToFn(offset, { behavior, adjustments }, this);
|
|
};
|
|
this.measure = () => {
|
|
this.itemSizeCache = /* @__PURE__ */ new Map();
|
|
this.laneAssignments = /* @__PURE__ */ new Map();
|
|
this.notify(false);
|
|
};
|
|
this.setOptions(opts);
|
|
}
|
|
scheduleScrollReconcile() {
|
|
if (!this.targetWindow) {
|
|
this.scrollState = null;
|
|
return;
|
|
}
|
|
if (this.rafId != null) return;
|
|
this.rafId = this.targetWindow.requestAnimationFrame(() => {
|
|
this.rafId = null;
|
|
this.reconcileScroll();
|
|
});
|
|
}
|
|
reconcileScroll() {
|
|
if (!this.scrollState) return;
|
|
const el = this.scrollElement;
|
|
if (!el) return;
|
|
const MAX_RECONCILE_MS = 5e3;
|
|
if (this.now() - this.scrollState.startedAt > MAX_RECONCILE_MS) {
|
|
this.scrollState = null;
|
|
return;
|
|
}
|
|
const offsetInfo = this.scrollState.index != null ? this.getOffsetForIndex(this.scrollState.index, this.scrollState.align) : void 0;
|
|
const targetOffset = offsetInfo ? offsetInfo[0] : this.scrollState.lastTargetOffset;
|
|
const STABLE_FRAMES = 1;
|
|
const targetChanged = targetOffset !== this.scrollState.lastTargetOffset;
|
|
if (!targetChanged && approxEqual(targetOffset, this.getScrollOffset())) {
|
|
this.scrollState.stableFrames++;
|
|
if (this.scrollState.stableFrames >= STABLE_FRAMES) {
|
|
this.scrollState = null;
|
|
return;
|
|
}
|
|
} else {
|
|
this.scrollState.stableFrames = 0;
|
|
if (targetChanged) {
|
|
this.scrollState.lastTargetOffset = targetOffset;
|
|
this.scrollState.behavior = "auto";
|
|
this._scrollToOffset(targetOffset, {
|
|
adjustments: void 0,
|
|
behavior: "auto"
|
|
});
|
|
}
|
|
}
|
|
this.scheduleScrollReconcile();
|
|
}
|
|
};
|
|
var findNearestBinarySearch = (low, high, getCurrentValue, value) => {
|
|
while (low <= high) {
|
|
const middle = (low + high) / 2 | 0;
|
|
const currentValue = getCurrentValue(middle);
|
|
if (currentValue < value) {
|
|
low = middle + 1;
|
|
} else if (currentValue > value) {
|
|
high = middle - 1;
|
|
} else {
|
|
return middle;
|
|
}
|
|
}
|
|
if (low > 0) {
|
|
return low - 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
};
|
|
function calculateRange({
|
|
measurements,
|
|
outerSize,
|
|
scrollOffset,
|
|
lanes
|
|
}) {
|
|
const lastIndex = measurements.length - 1;
|
|
const getOffset = (index) => measurements[index].start;
|
|
if (measurements.length <= lanes) {
|
|
return {
|
|
startIndex: 0,
|
|
endIndex: lastIndex
|
|
};
|
|
}
|
|
let startIndex = findNearestBinarySearch(
|
|
0,
|
|
lastIndex,
|
|
getOffset,
|
|
scrollOffset
|
|
);
|
|
let endIndex = startIndex;
|
|
if (lanes === 1) {
|
|
while (endIndex < lastIndex && measurements[endIndex].end < scrollOffset + outerSize) {
|
|
endIndex++;
|
|
}
|
|
} else if (lanes > 1) {
|
|
const endPerLane = Array(lanes).fill(0);
|
|
while (endIndex < lastIndex && endPerLane.some((pos) => pos < scrollOffset + outerSize)) {
|
|
const item = measurements[endIndex];
|
|
endPerLane[item.lane] = item.end;
|
|
endIndex++;
|
|
}
|
|
const startPerLane = Array(lanes).fill(scrollOffset + outerSize);
|
|
while (startIndex >= 0 && startPerLane.some((pos) => pos >= scrollOffset)) {
|
|
const item = measurements[startIndex];
|
|
startPerLane[item.lane] = item.start;
|
|
startIndex--;
|
|
}
|
|
startIndex = Math.max(0, startIndex - startIndex % lanes);
|
|
endIndex = Math.min(lastIndex, endIndex + (lanes - 1 - endIndex % lanes));
|
|
}
|
|
return { startIndex, endIndex };
|
|
}
|
|
|
|
// node_modules/@tanstack/react-virtual/dist/esm/index.js
|
|
var useIsomorphicLayoutEffect = typeof document !== "undefined" ? React.useLayoutEffect : React.useEffect;
|
|
function useVirtualizerBase({
|
|
useFlushSync = true,
|
|
...options
|
|
}) {
|
|
const rerender = React.useReducer(() => ({}), {})[1];
|
|
const resolvedOptions = {
|
|
...options,
|
|
onChange: (instance2, sync) => {
|
|
var _a;
|
|
if (useFlushSync && sync) {
|
|
(0, import_react_dom.flushSync)(rerender);
|
|
} else {
|
|
rerender();
|
|
}
|
|
(_a = options.onChange) == null ? void 0 : _a.call(options, instance2, sync);
|
|
}
|
|
};
|
|
const [instance] = React.useState(
|
|
() => new Virtualizer(resolvedOptions)
|
|
);
|
|
instance.setOptions(resolvedOptions);
|
|
useIsomorphicLayoutEffect(() => {
|
|
return instance._didMount();
|
|
}, []);
|
|
useIsomorphicLayoutEffect(() => {
|
|
return instance._willUpdate();
|
|
});
|
|
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 {
|
|
Virtualizer,
|
|
approxEqual,
|
|
debounce,
|
|
defaultKeyExtractor,
|
|
defaultRangeExtractor,
|
|
elementScroll,
|
|
measureElement,
|
|
memo,
|
|
notUndefined,
|
|
observeElementOffset,
|
|
observeElementRect,
|
|
observeWindowOffset,
|
|
observeWindowRect,
|
|
useVirtualizer,
|
|
useWindowVirtualizer,
|
|
windowScroll
|
|
};
|
|
//# sourceMappingURL=@tanstack_react-virtual.js.map
|