@tanstack/virtual-core 3.0.0-beta.23 → 3.0.0-beta.26

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.
@@ -22,53 +22,48 @@
22
22
  if (opts.key && opts.debug != null && opts.debug()) depTime = Date.now();
23
23
  const newDeps = getDeps();
24
24
  const depsChanged = newDeps.length !== deps.length || newDeps.some((dep, index) => deps[index] !== dep);
25
-
26
25
  if (!depsChanged) {
27
26
  return result;
28
27
  }
29
-
30
28
  deps = newDeps;
31
29
  let resultTime;
32
30
  if (opts.key && opts.debug != null && opts.debug()) resultTime = Date.now();
33
31
  result = fn(...newDeps);
34
32
  opts == null ? void 0 : opts.onChange == null ? void 0 : opts.onChange(result);
35
-
36
33
  if (opts.key && opts.debug != null && opts.debug()) {
37
34
  const depEndTime = Math.round((Date.now() - depTime) * 100) / 100;
38
35
  const resultEndTime = Math.round((Date.now() - resultTime) * 100) / 100;
39
36
  const resultFpsPercentage = resultEndTime / 16;
40
-
41
37
  const pad = (str, num) => {
42
38
  str = String(str);
43
-
44
39
  while (str.length < num) {
45
40
  str = ' ' + str;
46
41
  }
47
-
48
42
  return str;
49
43
  };
50
-
51
- console.info("%c\u23F1 " + pad(resultEndTime, 5) + " /" + pad(depEndTime, 5) + " ms", "\n font-size: .6rem;\n font-weight: bold;\n color: hsl(" + Math.max(0, Math.min(120 - 120 * resultFpsPercentage, 120)) + "deg 100% 31%);", opts == null ? void 0 : opts.key);
44
+ console.info(`%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`, `
45
+ font-size: .6rem;
46
+ font-weight: bold;
47
+ color: hsl(${Math.max(0, Math.min(120 - 120 * resultFpsPercentage, 120))}deg 100% 31%);`, opts == null ? void 0 : opts.key);
52
48
  }
53
-
54
49
  return result;
55
50
  };
56
51
  }
57
52
 
58
53
  //
54
+
55
+ //
56
+
59
57
  const defaultKeyExtractor = index => index;
60
58
  const defaultRangeExtractor = range => {
61
59
  const start = Math.max(range.startIndex - range.overscan, 0);
62
60
  const end = Math.min(range.endIndex + range.overscan, range.count - 1);
63
61
  const arr = [];
64
-
65
62
  for (let i = start; i <= end; i++) {
66
63
  arr.push(i);
67
64
  }
68
-
69
65
  return arr;
70
66
  };
71
-
72
67
  const memoRectCallback = (instance, cb) => {
73
68
  let prev = {
74
69
  height: -1,
@@ -78,25 +73,20 @@
78
73
  if (instance.options.horizontal ? rect.width !== prev.width : rect.height !== prev.height) {
79
74
  cb(rect);
80
75
  }
81
-
82
76
  prev = rect;
83
77
  };
84
78
  };
85
-
86
79
  const observeElementRect = (instance, cb) => {
87
80
  const observer = new ResizeObserver(entries => {
88
81
  var _entries$, _entries$2;
89
-
90
82
  cb({
91
83
  width: (_entries$ = entries[0]) == null ? void 0 : _entries$.contentRect.width,
92
84
  height: (_entries$2 = entries[0]) == null ? void 0 : _entries$2.contentRect.height
93
85
  });
94
86
  });
95
-
96
87
  if (!instance.scrollElement) {
97
88
  return;
98
89
  }
99
-
100
90
  cb(instance.scrollElement.getBoundingClientRect());
101
91
  observer.observe(instance.scrollElement);
102
92
  return () => {
@@ -105,16 +95,13 @@
105
95
  };
106
96
  const observeWindowRect = (instance, cb) => {
107
97
  const memoizedCallback = memoRectCallback(instance, cb);
108
-
109
98
  const onResize = () => memoizedCallback({
110
99
  width: instance.scrollElement.innerWidth,
111
100
  height: instance.scrollElement.innerHeight
112
101
  });
113
-
114
102
  if (!instance.scrollElement) {
115
103
  return;
116
104
  }
117
-
118
105
  onResize();
119
106
  instance.scrollElement.addEventListener('resize', onResize, {
120
107
  capture: false,
@@ -128,38 +115,30 @@
128
115
  element: ['scrollLeft', 'scrollTop'],
129
116
  window: ['scrollX', 'scrollY']
130
117
  };
131
-
132
118
  const createOffsetObserver = mode => {
133
119
  return (instance, cb) => {
134
120
  if (!instance.scrollElement) {
135
121
  return;
136
122
  }
137
-
138
123
  const propX = scrollProps[mode][0];
139
124
  const propY = scrollProps[mode][1];
140
125
  let prevX = instance.scrollElement[propX];
141
126
  let prevY = instance.scrollElement[propY];
142
-
143
127
  const scroll = () => {
144
128
  const offset = instance.scrollElement[instance.options.horizontal ? propX : propY];
145
129
  cb(Math.max(0, offset - instance.options.scrollMargin));
146
130
  };
147
-
148
131
  scroll();
149
-
150
132
  const onScroll = e => {
151
133
  const target = e.currentTarget;
152
134
  const scrollX = target[propX];
153
135
  const scrollY = target[propY];
154
-
155
136
  if (instance.options.horizontal ? prevX - scrollX : prevY - scrollY) {
156
137
  scroll();
157
138
  }
158
-
159
139
  prevX = scrollX;
160
140
  prevY = scrollY;
161
141
  };
162
-
163
142
  instance.scrollElement.addEventListener('scroll', onScroll, {
164
143
  capture: false,
165
144
  passive: true
@@ -169,7 +148,6 @@
169
148
  };
170
149
  };
171
150
  };
172
-
173
151
  const observeElementOffset = createOffsetObserver('element');
174
152
  const observeWindowOffset = createOffsetObserver('window');
175
153
  const measureElement = (element, instance) => {
@@ -177,34 +155,33 @@
177
155
  };
178
156
  const windowScroll = (offset, _ref, instance) => {
179
157
  var _instance$scrollEleme;
180
-
181
158
  let {
182
- canSmooth,
159
+ adjustments,
160
+ behavior,
183
161
  sync
184
162
  } = _ref;
185
- const toOffset = sync ? offset : offset + instance.options.scrollMargin;
163
+ const toOffset = (sync ? offset : offset + instance.options.scrollMargin) + (adjustments ?? 0);
186
164
  (_instance$scrollEleme = instance.scrollElement) == null ? void 0 : _instance$scrollEleme.scrollTo == null ? void 0 : _instance$scrollEleme.scrollTo({
187
165
  [instance.options.horizontal ? 'left' : 'top']: toOffset,
188
- behavior: canSmooth ? 'smooth' : undefined
166
+ behavior
189
167
  });
190
168
  };
191
169
  const elementScroll = (offset, _ref2, instance) => {
192
170
  var _instance$scrollEleme2;
193
-
194
171
  let {
195
- canSmooth,
172
+ adjustments,
173
+ behavior,
196
174
  sync
197
175
  } = _ref2;
198
- const toOffset = sync ? offset : offset + instance.options.scrollMargin;
176
+ const toOffset = (sync ? offset : offset + instance.options.scrollMargin) + (adjustments ?? 0);
199
177
  (_instance$scrollEleme2 = instance.scrollElement) == null ? void 0 : _instance$scrollEleme2.scrollTo == null ? void 0 : _instance$scrollEleme2.scrollTo({
200
178
  [instance.options.horizontal ? 'left' : 'top']: toOffset,
201
- behavior: canSmooth ? 'smooth' : undefined
179
+ behavior
202
180
  });
203
181
  };
204
182
  class Virtualizer {
205
183
  constructor(_opts) {
206
184
  var _this = this;
207
-
208
185
  this.unsubs = [];
209
186
  this.scrollElement = null;
210
187
  this.isScrolling = false;
@@ -212,9 +189,8 @@
212
189
  this.measurementsCache = [];
213
190
  this.itemMeasurementsCache = {};
214
191
  this.pendingMeasuredCacheIndexes = [];
215
- this.scrollDelta = 0;
192
+ this.scrollAdjustments = 0;
216
193
  this.measureElementCache = {};
217
-
218
194
  this.getResizeObserver = (() => {
219
195
  let _ro = null;
220
196
  return () => {
@@ -231,12 +207,10 @@
231
207
  }
232
208
  };
233
209
  })();
234
-
235
210
  this.range = {
236
211
  startIndex: 0,
237
212
  endIndex: 0
238
213
  };
239
-
240
214
  this.setOptions = opts => {
241
215
  Object.entries(opts).forEach(_ref3 => {
242
216
  let [key, value] = _ref3;
@@ -253,7 +227,6 @@
253
227
  horizontal: false,
254
228
  getItemKey: defaultKeyExtractor,
255
229
  rangeExtractor: defaultRangeExtractor,
256
- enableSmoothScroll: true,
257
230
  onChange: () => {},
258
231
  measureElement,
259
232
  initialRect: {
@@ -266,42 +239,33 @@
266
239
  ...opts
267
240
  };
268
241
  };
269
-
270
242
  this.notify = () => {
271
243
  var _this$options$onChang, _this$options;
272
-
273
244
  (_this$options$onChang = (_this$options = this.options).onChange) == null ? void 0 : _this$options$onChang.call(_this$options, this);
274
245
  };
275
-
276
246
  this.cleanup = () => {
277
247
  this.unsubs.filter(Boolean).forEach(d => d());
278
248
  this.unsubs = [];
279
249
  this.scrollElement = null;
280
250
  };
281
-
282
251
  this._didMount = () => {
283
252
  return () => {
284
253
  var _this$getResizeObserv;
285
-
286
254
  (_this$getResizeObserv = this.getResizeObserver()) == null ? void 0 : _this$getResizeObserv.disconnect();
287
255
  this.measureElementCache = {};
288
256
  this.cleanup();
289
257
  };
290
258
  };
291
-
292
259
  this._willUpdate = () => {
293
260
  const scrollElement = this.options.getScrollElement();
294
-
295
261
  if (this.scrollElement !== scrollElement) {
296
262
  this.cleanup();
297
263
  this.scrollElement = scrollElement;
298
-
299
264
  this._scrollToOffset(this.scrollOffset, {
300
- canSmooth: false,
301
- sync: true,
302
- requested: false
265
+ adjustments: undefined,
266
+ behavior: undefined,
267
+ sync: true
303
268
  });
304
-
305
269
  this.unsubs.push(this.options.observeElementRect(this, rect => {
306
270
  this.scrollRect = rect;
307
271
  this.calculateRange();
@@ -311,11 +275,10 @@
311
275
  clearTimeout(this.isScrollingTimeoutId);
312
276
  this.isScrollingTimeoutId = null;
313
277
  }
314
-
315
278
  if (this.scrollOffset !== offset) {
316
279
  this.scrollOffset = offset;
317
280
  this.isScrolling = true;
318
- this.scrollDelta = 0;
281
+ this.scrollAdjustments = 0;
319
282
  this.isScrollingTimeoutId = setTimeout(() => {
320
283
  this.isScrollingTimeoutId = null;
321
284
  this.isScrolling = false;
@@ -323,25 +286,21 @@
323
286
  }, this.options.scrollingDelay);
324
287
  } else {
325
288
  this.isScrolling = false;
326
- this.scrollDelta = 0;
289
+ this.scrollAdjustments = 0;
327
290
  }
328
-
329
291
  this.calculateRange();
330
292
  }));
331
293
  } else if (!this.isScrolling) {
332
294
  this.calculateRange();
333
295
  }
334
296
  };
335
-
336
297
  this.getSize = () => {
337
298
  return this.scrollRect[this.options.horizontal ? 'width' : 'height'];
338
299
  };
339
-
340
300
  this.getMeasurements = memo(() => [this.options.count, this.options.paddingStart, this.options.getItemKey, this.itemMeasurementsCache], (count, paddingStart, getItemKey, measurementsCache) => {
341
301
  const min = this.pendingMeasuredCacheIndexes.length > 0 ? Math.min(...this.pendingMeasuredCacheIndexes) : 0;
342
302
  this.pendingMeasuredCacheIndexes = [];
343
303
  const measurements = this.measurementsCache.slice(0, min);
344
-
345
304
  for (let i = min; i < count; i++) {
346
305
  const key = getItemKey(i);
347
306
  const measuredSize = measurementsCache[key];
@@ -356,7 +315,6 @@
356
315
  key
357
316
  };
358
317
  }
359
-
360
318
  this.measurementsCache = measurements;
361
319
  return measurements;
362
320
  }, {
@@ -369,19 +327,18 @@
369
327
  outerSize,
370
328
  scrollOffset
371
329
  });
372
-
373
330
  if (range.startIndex !== this.range.startIndex || range.endIndex !== this.range.endIndex) {
374
331
  this.range = range;
375
332
  this.notify();
376
333
  }
377
-
378
334
  return this.range;
379
335
  }, {
380
336
  key: 'calculateRange',
381
337
  debug: () => this.options.debug
382
338
  });
383
339
  this.getIndexes = memo(() => [this.options.rangeExtractor, this.range, this.options.overscan, this.options.count], (rangeExtractor, range, overscan, count) => {
384
- return rangeExtractor({ ...range,
340
+ return rangeExtractor({
341
+ ...range,
385
342
  overscan,
386
343
  count: count
387
344
  });
@@ -389,109 +346,84 @@
389
346
  key: 'getIndexes',
390
347
  debug: () => this.options.debug
391
348
  });
392
-
393
349
  this.indexFromElement = node => {
394
350
  const attributeName = this.options.indexAttribute;
395
351
  const indexStr = node.getAttribute(attributeName);
396
-
397
352
  if (!indexStr) {
398
- console.warn("Missing attribute name '" + attributeName + "={index}' on measured element.");
353
+ console.warn(`Missing attribute name '${attributeName}={index}' on measured element.`);
399
354
  return -1;
400
355
  }
401
-
402
356
  return parseInt(indexStr, 10);
403
357
  };
404
-
405
358
  this._measureElement = (node, _sync) => {
406
- var _this$itemMeasurement;
407
-
408
359
  const index = this.indexFromElement(node);
409
360
  const item = this.measurementsCache[index];
410
-
411
361
  if (!item) {
412
362
  return;
413
363
  }
414
-
415
364
  const prevNode = this.measureElementCache[item.key];
416
365
  const ro = this.getResizeObserver();
417
-
418
366
  if (!node.isConnected) {
419
367
  if (prevNode) {
420
368
  ro == null ? void 0 : ro.unobserve(prevNode);
421
369
  delete this.measureElementCache[item.key];
422
370
  }
423
-
424
371
  return;
425
372
  }
426
-
427
373
  if (!prevNode || prevNode !== node) {
428
374
  if (prevNode) {
429
375
  ro == null ? void 0 : ro.unobserve(prevNode);
430
376
  }
431
-
432
377
  this.measureElementCache[item.key] = node;
433
378
  ro == null ? void 0 : ro.observe(node);
434
379
  }
435
-
436
380
  const measuredItemSize = this.options.measureElement(node, this);
437
- const itemSize = (_this$itemMeasurement = this.itemMeasurementsCache[item.key]) != null ? _this$itemMeasurement : item.size;
381
+ const itemSize = this.itemMeasurementsCache[item.key] ?? item.size;
438
382
  const delta = measuredItemSize - itemSize;
439
-
440
383
  if (delta !== 0) {
441
- if (item.start < this.scrollOffset && this.isScrolling && this.destinationOffset === undefined) {
384
+ if (item.start < this.scrollOffset && this.isScrolling) {
442
385
  if (this.options.debug) {
443
386
  console.info('correction', delta);
444
387
  }
445
-
446
- this.scrollDelta += delta;
447
-
448
- this._scrollToOffset(this.scrollOffset + this.scrollDelta, {
449
- canSmooth: false,
450
- sync: false,
451
- requested: false
388
+ this._scrollToOffset(this.scrollOffset, {
389
+ adjustments: this.scrollAdjustments += delta,
390
+ behavior: undefined,
391
+ sync: false
452
392
  });
453
393
  }
454
-
455
394
  this.pendingMeasuredCacheIndexes.push(index);
456
- this.itemMeasurementsCache = { ...this.itemMeasurementsCache,
395
+ this.itemMeasurementsCache = {
396
+ ...this.itemMeasurementsCache,
457
397
  [item.key]: measuredItemSize
458
398
  };
459
399
  this.notify();
460
400
  }
461
401
  };
462
-
463
402
  this.measureElement = node => {
464
403
  if (!node) {
465
404
  return;
466
405
  }
467
-
468
406
  this._measureElement(node, true);
469
407
  };
470
-
471
408
  this.getVirtualItems = memo(() => [this.getIndexes(), this.getMeasurements()], (indexes, measurements) => {
472
409
  const virtualItems = [];
473
-
474
410
  for (let k = 0, len = indexes.length; k < len; k++) {
475
411
  const i = indexes[k];
476
412
  const measurement = measurements[i];
477
413
  virtualItems.push(measurement);
478
414
  }
479
-
480
415
  return virtualItems;
481
416
  }, {
482
417
  key: 'getIndexes',
483
418
  debug: () => this.options.debug
484
419
  });
485
-
486
420
  this.scrollToOffset = function (toOffset, _temp) {
487
421
  let {
488
422
  align = 'start',
489
- smoothScroll = _this.options.enableSmoothScroll
423
+ behavior
490
424
  } = _temp === void 0 ? {} : _temp;
491
425
  const offset = _this.scrollOffset;
492
-
493
426
  const size = _this.getSize();
494
-
495
427
  if (align === 'auto') {
496
428
  if (toOffset <= offset) {
497
429
  align = 'start';
@@ -501,13 +433,11 @@
501
433
  align = 'start';
502
434
  }
503
435
  }
504
-
505
436
  const options = {
506
- canSmooth: smoothScroll,
507
- sync: false,
508
- requested: true
437
+ adjustments: undefined,
438
+ behavior,
439
+ sync: false
509
440
  };
510
-
511
441
  if (align === 'start') {
512
442
  _this._scrollToOffset(toOffset, options);
513
443
  } else if (align === 'end') {
@@ -516,29 +446,21 @@
516
446
  _this._scrollToOffset(toOffset - size / 2, options);
517
447
  }
518
448
  };
519
-
520
449
  this.scrollToIndex = function (index, _temp2) {
521
450
  let {
522
451
  align = 'auto',
523
- smoothScroll = _this.options.enableSmoothScroll,
524
452
  ...rest
525
453
  } = _temp2 === void 0 ? {} : _temp2;
526
-
527
454
  const measurements = _this.getMeasurements();
528
-
529
455
  const offset = _this.scrollOffset;
530
-
531
456
  const size = _this.getSize();
532
-
533
457
  const {
534
458
  count
535
459
  } = _this.options;
536
460
  const measurement = measurements[Math.max(0, Math.min(index, count - 1))];
537
-
538
461
  if (!measurement) {
539
462
  return;
540
463
  }
541
-
542
464
  if (align === 'auto') {
543
465
  if (measurement.end >= offset + size - _this.options.scrollPaddingEnd) {
544
466
  align = 'end';
@@ -548,78 +470,42 @@
548
470
  return;
549
471
  }
550
472
  }
551
-
552
473
  const toOffset = align === 'end' ? measurement.end + _this.options.scrollPaddingEnd : measurement.start - _this.options.scrollPaddingStart;
553
-
554
474
  _this.scrollToOffset(toOffset, {
555
475
  align,
556
- smoothScroll,
557
476
  ...rest
558
477
  });
559
478
  };
560
-
561
479
  this.getTotalSize = () => {
562
480
  var _this$getMeasurements;
563
-
564
481
  return (((_this$getMeasurements = this.getMeasurements()[this.options.count - 1]) == null ? void 0 : _this$getMeasurements.end) || this.options.paddingStart) + this.options.paddingEnd;
565
482
  };
566
-
567
483
  this._scrollToOffset = (offset, _ref4) => {
568
484
  let {
569
- requested,
570
- canSmooth,
485
+ adjustments,
486
+ behavior,
571
487
  sync
572
488
  } = _ref4;
573
- clearTimeout(this.scrollCheckFrame);
574
-
575
- if (requested) {
576
- this.destinationOffset = offset;
577
- }
578
-
579
489
  this.options.scrollToFn(offset, {
580
- canSmooth,
581
- sync
490
+ behavior,
491
+ sync,
492
+ adjustments
582
493
  }, this);
583
- let scrollCheckFrame;
584
-
585
- const check = () => {
586
- let lastOffset = this.scrollOffset;
587
- this.scrollCheckFrame = scrollCheckFrame = setTimeout(() => {
588
- if (this.scrollCheckFrame !== scrollCheckFrame) {
589
- return;
590
- }
591
-
592
- if (this.scrollOffset === lastOffset) {
593
- this.destinationOffset = undefined;
594
- return;
595
- }
596
-
597
- lastOffset = this.scrollOffset;
598
- check();
599
- }, 100);
600
- };
601
-
602
- check();
603
494
  };
604
-
605
495
  this.measure = () => {
606
496
  this.itemMeasurementsCache = {};
607
497
  this.notify();
608
498
  };
609
-
610
499
  this.setOptions(_opts);
611
500
  this.scrollRect = this.options.initialRect;
612
501
  this.scrollOffset = this.options.initialOffset;
613
502
  this.calculateRange();
614
503
  }
615
-
616
504
  }
617
-
618
505
  const findNearestBinarySearch = (low, high, getCurrentValue, value) => {
619
506
  while (low <= high) {
620
507
  const middle = (low + high) / 2 | 0;
621
508
  const currentValue = getCurrentValue(middle);
622
-
623
509
  if (currentValue < value) {
624
510
  low = middle + 1;
625
511
  } else if (currentValue > value) {
@@ -628,14 +514,12 @@
628
514
  return middle;
629
515
  }
630
516
  }
631
-
632
517
  if (low > 0) {
633
518
  return low - 1;
634
519
  } else {
635
520
  return 0;
636
521
  }
637
522
  };
638
-
639
523
  function calculateRange(_ref5) {
640
524
  let {
641
525
  measurements,
@@ -643,16 +527,12 @@
643
527
  scrollOffset
644
528
  } = _ref5;
645
529
  const count = measurements.length - 1;
646
-
647
530
  const getOffset = index => measurements[index].start;
648
-
649
531
  const startIndex = findNearestBinarySearch(0, count, getOffset, scrollOffset);
650
532
  let endIndex = startIndex;
651
-
652
533
  while (endIndex < count && measurements[endIndex].end < scrollOffset + outerSize) {
653
534
  endIndex++;
654
535
  }
655
-
656
536
  return {
657
537
  startIndex,
658
538
  endIndex