@usefragments/ui 1.0.0 → 1.0.2

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.
@@ -1,822 +0,0 @@
1
- import { memo, notUndefined, approxEqual, debounce } from "./utils.js";
2
- const getRect = (element) => {
3
- const { offsetWidth, offsetHeight } = element;
4
- return { width: offsetWidth, height: offsetHeight };
5
- };
6
- const defaultKeyExtractor = (index) => index;
7
- const defaultRangeExtractor = (range) => {
8
- const start = Math.max(range.startIndex - range.overscan, 0);
9
- const end = Math.min(range.endIndex + range.overscan, range.count - 1);
10
- const arr = [];
11
- for (let i = start; i <= end; i++) {
12
- arr.push(i);
13
- }
14
- return arr;
15
- };
16
- const observeElementRect = (instance, cb) => {
17
- const element = instance.scrollElement;
18
- if (!element) {
19
- return;
20
- }
21
- const targetWindow = instance.targetWindow;
22
- if (!targetWindow) {
23
- return;
24
- }
25
- const handler = (rect) => {
26
- const { width, height } = rect;
27
- cb({ width: Math.round(width), height: Math.round(height) });
28
- };
29
- handler(getRect(element));
30
- if (!targetWindow.ResizeObserver) {
31
- return () => {
32
- };
33
- }
34
- const observer = new targetWindow.ResizeObserver((entries) => {
35
- const run = () => {
36
- const entry = entries[0];
37
- if (entry == null ? void 0 : entry.borderBoxSize) {
38
- const box = entry.borderBoxSize[0];
39
- if (box) {
40
- handler({ width: box.inlineSize, height: box.blockSize });
41
- return;
42
- }
43
- }
44
- handler(getRect(element));
45
- };
46
- instance.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
47
- });
48
- observer.observe(element, { box: "border-box" });
49
- return () => {
50
- observer.unobserve(element);
51
- };
52
- };
53
- const addEventListenerOptions = {
54
- passive: true
55
- };
56
- const supportsScrollend = typeof window == "undefined" ? true : "onscrollend" in window;
57
- const observeElementOffset = (instance, cb) => {
58
- const element = instance.scrollElement;
59
- if (!element) {
60
- return;
61
- }
62
- const targetWindow = instance.targetWindow;
63
- if (!targetWindow) {
64
- return;
65
- }
66
- let offset = 0;
67
- const fallback = instance.options.useScrollendEvent && supportsScrollend ? () => void 0 : debounce(
68
- targetWindow,
69
- () => {
70
- cb(offset, false);
71
- },
72
- instance.options.isScrollingResetDelay
73
- );
74
- const createHandler = (isScrolling) => () => {
75
- const { horizontal, isRtl } = instance.options;
76
- offset = horizontal ? element["scrollLeft"] * (isRtl && -1 || 1) : element["scrollTop"];
77
- fallback();
78
- cb(offset, isScrolling);
79
- };
80
- const handler = createHandler(true);
81
- const endHandler = createHandler(false);
82
- element.addEventListener("scroll", handler, addEventListenerOptions);
83
- const registerScrollendEvent = instance.options.useScrollendEvent && supportsScrollend;
84
- if (registerScrollendEvent) {
85
- element.addEventListener("scrollend", endHandler, addEventListenerOptions);
86
- }
87
- return () => {
88
- element.removeEventListener("scroll", handler);
89
- if (registerScrollendEvent) {
90
- element.removeEventListener("scrollend", endHandler);
91
- }
92
- };
93
- };
94
- const measureElement = (element, entry, instance) => {
95
- if (entry == null ? void 0 : entry.borderBoxSize) {
96
- const box = entry.borderBoxSize[0];
97
- if (box) {
98
- const size = Math.round(
99
- box[instance.options.horizontal ? "inlineSize" : "blockSize"]
100
- );
101
- return size;
102
- }
103
- }
104
- return element[instance.options.horizontal ? "offsetWidth" : "offsetHeight"];
105
- };
106
- const elementScroll = (offset, {
107
- adjustments = 0,
108
- behavior
109
- }, instance) => {
110
- var _a, _b;
111
- const toOffset = offset + adjustments;
112
- (_b = (_a = instance.scrollElement) == null ? void 0 : _a.scrollTo) == null ? void 0 : _b.call(_a, {
113
- [instance.options.horizontal ? "left" : "top"]: toOffset,
114
- behavior
115
- });
116
- };
117
- class Virtualizer {
118
- constructor(opts) {
119
- this.unsubs = [];
120
- this.scrollElement = null;
121
- this.targetWindow = null;
122
- this.isScrolling = false;
123
- this.currentScrollToIndex = null;
124
- this.measurementsCache = [];
125
- this.itemSizeCache = /* @__PURE__ */ new Map();
126
- this.laneAssignments = /* @__PURE__ */ new Map();
127
- this.pendingMeasuredCacheIndexes = [];
128
- this.prevLanes = void 0;
129
- this.lanesChangedFlag = false;
130
- this.lanesSettling = false;
131
- this.scrollRect = null;
132
- this.scrollOffset = null;
133
- this.scrollDirection = null;
134
- this.scrollAdjustments = 0;
135
- this.elementsCache = /* @__PURE__ */ new Map();
136
- this.observer = /* @__PURE__ */ (() => {
137
- let _ro = null;
138
- const get = () => {
139
- if (_ro) {
140
- return _ro;
141
- }
142
- if (!this.targetWindow || !this.targetWindow.ResizeObserver) {
143
- return null;
144
- }
145
- return _ro = new this.targetWindow.ResizeObserver((entries) => {
146
- entries.forEach((entry) => {
147
- const run = () => {
148
- this._measureElement(entry.target, entry);
149
- };
150
- this.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
151
- });
152
- });
153
- };
154
- return {
155
- disconnect: () => {
156
- var _a;
157
- (_a = get()) == null ? void 0 : _a.disconnect();
158
- _ro = null;
159
- },
160
- observe: (target) => {
161
- var _a;
162
- return (_a = get()) == null ? void 0 : _a.observe(target, { box: "border-box" });
163
- },
164
- unobserve: (target) => {
165
- var _a;
166
- return (_a = get()) == null ? void 0 : _a.unobserve(target);
167
- }
168
- };
169
- })();
170
- this.range = null;
171
- this.setOptions = (opts2) => {
172
- Object.entries(opts2).forEach(([key, value]) => {
173
- if (typeof value === "undefined") delete opts2[key];
174
- });
175
- this.options = {
176
- debug: false,
177
- initialOffset: 0,
178
- overscan: 1,
179
- paddingStart: 0,
180
- paddingEnd: 0,
181
- scrollPaddingStart: 0,
182
- scrollPaddingEnd: 0,
183
- horizontal: false,
184
- getItemKey: defaultKeyExtractor,
185
- rangeExtractor: defaultRangeExtractor,
186
- onChange: () => {
187
- },
188
- measureElement,
189
- initialRect: { width: 0, height: 0 },
190
- scrollMargin: 0,
191
- gap: 0,
192
- indexAttribute: "data-index",
193
- initialMeasurementsCache: [],
194
- lanes: 1,
195
- isScrollingResetDelay: 150,
196
- enabled: true,
197
- isRtl: false,
198
- useScrollendEvent: false,
199
- useAnimationFrameWithResizeObserver: false,
200
- ...opts2
201
- };
202
- };
203
- this.notify = (sync) => {
204
- var _a, _b;
205
- (_b = (_a = this.options).onChange) == null ? void 0 : _b.call(_a, this, sync);
206
- };
207
- this.maybeNotify = memo(
208
- () => {
209
- this.calculateRange();
210
- return [
211
- this.isScrolling,
212
- this.range ? this.range.startIndex : null,
213
- this.range ? this.range.endIndex : null
214
- ];
215
- },
216
- (isScrolling) => {
217
- this.notify(isScrolling);
218
- },
219
- {
220
- key: process.env.NODE_ENV !== "production" && "maybeNotify",
221
- debug: () => this.options.debug,
222
- initialDeps: [
223
- this.isScrolling,
224
- this.range ? this.range.startIndex : null,
225
- this.range ? this.range.endIndex : null
226
- ]
227
- }
228
- );
229
- this.cleanup = () => {
230
- this.unsubs.filter(Boolean).forEach((d) => d());
231
- this.unsubs = [];
232
- this.observer.disconnect();
233
- this.scrollElement = null;
234
- this.targetWindow = null;
235
- };
236
- this._didMount = () => {
237
- return () => {
238
- this.cleanup();
239
- };
240
- };
241
- this._willUpdate = () => {
242
- var _a;
243
- const scrollElement = this.options.enabled ? this.options.getScrollElement() : null;
244
- if (this.scrollElement !== scrollElement) {
245
- this.cleanup();
246
- if (!scrollElement) {
247
- this.maybeNotify();
248
- return;
249
- }
250
- this.scrollElement = scrollElement;
251
- if (this.scrollElement && "ownerDocument" in this.scrollElement) {
252
- this.targetWindow = this.scrollElement.ownerDocument.defaultView;
253
- } else {
254
- this.targetWindow = ((_a = this.scrollElement) == null ? void 0 : _a.window) ?? null;
255
- }
256
- this.elementsCache.forEach((cached) => {
257
- this.observer.observe(cached);
258
- });
259
- this.unsubs.push(
260
- this.options.observeElementRect(this, (rect) => {
261
- this.scrollRect = rect;
262
- this.maybeNotify();
263
- })
264
- );
265
- this.unsubs.push(
266
- this.options.observeElementOffset(this, (offset, isScrolling) => {
267
- this.scrollAdjustments = 0;
268
- this.scrollDirection = isScrolling ? this.getScrollOffset() < offset ? "forward" : "backward" : null;
269
- this.scrollOffset = offset;
270
- this.isScrolling = isScrolling;
271
- this.maybeNotify();
272
- })
273
- );
274
- this._scrollToOffset(this.getScrollOffset(), {
275
- adjustments: void 0,
276
- behavior: void 0
277
- });
278
- }
279
- };
280
- this.getSize = () => {
281
- if (!this.options.enabled) {
282
- this.scrollRect = null;
283
- return 0;
284
- }
285
- this.scrollRect = this.scrollRect ?? this.options.initialRect;
286
- return this.scrollRect[this.options.horizontal ? "width" : "height"];
287
- };
288
- this.getScrollOffset = () => {
289
- if (!this.options.enabled) {
290
- this.scrollOffset = null;
291
- return 0;
292
- }
293
- this.scrollOffset = this.scrollOffset ?? (typeof this.options.initialOffset === "function" ? this.options.initialOffset() : this.options.initialOffset);
294
- return this.scrollOffset;
295
- };
296
- this.getFurthestMeasurement = (measurements, index) => {
297
- const furthestMeasurementsFound = /* @__PURE__ */ new Map();
298
- const furthestMeasurements = /* @__PURE__ */ new Map();
299
- for (let m = index - 1; m >= 0; m--) {
300
- const measurement = measurements[m];
301
- if (furthestMeasurementsFound.has(measurement.lane)) {
302
- continue;
303
- }
304
- const previousFurthestMeasurement = furthestMeasurements.get(
305
- measurement.lane
306
- );
307
- if (previousFurthestMeasurement == null || measurement.end > previousFurthestMeasurement.end) {
308
- furthestMeasurements.set(measurement.lane, measurement);
309
- } else if (measurement.end < previousFurthestMeasurement.end) {
310
- furthestMeasurementsFound.set(measurement.lane, true);
311
- }
312
- if (furthestMeasurementsFound.size === this.options.lanes) {
313
- break;
314
- }
315
- }
316
- return furthestMeasurements.size === this.options.lanes ? Array.from(furthestMeasurements.values()).sort((a, b) => {
317
- if (a.end === b.end) {
318
- return a.index - b.index;
319
- }
320
- return a.end - b.end;
321
- })[0] : void 0;
322
- };
323
- this.getMeasurementOptions = memo(
324
- () => [
325
- this.options.count,
326
- this.options.paddingStart,
327
- this.options.scrollMargin,
328
- this.options.getItemKey,
329
- this.options.enabled,
330
- this.options.lanes
331
- ],
332
- (count, paddingStart, scrollMargin, getItemKey, enabled, lanes) => {
333
- const lanesChanged = this.prevLanes !== void 0 && this.prevLanes !== lanes;
334
- if (lanesChanged) {
335
- this.lanesChangedFlag = true;
336
- }
337
- this.prevLanes = lanes;
338
- this.pendingMeasuredCacheIndexes = [];
339
- return {
340
- count,
341
- paddingStart,
342
- scrollMargin,
343
- getItemKey,
344
- enabled,
345
- lanes
346
- };
347
- },
348
- {
349
- key: false
350
- }
351
- );
352
- this.getMeasurements = memo(
353
- () => [this.getMeasurementOptions(), this.itemSizeCache],
354
- ({ count, paddingStart, scrollMargin, getItemKey, enabled, lanes }, itemSizeCache) => {
355
- if (!enabled) {
356
- this.measurementsCache = [];
357
- this.itemSizeCache.clear();
358
- this.laneAssignments.clear();
359
- return [];
360
- }
361
- if (this.laneAssignments.size > count) {
362
- for (const index of this.laneAssignments.keys()) {
363
- if (index >= count) {
364
- this.laneAssignments.delete(index);
365
- }
366
- }
367
- }
368
- if (this.lanesChangedFlag) {
369
- this.lanesChangedFlag = false;
370
- this.lanesSettling = true;
371
- this.measurementsCache = [];
372
- this.itemSizeCache.clear();
373
- this.laneAssignments.clear();
374
- this.pendingMeasuredCacheIndexes = [];
375
- }
376
- if (this.measurementsCache.length === 0 && !this.lanesSettling) {
377
- this.measurementsCache = this.options.initialMeasurementsCache;
378
- this.measurementsCache.forEach((item) => {
379
- this.itemSizeCache.set(item.key, item.size);
380
- });
381
- }
382
- const min = this.lanesSettling ? 0 : this.pendingMeasuredCacheIndexes.length > 0 ? Math.min(...this.pendingMeasuredCacheIndexes) : 0;
383
- this.pendingMeasuredCacheIndexes = [];
384
- if (this.lanesSettling && this.measurementsCache.length === count) {
385
- this.lanesSettling = false;
386
- }
387
- const measurements = this.measurementsCache.slice(0, min);
388
- const laneLastIndex = new Array(lanes).fill(
389
- void 0
390
- );
391
- for (let m = 0; m < min; m++) {
392
- const item = measurements[m];
393
- if (item) {
394
- laneLastIndex[item.lane] = m;
395
- }
396
- }
397
- for (let i = min; i < count; i++) {
398
- const key = getItemKey(i);
399
- const cachedLane = this.laneAssignments.get(i);
400
- let lane;
401
- let start;
402
- if (cachedLane !== void 0 && this.options.lanes > 1) {
403
- lane = cachedLane;
404
- const prevIndex = laneLastIndex[lane];
405
- const prevInLane = prevIndex !== void 0 ? measurements[prevIndex] : void 0;
406
- start = prevInLane ? prevInLane.end + this.options.gap : paddingStart + scrollMargin;
407
- } else {
408
- const furthestMeasurement = this.options.lanes === 1 ? measurements[i - 1] : this.getFurthestMeasurement(measurements, i);
409
- start = furthestMeasurement ? furthestMeasurement.end + this.options.gap : paddingStart + scrollMargin;
410
- lane = furthestMeasurement ? furthestMeasurement.lane : i % this.options.lanes;
411
- if (this.options.lanes > 1) {
412
- this.laneAssignments.set(i, lane);
413
- }
414
- }
415
- const measuredSize = itemSizeCache.get(key);
416
- const size = typeof measuredSize === "number" ? measuredSize : this.options.estimateSize(i);
417
- const end = start + size;
418
- measurements[i] = {
419
- index: i,
420
- start,
421
- size,
422
- end,
423
- key,
424
- lane
425
- };
426
- laneLastIndex[lane] = i;
427
- }
428
- this.measurementsCache = measurements;
429
- return measurements;
430
- },
431
- {
432
- key: process.env.NODE_ENV !== "production" && "getMeasurements",
433
- debug: () => this.options.debug
434
- }
435
- );
436
- this.calculateRange = memo(
437
- () => [
438
- this.getMeasurements(),
439
- this.getSize(),
440
- this.getScrollOffset(),
441
- this.options.lanes
442
- ],
443
- (measurements, outerSize, scrollOffset, lanes) => {
444
- return this.range = measurements.length > 0 && outerSize > 0 ? calculateRange({
445
- measurements,
446
- outerSize,
447
- scrollOffset,
448
- lanes
449
- }) : null;
450
- },
451
- {
452
- key: process.env.NODE_ENV !== "production" && "calculateRange",
453
- debug: () => this.options.debug
454
- }
455
- );
456
- this.getVirtualIndexes = memo(
457
- () => {
458
- let startIndex = null;
459
- let endIndex = null;
460
- const range = this.calculateRange();
461
- if (range) {
462
- startIndex = range.startIndex;
463
- endIndex = range.endIndex;
464
- }
465
- this.maybeNotify.updateDeps([this.isScrolling, startIndex, endIndex]);
466
- return [
467
- this.options.rangeExtractor,
468
- this.options.overscan,
469
- this.options.count,
470
- startIndex,
471
- endIndex
472
- ];
473
- },
474
- (rangeExtractor, overscan, count, startIndex, endIndex) => {
475
- return startIndex === null || endIndex === null ? [] : rangeExtractor({
476
- startIndex,
477
- endIndex,
478
- overscan,
479
- count
480
- });
481
- },
482
- {
483
- key: process.env.NODE_ENV !== "production" && "getVirtualIndexes",
484
- debug: () => this.options.debug
485
- }
486
- );
487
- this.indexFromElement = (node) => {
488
- const attributeName = this.options.indexAttribute;
489
- const indexStr = node.getAttribute(attributeName);
490
- if (!indexStr) {
491
- console.warn(
492
- `Missing attribute name '${attributeName}={index}' on measured element.`
493
- );
494
- return -1;
495
- }
496
- return parseInt(indexStr, 10);
497
- };
498
- this._measureElement = (node, entry) => {
499
- const index = this.indexFromElement(node);
500
- const item = this.measurementsCache[index];
501
- if (!item) {
502
- return;
503
- }
504
- const key = item.key;
505
- const prevNode = this.elementsCache.get(key);
506
- if (prevNode !== node) {
507
- if (prevNode) {
508
- this.observer.unobserve(prevNode);
509
- }
510
- this.observer.observe(node);
511
- this.elementsCache.set(key, node);
512
- }
513
- if (node.isConnected) {
514
- this.resizeItem(index, this.options.measureElement(node, entry, this));
515
- }
516
- };
517
- this.resizeItem = (index, size) => {
518
- const item = this.measurementsCache[index];
519
- if (!item) {
520
- return;
521
- }
522
- const itemSize = this.itemSizeCache.get(item.key) ?? item.size;
523
- const delta = size - itemSize;
524
- if (delta !== 0) {
525
- if (this.shouldAdjustScrollPositionOnItemSizeChange !== void 0 ? this.shouldAdjustScrollPositionOnItemSizeChange(item, delta, this) : item.start < this.getScrollOffset() + this.scrollAdjustments) {
526
- if (process.env.NODE_ENV !== "production" && this.options.debug) {
527
- console.info("correction", delta);
528
- }
529
- this._scrollToOffset(this.getScrollOffset(), {
530
- adjustments: this.scrollAdjustments += delta,
531
- behavior: void 0
532
- });
533
- }
534
- this.pendingMeasuredCacheIndexes.push(item.index);
535
- this.itemSizeCache = new Map(this.itemSizeCache.set(item.key, size));
536
- this.notify(false);
537
- }
538
- };
539
- this.measureElement = (node) => {
540
- if (!node) {
541
- this.elementsCache.forEach((cached, key) => {
542
- if (!cached.isConnected) {
543
- this.observer.unobserve(cached);
544
- this.elementsCache.delete(key);
545
- }
546
- });
547
- return;
548
- }
549
- this._measureElement(node, void 0);
550
- };
551
- this.getVirtualItems = memo(
552
- () => [this.getVirtualIndexes(), this.getMeasurements()],
553
- (indexes, measurements) => {
554
- const virtualItems = [];
555
- for (let k = 0, len = indexes.length; k < len; k++) {
556
- const i = indexes[k];
557
- const measurement = measurements[i];
558
- virtualItems.push(measurement);
559
- }
560
- return virtualItems;
561
- },
562
- {
563
- key: process.env.NODE_ENV !== "production" && "getVirtualItems",
564
- debug: () => this.options.debug
565
- }
566
- );
567
- this.getVirtualItemForOffset = (offset) => {
568
- const measurements = this.getMeasurements();
569
- if (measurements.length === 0) {
570
- return void 0;
571
- }
572
- return notUndefined(
573
- measurements[findNearestBinarySearch(
574
- 0,
575
- measurements.length - 1,
576
- (index) => notUndefined(measurements[index]).start,
577
- offset
578
- )]
579
- );
580
- };
581
- this.getMaxScrollOffset = () => {
582
- if (!this.scrollElement) return 0;
583
- if ("scrollHeight" in this.scrollElement) {
584
- return this.options.horizontal ? this.scrollElement.scrollWidth - this.scrollElement.clientWidth : this.scrollElement.scrollHeight - this.scrollElement.clientHeight;
585
- } else {
586
- const doc = this.scrollElement.document.documentElement;
587
- return this.options.horizontal ? doc.scrollWidth - this.scrollElement.innerWidth : doc.scrollHeight - this.scrollElement.innerHeight;
588
- }
589
- };
590
- this.getOffsetForAlignment = (toOffset, align, itemSize = 0) => {
591
- if (!this.scrollElement) return 0;
592
- const size = this.getSize();
593
- const scrollOffset = this.getScrollOffset();
594
- if (align === "auto") {
595
- align = toOffset >= scrollOffset + size ? "end" : "start";
596
- }
597
- if (align === "center") {
598
- toOffset += (itemSize - size) / 2;
599
- } else if (align === "end") {
600
- toOffset -= size;
601
- }
602
- const maxOffset = this.getMaxScrollOffset();
603
- return Math.max(Math.min(maxOffset, toOffset), 0);
604
- };
605
- this.getOffsetForIndex = (index, align = "auto") => {
606
- index = Math.max(0, Math.min(index, this.options.count - 1));
607
- const item = this.measurementsCache[index];
608
- if (!item) {
609
- return void 0;
610
- }
611
- const size = this.getSize();
612
- const scrollOffset = this.getScrollOffset();
613
- if (align === "auto") {
614
- if (item.end >= scrollOffset + size - this.options.scrollPaddingEnd) {
615
- align = "end";
616
- } else if (item.start <= scrollOffset + this.options.scrollPaddingStart) {
617
- align = "start";
618
- } else {
619
- return [scrollOffset, align];
620
- }
621
- }
622
- if (align === "end" && index === this.options.count - 1) {
623
- return [this.getMaxScrollOffset(), align];
624
- }
625
- const toOffset = align === "end" ? item.end + this.options.scrollPaddingEnd : item.start - this.options.scrollPaddingStart;
626
- return [
627
- this.getOffsetForAlignment(toOffset, align, item.size),
628
- align
629
- ];
630
- };
631
- this.isDynamicMode = () => this.elementsCache.size > 0;
632
- this.scrollToOffset = (toOffset, { align = "start", behavior } = {}) => {
633
- if (behavior === "smooth" && this.isDynamicMode()) {
634
- console.warn(
635
- "The `smooth` scroll behavior is not fully supported with dynamic size."
636
- );
637
- }
638
- this._scrollToOffset(this.getOffsetForAlignment(toOffset, align), {
639
- adjustments: void 0,
640
- behavior
641
- });
642
- };
643
- this.scrollToIndex = (index, { align: initialAlign = "auto", behavior } = {}) => {
644
- if (behavior === "smooth" && this.isDynamicMode()) {
645
- console.warn(
646
- "The `smooth` scroll behavior is not fully supported with dynamic size."
647
- );
648
- }
649
- index = Math.max(0, Math.min(index, this.options.count - 1));
650
- this.currentScrollToIndex = index;
651
- let attempts = 0;
652
- const maxAttempts = 10;
653
- const tryScroll = (currentAlign) => {
654
- if (!this.targetWindow) return;
655
- const offsetInfo = this.getOffsetForIndex(index, currentAlign);
656
- if (!offsetInfo) {
657
- console.warn("Failed to get offset for index:", index);
658
- return;
659
- }
660
- const [offset, align] = offsetInfo;
661
- this._scrollToOffset(offset, { adjustments: void 0, behavior });
662
- this.targetWindow.requestAnimationFrame(() => {
663
- const verify = () => {
664
- if (this.currentScrollToIndex !== index) return;
665
- const currentOffset = this.getScrollOffset();
666
- const afterInfo = this.getOffsetForIndex(index, align);
667
- if (!afterInfo) {
668
- console.warn("Failed to get offset for index:", index);
669
- return;
670
- }
671
- if (!approxEqual(afterInfo[0], currentOffset)) {
672
- scheduleRetry(align);
673
- }
674
- };
675
- if (this.isDynamicMode()) {
676
- this.targetWindow.requestAnimationFrame(verify);
677
- } else {
678
- verify();
679
- }
680
- });
681
- };
682
- const scheduleRetry = (align) => {
683
- if (!this.targetWindow) return;
684
- if (this.currentScrollToIndex !== index) return;
685
- attempts++;
686
- if (attempts < maxAttempts) {
687
- if (process.env.NODE_ENV !== "production" && this.options.debug) {
688
- console.info("Schedule retry", attempts, maxAttempts);
689
- }
690
- this.targetWindow.requestAnimationFrame(() => tryScroll(align));
691
- } else {
692
- console.warn(
693
- `Failed to scroll to index ${index} after ${maxAttempts} attempts.`
694
- );
695
- }
696
- };
697
- tryScroll(initialAlign);
698
- };
699
- this.scrollBy = (delta, { behavior } = {}) => {
700
- if (behavior === "smooth" && this.isDynamicMode()) {
701
- console.warn(
702
- "The `smooth` scroll behavior is not fully supported with dynamic size."
703
- );
704
- }
705
- this._scrollToOffset(this.getScrollOffset() + delta, {
706
- adjustments: void 0,
707
- behavior
708
- });
709
- };
710
- this.getTotalSize = () => {
711
- var _a;
712
- const measurements = this.getMeasurements();
713
- let end;
714
- if (measurements.length === 0) {
715
- end = this.options.paddingStart;
716
- } else if (this.options.lanes === 1) {
717
- end = ((_a = measurements[measurements.length - 1]) == null ? void 0 : _a.end) ?? 0;
718
- } else {
719
- const endByLane = Array(this.options.lanes).fill(null);
720
- let endIndex = measurements.length - 1;
721
- while (endIndex >= 0 && endByLane.some((val) => val === null)) {
722
- const item = measurements[endIndex];
723
- if (endByLane[item.lane] === null) {
724
- endByLane[item.lane] = item.end;
725
- }
726
- endIndex--;
727
- }
728
- end = Math.max(...endByLane.filter((val) => val !== null));
729
- }
730
- return Math.max(
731
- end - this.options.scrollMargin + this.options.paddingEnd,
732
- 0
733
- );
734
- };
735
- this._scrollToOffset = (offset, {
736
- adjustments,
737
- behavior
738
- }) => {
739
- this.options.scrollToFn(offset, { behavior, adjustments }, this);
740
- };
741
- this.measure = () => {
742
- this.itemSizeCache = /* @__PURE__ */ new Map();
743
- this.laneAssignments = /* @__PURE__ */ new Map();
744
- this.notify(false);
745
- };
746
- this.setOptions(opts);
747
- }
748
- }
749
- const findNearestBinarySearch = (low, high, getCurrentValue, value) => {
750
- while (low <= high) {
751
- const middle = (low + high) / 2 | 0;
752
- const currentValue = getCurrentValue(middle);
753
- if (currentValue < value) {
754
- low = middle + 1;
755
- } else if (currentValue > value) {
756
- high = middle - 1;
757
- } else {
758
- return middle;
759
- }
760
- }
761
- if (low > 0) {
762
- return low - 1;
763
- } else {
764
- return 0;
765
- }
766
- };
767
- function calculateRange({
768
- measurements,
769
- outerSize,
770
- scrollOffset,
771
- lanes
772
- }) {
773
- const lastIndex = measurements.length - 1;
774
- const getOffset = (index) => measurements[index].start;
775
- if (measurements.length <= lanes) {
776
- return {
777
- startIndex: 0,
778
- endIndex: lastIndex
779
- };
780
- }
781
- let startIndex = findNearestBinarySearch(
782
- 0,
783
- lastIndex,
784
- getOffset,
785
- scrollOffset
786
- );
787
- let endIndex = startIndex;
788
- if (lanes === 1) {
789
- while (endIndex < lastIndex && measurements[endIndex].end < scrollOffset + outerSize) {
790
- endIndex++;
791
- }
792
- } else if (lanes > 1) {
793
- const endPerLane = Array(lanes).fill(0);
794
- while (endIndex < lastIndex && endPerLane.some((pos) => pos < scrollOffset + outerSize)) {
795
- const item = measurements[endIndex];
796
- endPerLane[item.lane] = item.end;
797
- endIndex++;
798
- }
799
- const startPerLane = Array(lanes).fill(scrollOffset + outerSize);
800
- while (startIndex >= 0 && startPerLane.some((pos) => pos >= scrollOffset)) {
801
- const item = measurements[startIndex];
802
- startPerLane[item.lane] = item.start;
803
- startIndex--;
804
- }
805
- startIndex = Math.max(0, startIndex - startIndex % lanes);
806
- endIndex = Math.min(lastIndex, endIndex + (lanes - 1 - endIndex % lanes));
807
- }
808
- return { startIndex, endIndex };
809
- }
810
- export {
811
- Virtualizer,
812
- approxEqual,
813
- debounce,
814
- defaultKeyExtractor,
815
- defaultRangeExtractor,
816
- elementScroll,
817
- measureElement,
818
- memo,
819
- notUndefined,
820
- observeElementOffset,
821
- observeElementRect
822
- };