@tanstack/virtual-core 3.0.3 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/cjs/index.cjs +645 -0
  2. package/dist/cjs/index.cjs.map +1 -0
  3. package/dist/cjs/index.d.cts +126 -0
  4. package/dist/cjs/utils.cjs +59 -0
  5. package/dist/cjs/utils.cjs.map +1 -0
  6. package/{build/lib → dist/esm}/index.d.ts +1 -1
  7. package/dist/esm/index.js +645 -0
  8. package/dist/esm/index.js.map +1 -0
  9. package/dist/esm/utils.d.ts +10 -0
  10. package/dist/esm/utils.js +59 -0
  11. package/dist/esm/utils.js.map +1 -0
  12. package/package.json +33 -23
  13. package/src/index.ts +27 -17
  14. package/build/lib/_virtual/_rollupPluginBabelHelpers.esm.js +0 -27
  15. package/build/lib/_virtual/_rollupPluginBabelHelpers.esm.js.map +0 -1
  16. package/build/lib/_virtual/_rollupPluginBabelHelpers.js +0 -31
  17. package/build/lib/_virtual/_rollupPluginBabelHelpers.js.map +0 -1
  18. package/build/lib/_virtual/_rollupPluginBabelHelpers.mjs +0 -27
  19. package/build/lib/_virtual/_rollupPluginBabelHelpers.mjs.map +0 -1
  20. package/build/lib/index.esm.js +0 -642
  21. package/build/lib/index.esm.js.map +0 -1
  22. package/build/lib/index.js +0 -657
  23. package/build/lib/index.js.map +0 -1
  24. package/build/lib/index.mjs +0 -642
  25. package/build/lib/index.mjs.map +0 -1
  26. package/build/lib/utils.esm.js +0 -58
  27. package/build/lib/utils.esm.js.map +0 -1
  28. package/build/lib/utils.js +0 -64
  29. package/build/lib/utils.js.map +0 -1
  30. package/build/lib/utils.mjs +0 -58
  31. package/build/lib/utils.mjs.map +0 -1
  32. package/build/umd/index.development.js +0 -721
  33. package/build/umd/index.development.js.map +0 -1
  34. package/build/umd/index.production.js +0 -12
  35. package/build/umd/index.production.js.map +0 -1
  36. /package/{build/lib/utils.d.ts → dist/cjs/utils.d.cts} +0 -0
@@ -0,0 +1,645 @@
1
+ import { memo, notUndefined, approxEqual } from "./utils.js";
2
+ const defaultKeyExtractor = (index) => index;
3
+ const defaultRangeExtractor = (range) => {
4
+ const start = Math.max(range.startIndex - range.overscan, 0);
5
+ const end = Math.min(range.endIndex + range.overscan, range.count - 1);
6
+ const arr = [];
7
+ for (let i = start; i <= end; i++) {
8
+ arr.push(i);
9
+ }
10
+ return arr;
11
+ };
12
+ const observeElementRect = (instance, cb) => {
13
+ const element = instance.scrollElement;
14
+ if (!element) {
15
+ return;
16
+ }
17
+ const handler = (rect) => {
18
+ const { width, height } = rect;
19
+ cb({ width: Math.round(width), height: Math.round(height) });
20
+ };
21
+ handler(element.getBoundingClientRect());
22
+ const observer = new ResizeObserver((entries) => {
23
+ const entry = entries[0];
24
+ if (entry == null ? void 0 : entry.borderBoxSize) {
25
+ const box = entry.borderBoxSize[0];
26
+ if (box) {
27
+ handler({ width: box.inlineSize, height: box.blockSize });
28
+ return;
29
+ }
30
+ }
31
+ handler(element.getBoundingClientRect());
32
+ });
33
+ observer.observe(element, { box: "border-box" });
34
+ return () => {
35
+ observer.unobserve(element);
36
+ };
37
+ };
38
+ const observeWindowRect = (instance, cb) => {
39
+ const element = instance.scrollElement;
40
+ if (!element) {
41
+ return;
42
+ }
43
+ const handler = () => {
44
+ cb({ width: element.innerWidth, height: element.innerHeight });
45
+ };
46
+ handler();
47
+ element.addEventListener("resize", handler, {
48
+ passive: true
49
+ });
50
+ return () => {
51
+ element.removeEventListener("resize", handler);
52
+ };
53
+ };
54
+ const observeElementOffset = (instance, cb) => {
55
+ const element = instance.scrollElement;
56
+ if (!element) {
57
+ return;
58
+ }
59
+ const handler = () => {
60
+ cb(element[instance.options.horizontal ? "scrollLeft" : "scrollTop"]);
61
+ };
62
+ handler();
63
+ element.addEventListener("scroll", handler, {
64
+ passive: true
65
+ });
66
+ return () => {
67
+ element.removeEventListener("scroll", handler);
68
+ };
69
+ };
70
+ const observeWindowOffset = (instance, cb) => {
71
+ const element = instance.scrollElement;
72
+ if (!element) {
73
+ return;
74
+ }
75
+ const handler = () => {
76
+ cb(element[instance.options.horizontal ? "scrollX" : "scrollY"]);
77
+ };
78
+ handler();
79
+ element.addEventListener("scroll", handler, {
80
+ passive: true
81
+ });
82
+ return () => {
83
+ element.removeEventListener("scroll", handler);
84
+ };
85
+ };
86
+ const measureElement = (element, entry, instance) => {
87
+ if (entry == null ? void 0 : entry.borderBoxSize) {
88
+ const box = entry.borderBoxSize[0];
89
+ if (box) {
90
+ const size = Math.round(
91
+ box[instance.options.horizontal ? "inlineSize" : "blockSize"]
92
+ );
93
+ return size;
94
+ }
95
+ }
96
+ return Math.round(
97
+ element.getBoundingClientRect()[instance.options.horizontal ? "width" : "height"]
98
+ );
99
+ };
100
+ const windowScroll = (offset, {
101
+ adjustments = 0,
102
+ behavior
103
+ }, instance) => {
104
+ var _a, _b;
105
+ const toOffset = offset + adjustments;
106
+ (_b = (_a = instance.scrollElement) == null ? void 0 : _a.scrollTo) == null ? void 0 : _b.call(_a, {
107
+ [instance.options.horizontal ? "left" : "top"]: toOffset,
108
+ behavior
109
+ });
110
+ };
111
+ const elementScroll = (offset, {
112
+ adjustments = 0,
113
+ behavior
114
+ }, instance) => {
115
+ var _a, _b;
116
+ const toOffset = offset + adjustments;
117
+ (_b = (_a = instance.scrollElement) == null ? void 0 : _a.scrollTo) == null ? void 0 : _b.call(_a, {
118
+ [instance.options.horizontal ? "left" : "top"]: toOffset,
119
+ behavior
120
+ });
121
+ };
122
+ class Virtualizer {
123
+ constructor(opts) {
124
+ this.unsubs = [];
125
+ this.scrollElement = null;
126
+ this.isScrolling = false;
127
+ this.isScrollingTimeoutId = null;
128
+ this.scrollToIndexTimeoutId = null;
129
+ this.measurementsCache = [];
130
+ this.itemSizeCache = /* @__PURE__ */ new Map();
131
+ this.pendingMeasuredCacheIndexes = [];
132
+ this.scrollDirection = null;
133
+ this.scrollAdjustments = 0;
134
+ this.measureElementCache = /* @__PURE__ */ new Map();
135
+ this.observer = (() => {
136
+ let _ro = null;
137
+ const get = () => {
138
+ if (_ro) {
139
+ return _ro;
140
+ } else if (typeof ResizeObserver !== "undefined") {
141
+ return _ro = new ResizeObserver((entries) => {
142
+ entries.forEach((entry) => {
143
+ this._measureElement(entry.target, entry);
144
+ });
145
+ });
146
+ } else {
147
+ return null;
148
+ }
149
+ };
150
+ return {
151
+ disconnect: () => {
152
+ var _a;
153
+ return (_a = get()) == null ? void 0 : _a.disconnect();
154
+ },
155
+ observe: (target) => {
156
+ var _a;
157
+ return (_a = get()) == null ? void 0 : _a.observe(target, { box: "border-box" });
158
+ },
159
+ unobserve: (target) => {
160
+ var _a;
161
+ return (_a = get()) == null ? void 0 : _a.unobserve(target);
162
+ }
163
+ };
164
+ })();
165
+ this.range = null;
166
+ this.setOptions = (opts2) => {
167
+ Object.entries(opts2).forEach(([key, value]) => {
168
+ if (typeof value === "undefined")
169
+ delete opts2[key];
170
+ });
171
+ this.options = {
172
+ debug: false,
173
+ initialOffset: 0,
174
+ overscan: 1,
175
+ paddingStart: 0,
176
+ paddingEnd: 0,
177
+ scrollPaddingStart: 0,
178
+ scrollPaddingEnd: 0,
179
+ horizontal: false,
180
+ getItemKey: defaultKeyExtractor,
181
+ rangeExtractor: defaultRangeExtractor,
182
+ onChange: () => {
183
+ },
184
+ measureElement,
185
+ initialRect: { width: 0, height: 0 },
186
+ scrollMargin: 0,
187
+ scrollingDelay: 150,
188
+ indexAttribute: "data-index",
189
+ initialMeasurementsCache: [],
190
+ lanes: 1,
191
+ ...opts2
192
+ };
193
+ };
194
+ this.notify = (sync) => {
195
+ var _a, _b;
196
+ (_b = (_a = this.options).onChange) == null ? void 0 : _b.call(_a, this, sync);
197
+ };
198
+ this.maybeNotify = memo(
199
+ () => {
200
+ this.calculateRange();
201
+ return [
202
+ this.isScrolling,
203
+ this.range ? this.range.startIndex : null,
204
+ this.range ? this.range.endIndex : null
205
+ ];
206
+ },
207
+ (isScrolling) => {
208
+ this.notify(isScrolling);
209
+ },
210
+ {
211
+ key: process.env.NODE_ENV !== "production" && "maybeNotify",
212
+ debug: () => this.options.debug,
213
+ initialDeps: [
214
+ this.isScrolling,
215
+ this.range ? this.range.startIndex : null,
216
+ this.range ? this.range.endIndex : null
217
+ ]
218
+ }
219
+ );
220
+ this.cleanup = () => {
221
+ this.unsubs.filter(Boolean).forEach((d) => d());
222
+ this.unsubs = [];
223
+ this.scrollElement = null;
224
+ };
225
+ this._didMount = () => {
226
+ this.measureElementCache.forEach(this.observer.observe);
227
+ return () => {
228
+ this.observer.disconnect();
229
+ this.cleanup();
230
+ };
231
+ };
232
+ this._willUpdate = () => {
233
+ const scrollElement = this.options.getScrollElement();
234
+ if (this.scrollElement !== scrollElement) {
235
+ this.cleanup();
236
+ this.scrollElement = scrollElement;
237
+ this._scrollToOffset(this.scrollOffset, {
238
+ adjustments: void 0,
239
+ behavior: void 0
240
+ });
241
+ this.unsubs.push(
242
+ this.options.observeElementRect(this, (rect) => {
243
+ this.scrollRect = rect;
244
+ this.maybeNotify();
245
+ })
246
+ );
247
+ this.unsubs.push(
248
+ this.options.observeElementOffset(this, (offset) => {
249
+ this.scrollAdjustments = 0;
250
+ if (this.scrollOffset === offset) {
251
+ return;
252
+ }
253
+ if (this.isScrollingTimeoutId !== null) {
254
+ clearTimeout(this.isScrollingTimeoutId);
255
+ this.isScrollingTimeoutId = null;
256
+ }
257
+ this.isScrolling = true;
258
+ this.scrollDirection = this.scrollOffset < offset ? "forward" : "backward";
259
+ this.scrollOffset = offset;
260
+ this.maybeNotify();
261
+ this.isScrollingTimeoutId = setTimeout(() => {
262
+ this.isScrollingTimeoutId = null;
263
+ this.isScrolling = false;
264
+ this.scrollDirection = null;
265
+ this.maybeNotify();
266
+ }, this.options.scrollingDelay);
267
+ })
268
+ );
269
+ }
270
+ };
271
+ this.getSize = () => {
272
+ return this.scrollRect[this.options.horizontal ? "width" : "height"];
273
+ };
274
+ this.memoOptions = memo(
275
+ () => [
276
+ this.options.count,
277
+ this.options.paddingStart,
278
+ this.options.scrollMargin,
279
+ this.options.getItemKey
280
+ ],
281
+ (count, paddingStart, scrollMargin, getItemKey) => {
282
+ this.pendingMeasuredCacheIndexes = [];
283
+ return {
284
+ count,
285
+ paddingStart,
286
+ scrollMargin,
287
+ getItemKey
288
+ };
289
+ },
290
+ {
291
+ key: false
292
+ }
293
+ );
294
+ this.getFurthestMeasurement = (measurements, index) => {
295
+ const furthestMeasurementsFound = /* @__PURE__ */ new Map();
296
+ const furthestMeasurements = /* @__PURE__ */ new Map();
297
+ for (let m = index - 1; m >= 0; m--) {
298
+ const measurement = measurements[m];
299
+ if (furthestMeasurementsFound.has(measurement.lane)) {
300
+ continue;
301
+ }
302
+ const previousFurthestMeasurement = furthestMeasurements.get(
303
+ measurement.lane
304
+ );
305
+ if (previousFurthestMeasurement == null || measurement.end > previousFurthestMeasurement.end) {
306
+ furthestMeasurements.set(measurement.lane, measurement);
307
+ } else if (measurement.end < previousFurthestMeasurement.end) {
308
+ furthestMeasurementsFound.set(measurement.lane, true);
309
+ }
310
+ if (furthestMeasurementsFound.size === this.options.lanes) {
311
+ break;
312
+ }
313
+ }
314
+ return furthestMeasurements.size === this.options.lanes ? Array.from(furthestMeasurements.values()).sort((a, b) => {
315
+ if (a.end === b.end) {
316
+ return a.index - b.index;
317
+ }
318
+ return a.end - b.end;
319
+ })[0] : void 0;
320
+ };
321
+ this.getMeasurements = memo(
322
+ () => [this.memoOptions(), this.itemSizeCache],
323
+ ({ count, paddingStart, scrollMargin, getItemKey }, itemSizeCache) => {
324
+ const min = this.pendingMeasuredCacheIndexes.length > 0 ? Math.min(...this.pendingMeasuredCacheIndexes) : 0;
325
+ this.pendingMeasuredCacheIndexes = [];
326
+ const measurements = this.measurementsCache.slice(0, min);
327
+ for (let i = min; i < count; i++) {
328
+ const key = getItemKey(i);
329
+ const furthestMeasurement = this.options.lanes === 1 ? measurements[i - 1] : this.getFurthestMeasurement(measurements, i);
330
+ const start = furthestMeasurement ? furthestMeasurement.end : paddingStart + scrollMargin;
331
+ const measuredSize = itemSizeCache.get(key);
332
+ const size = typeof measuredSize === "number" ? measuredSize : this.options.estimateSize(i);
333
+ const end = start + size;
334
+ const lane = furthestMeasurement ? furthestMeasurement.lane : i % this.options.lanes;
335
+ measurements[i] = {
336
+ index: i,
337
+ start,
338
+ size,
339
+ end,
340
+ key,
341
+ lane
342
+ };
343
+ }
344
+ this.measurementsCache = measurements;
345
+ return measurements;
346
+ },
347
+ {
348
+ key: process.env.NODE_ENV !== "production" && "getMeasurements",
349
+ debug: () => this.options.debug
350
+ }
351
+ );
352
+ this.calculateRange = memo(
353
+ () => [this.getMeasurements(), this.getSize(), this.scrollOffset],
354
+ (measurements, outerSize, scrollOffset) => {
355
+ return this.range = measurements.length > 0 && outerSize > 0 ? calculateRange({
356
+ measurements,
357
+ outerSize,
358
+ scrollOffset
359
+ }) : null;
360
+ },
361
+ {
362
+ key: process.env.NODE_ENV !== "production" && "calculateRange",
363
+ debug: () => this.options.debug
364
+ }
365
+ );
366
+ this.getIndexes = memo(
367
+ () => [
368
+ this.options.rangeExtractor,
369
+ this.calculateRange(),
370
+ this.options.overscan,
371
+ this.options.count
372
+ ],
373
+ (rangeExtractor, range, overscan, count) => {
374
+ return range === null ? [] : rangeExtractor({
375
+ ...range,
376
+ overscan,
377
+ count
378
+ });
379
+ },
380
+ {
381
+ key: process.env.NODE_ENV !== "production" && "getIndexes",
382
+ debug: () => this.options.debug
383
+ }
384
+ );
385
+ this.indexFromElement = (node) => {
386
+ const attributeName = this.options.indexAttribute;
387
+ const indexStr = node.getAttribute(attributeName);
388
+ if (!indexStr) {
389
+ console.warn(
390
+ `Missing attribute name '${attributeName}={index}' on measured element.`
391
+ );
392
+ return -1;
393
+ }
394
+ return parseInt(indexStr, 10);
395
+ };
396
+ this._measureElement = (node, entry) => {
397
+ const item = this.measurementsCache[this.indexFromElement(node)];
398
+ if (!item || !node.isConnected) {
399
+ this.measureElementCache.forEach((cached, key) => {
400
+ if (cached === node) {
401
+ this.observer.unobserve(node);
402
+ this.measureElementCache.delete(key);
403
+ }
404
+ });
405
+ return;
406
+ }
407
+ const prevNode = this.measureElementCache.get(item.key);
408
+ if (prevNode !== node) {
409
+ if (prevNode) {
410
+ this.observer.unobserve(prevNode);
411
+ }
412
+ this.observer.observe(node);
413
+ this.measureElementCache.set(item.key, node);
414
+ }
415
+ const measuredItemSize = this.options.measureElement(node, entry, this);
416
+ this.resizeItem(item, measuredItemSize);
417
+ };
418
+ this.resizeItem = (item, size) => {
419
+ const itemSize = this.itemSizeCache.get(item.key) ?? item.size;
420
+ const delta = size - itemSize;
421
+ if (delta !== 0) {
422
+ if (item.start < this.scrollOffset + this.scrollAdjustments) {
423
+ if (process.env.NODE_ENV !== "production" && this.options.debug) {
424
+ console.info("correction", delta);
425
+ }
426
+ this._scrollToOffset(this.scrollOffset, {
427
+ adjustments: this.scrollAdjustments += delta,
428
+ behavior: void 0
429
+ });
430
+ }
431
+ this.pendingMeasuredCacheIndexes.push(item.index);
432
+ this.itemSizeCache = new Map(this.itemSizeCache.set(item.key, size));
433
+ this.notify(false);
434
+ }
435
+ };
436
+ this.measureElement = (node) => {
437
+ if (!node) {
438
+ return;
439
+ }
440
+ this._measureElement(node, void 0);
441
+ };
442
+ this.getVirtualItems = memo(
443
+ () => [this.getIndexes(), this.getMeasurements()],
444
+ (indexes, measurements) => {
445
+ const virtualItems = [];
446
+ for (let k = 0, len = indexes.length; k < len; k++) {
447
+ const i = indexes[k];
448
+ const measurement = measurements[i];
449
+ virtualItems.push(measurement);
450
+ }
451
+ return virtualItems;
452
+ },
453
+ {
454
+ key: process.env.NODE_ENV !== "production" && "getIndexes",
455
+ debug: () => this.options.debug
456
+ }
457
+ );
458
+ this.getVirtualItemForOffset = (offset) => {
459
+ const measurements = this.getMeasurements();
460
+ return notUndefined(
461
+ measurements[findNearestBinarySearch(
462
+ 0,
463
+ measurements.length - 1,
464
+ (index) => notUndefined(measurements[index]).start,
465
+ offset
466
+ )]
467
+ );
468
+ };
469
+ this.getOffsetForAlignment = (toOffset, align) => {
470
+ const size = this.getSize();
471
+ if (align === "auto") {
472
+ if (toOffset <= this.scrollOffset) {
473
+ align = "start";
474
+ } else if (toOffset >= this.scrollOffset + size) {
475
+ align = "end";
476
+ } else {
477
+ align = "start";
478
+ }
479
+ }
480
+ if (align === "start") {
481
+ toOffset = toOffset;
482
+ } else if (align === "end") {
483
+ toOffset = toOffset - size;
484
+ } else if (align === "center") {
485
+ toOffset = toOffset - size / 2;
486
+ }
487
+ const scrollSizeProp = this.options.horizontal ? "scrollWidth" : "scrollHeight";
488
+ const scrollSize = this.scrollElement ? "document" in this.scrollElement ? this.scrollElement.document.documentElement[scrollSizeProp] : this.scrollElement[scrollSizeProp] : 0;
489
+ const maxOffset = scrollSize - this.getSize();
490
+ return Math.max(Math.min(maxOffset, toOffset), 0);
491
+ };
492
+ this.getOffsetForIndex = (index, align = "auto") => {
493
+ index = Math.max(0, Math.min(index, this.options.count - 1));
494
+ const measurement = notUndefined(this.getMeasurements()[index]);
495
+ if (align === "auto") {
496
+ if (measurement.end >= this.scrollOffset + this.getSize() - this.options.scrollPaddingEnd) {
497
+ align = "end";
498
+ } else if (measurement.start <= this.scrollOffset + this.options.scrollPaddingStart) {
499
+ align = "start";
500
+ } else {
501
+ return [this.scrollOffset, align];
502
+ }
503
+ }
504
+ const toOffset = align === "end" ? measurement.end + this.options.scrollPaddingEnd : measurement.start - this.options.scrollPaddingStart;
505
+ return [this.getOffsetForAlignment(toOffset, align), align];
506
+ };
507
+ this.isDynamicMode = () => this.measureElementCache.size > 0;
508
+ this.cancelScrollToIndex = () => {
509
+ if (this.scrollToIndexTimeoutId !== null) {
510
+ clearTimeout(this.scrollToIndexTimeoutId);
511
+ this.scrollToIndexTimeoutId = null;
512
+ }
513
+ };
514
+ this.scrollToOffset = (toOffset, { align = "start", behavior } = {}) => {
515
+ this.cancelScrollToIndex();
516
+ if (behavior === "smooth" && this.isDynamicMode()) {
517
+ console.warn(
518
+ "The `smooth` scroll behavior is not fully supported with dynamic size."
519
+ );
520
+ }
521
+ this._scrollToOffset(this.getOffsetForAlignment(toOffset, align), {
522
+ adjustments: void 0,
523
+ behavior
524
+ });
525
+ };
526
+ this.scrollToIndex = (index, { align: initialAlign = "auto", behavior } = {}) => {
527
+ index = Math.max(0, Math.min(index, this.options.count - 1));
528
+ this.cancelScrollToIndex();
529
+ if (behavior === "smooth" && this.isDynamicMode()) {
530
+ console.warn(
531
+ "The `smooth` scroll behavior is not fully supported with dynamic size."
532
+ );
533
+ }
534
+ const [toOffset, align] = this.getOffsetForIndex(index, initialAlign);
535
+ this._scrollToOffset(toOffset, { adjustments: void 0, behavior });
536
+ if (behavior !== "smooth" && this.isDynamicMode()) {
537
+ this.scrollToIndexTimeoutId = setTimeout(() => {
538
+ this.scrollToIndexTimeoutId = null;
539
+ const elementInDOM = this.measureElementCache.has(
540
+ this.options.getItemKey(index)
541
+ );
542
+ if (elementInDOM) {
543
+ const [toOffset2] = this.getOffsetForIndex(index, align);
544
+ if (!approxEqual(toOffset2, this.scrollOffset)) {
545
+ this.scrollToIndex(index, { align, behavior });
546
+ }
547
+ } else {
548
+ this.scrollToIndex(index, { align, behavior });
549
+ }
550
+ });
551
+ }
552
+ };
553
+ this.scrollBy = (delta, { behavior } = {}) => {
554
+ this.cancelScrollToIndex();
555
+ if (behavior === "smooth" && this.isDynamicMode()) {
556
+ console.warn(
557
+ "The `smooth` scroll behavior is not fully supported with dynamic size."
558
+ );
559
+ }
560
+ this._scrollToOffset(this.scrollOffset + delta, {
561
+ adjustments: void 0,
562
+ behavior
563
+ });
564
+ };
565
+ this.getTotalSize = () => {
566
+ var _a;
567
+ const measurements = this.getMeasurements();
568
+ let end;
569
+ if (measurements.length === 0) {
570
+ end = this.options.paddingStart;
571
+ } else {
572
+ end = this.options.lanes === 1 ? ((_a = measurements[measurements.length - 1]) == null ? void 0 : _a.end) ?? 0 : Math.max(
573
+ ...measurements.slice(-this.options.lanes).map((m) => m.end)
574
+ );
575
+ }
576
+ return end - this.options.scrollMargin + this.options.paddingEnd;
577
+ };
578
+ this._scrollToOffset = (offset, {
579
+ adjustments,
580
+ behavior
581
+ }) => {
582
+ this.options.scrollToFn(offset, { behavior, adjustments }, this);
583
+ };
584
+ this.measure = () => {
585
+ this.itemSizeCache = /* @__PURE__ */ new Map();
586
+ this.notify(false);
587
+ };
588
+ this.setOptions(opts);
589
+ this.scrollRect = this.options.initialRect;
590
+ this.scrollOffset = this.options.initialOffset;
591
+ this.measurementsCache = this.options.initialMeasurementsCache;
592
+ this.measurementsCache.forEach((item) => {
593
+ this.itemSizeCache.set(item.key, item.size);
594
+ });
595
+ this.maybeNotify();
596
+ }
597
+ }
598
+ const findNearestBinarySearch = (low, high, getCurrentValue, value) => {
599
+ while (low <= high) {
600
+ const middle = (low + high) / 2 | 0;
601
+ const currentValue = getCurrentValue(middle);
602
+ if (currentValue < value) {
603
+ low = middle + 1;
604
+ } else if (currentValue > value) {
605
+ high = middle - 1;
606
+ } else {
607
+ return middle;
608
+ }
609
+ }
610
+ if (low > 0) {
611
+ return low - 1;
612
+ } else {
613
+ return 0;
614
+ }
615
+ };
616
+ function calculateRange({
617
+ measurements,
618
+ outerSize,
619
+ scrollOffset
620
+ }) {
621
+ const count = measurements.length - 1;
622
+ const getOffset = (index) => measurements[index].start;
623
+ const startIndex = findNearestBinarySearch(0, count, getOffset, scrollOffset);
624
+ let endIndex = startIndex;
625
+ while (endIndex < count && measurements[endIndex].end < scrollOffset + outerSize) {
626
+ endIndex++;
627
+ }
628
+ return { startIndex, endIndex };
629
+ }
630
+ export {
631
+ Virtualizer,
632
+ approxEqual,
633
+ defaultKeyExtractor,
634
+ defaultRangeExtractor,
635
+ elementScroll,
636
+ measureElement,
637
+ memo,
638
+ notUndefined,
639
+ observeElementOffset,
640
+ observeElementRect,
641
+ observeWindowOffset,
642
+ observeWindowRect,
643
+ windowScroll
644
+ };
645
+ //# sourceMappingURL=index.js.map