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