lkd-web-kit 0.4.0 → 0.4.1

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