@tanstack/virtual-core 3.5.1 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +90 -49
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +7 -5
- package/dist/esm/index.d.ts +7 -5
- package/dist/esm/index.js +90 -49
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +111 -64
package/src/index.ts
CHANGED
|
@@ -318,6 +318,7 @@ export interface VirtualizerOptions<
|
|
|
318
318
|
initialMeasurementsCache?: VirtualItem[]
|
|
319
319
|
lanes?: number
|
|
320
320
|
isScrollingResetDelay?: number
|
|
321
|
+
enabled?: boolean
|
|
321
322
|
}
|
|
322
323
|
|
|
323
324
|
export class Virtualizer<
|
|
@@ -333,8 +334,8 @@ export class Virtualizer<
|
|
|
333
334
|
measurementsCache: VirtualItem[] = []
|
|
334
335
|
private itemSizeCache = new Map<Key, number>()
|
|
335
336
|
private pendingMeasuredCacheIndexes: number[] = []
|
|
336
|
-
scrollRect: Rect
|
|
337
|
-
scrollOffset: number
|
|
337
|
+
scrollRect: Rect | null = null
|
|
338
|
+
scrollOffset: number | null = null
|
|
338
339
|
scrollDirection: ScrollDirection | null = null
|
|
339
340
|
private scrollAdjustments: number = 0
|
|
340
341
|
shouldAdjustScrollPositionOnItemSizeChange:
|
|
@@ -375,17 +376,6 @@ export class Virtualizer<
|
|
|
375
376
|
|
|
376
377
|
constructor(opts: VirtualizerOptions<TScrollElement, TItemElement>) {
|
|
377
378
|
this.setOptions(opts)
|
|
378
|
-
this.scrollRect = this.options.initialRect
|
|
379
|
-
this.scrollOffset =
|
|
380
|
-
typeof this.options.initialOffset === 'function'
|
|
381
|
-
? this.options.initialOffset()
|
|
382
|
-
: this.options.initialOffset
|
|
383
|
-
this.measurementsCache = this.options.initialMeasurementsCache
|
|
384
|
-
this.measurementsCache.forEach((item) => {
|
|
385
|
-
this.itemSizeCache.set(item.key, item.size)
|
|
386
|
-
})
|
|
387
|
-
|
|
388
|
-
this.notify(false, false)
|
|
389
379
|
}
|
|
390
380
|
|
|
391
381
|
setOptions = (opts: VirtualizerOptions<TScrollElement, TItemElement>) => {
|
|
@@ -413,6 +403,7 @@ export class Virtualizer<
|
|
|
413
403
|
initialMeasurementsCache: [],
|
|
414
404
|
lanes: 1,
|
|
415
405
|
isScrollingResetDelay: 150,
|
|
406
|
+
enabled: true,
|
|
416
407
|
...opts,
|
|
417
408
|
}
|
|
418
409
|
}
|
|
@@ -437,22 +428,30 @@ export class Virtualizer<
|
|
|
437
428
|
this.unsubs.filter(Boolean).forEach((d) => d!())
|
|
438
429
|
this.unsubs = []
|
|
439
430
|
this.scrollElement = null
|
|
431
|
+
this.targetWindow = null
|
|
432
|
+
this.observer.disconnect()
|
|
433
|
+
this.measureElementCache.clear()
|
|
440
434
|
}
|
|
441
435
|
|
|
442
436
|
_didMount = () => {
|
|
443
|
-
this.measureElementCache.forEach(this.observer.observe)
|
|
444
437
|
return () => {
|
|
445
|
-
this.observer.disconnect()
|
|
446
438
|
this.cleanup()
|
|
447
439
|
}
|
|
448
440
|
}
|
|
449
441
|
|
|
450
442
|
_willUpdate = () => {
|
|
451
|
-
const scrollElement = this.options.
|
|
443
|
+
const scrollElement = this.options.enabled
|
|
444
|
+
? this.options.getScrollElement()
|
|
445
|
+
: null
|
|
452
446
|
|
|
453
447
|
if (this.scrollElement !== scrollElement) {
|
|
454
448
|
this.cleanup()
|
|
455
449
|
|
|
450
|
+
if (!scrollElement) {
|
|
451
|
+
this.notify(false, false)
|
|
452
|
+
return
|
|
453
|
+
}
|
|
454
|
+
|
|
456
455
|
this.scrollElement = scrollElement
|
|
457
456
|
|
|
458
457
|
if (this.scrollElement && 'ownerDocument' in this.scrollElement) {
|
|
@@ -461,7 +460,7 @@ export class Virtualizer<
|
|
|
461
460
|
this.targetWindow = this.scrollElement?.window ?? null
|
|
462
461
|
}
|
|
463
462
|
|
|
464
|
-
this._scrollToOffset(this.
|
|
463
|
+
this._scrollToOffset(this.getScrollOffset(), {
|
|
465
464
|
adjustments: undefined,
|
|
466
465
|
behavior: undefined,
|
|
467
466
|
})
|
|
@@ -477,7 +476,7 @@ export class Virtualizer<
|
|
|
477
476
|
this.options.observeElementOffset(this, (offset, isScrolling) => {
|
|
478
477
|
this.scrollAdjustments = 0
|
|
479
478
|
this.scrollDirection = isScrolling
|
|
480
|
-
? this.
|
|
479
|
+
? this.getScrollOffset() < offset
|
|
481
480
|
? 'forward'
|
|
482
481
|
: 'backward'
|
|
483
482
|
: null
|
|
@@ -493,29 +492,30 @@ export class Virtualizer<
|
|
|
493
492
|
}
|
|
494
493
|
|
|
495
494
|
private getSize = () => {
|
|
495
|
+
if (!this.options.enabled) {
|
|
496
|
+
this.scrollRect = null
|
|
497
|
+
return 0
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
this.scrollRect = this.scrollRect ?? this.options.initialRect
|
|
501
|
+
|
|
496
502
|
return this.scrollRect[this.options.horizontal ? 'width' : 'height']
|
|
497
503
|
}
|
|
498
504
|
|
|
499
|
-
private
|
|
500
|
-
()
|
|
501
|
-
this.
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
this.
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
514
|
-
},
|
|
515
|
-
{
|
|
516
|
-
key: false,
|
|
517
|
-
},
|
|
518
|
-
)
|
|
505
|
+
private getScrollOffset = () => {
|
|
506
|
+
if (!this.options.enabled) {
|
|
507
|
+
this.scrollOffset = null
|
|
508
|
+
return 0
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
this.scrollOffset =
|
|
512
|
+
this.scrollOffset ??
|
|
513
|
+
(typeof this.options.initialOffset === 'function'
|
|
514
|
+
? this.options.initialOffset()
|
|
515
|
+
: this.options.initialOffset)
|
|
516
|
+
|
|
517
|
+
return this.scrollOffset
|
|
518
|
+
}
|
|
519
519
|
|
|
520
520
|
private getFurthestMeasurement = (
|
|
521
521
|
measurements: VirtualItem[],
|
|
@@ -558,9 +558,48 @@ export class Virtualizer<
|
|
|
558
558
|
: undefined
|
|
559
559
|
}
|
|
560
560
|
|
|
561
|
+
private getMeasurementOptions = memo(
|
|
562
|
+
() => [
|
|
563
|
+
this.options.count,
|
|
564
|
+
this.options.paddingStart,
|
|
565
|
+
this.options.scrollMargin,
|
|
566
|
+
this.options.getItemKey,
|
|
567
|
+
this.options.enabled,
|
|
568
|
+
],
|
|
569
|
+
(count, paddingStart, scrollMargin, getItemKey, enabled) => {
|
|
570
|
+
this.pendingMeasuredCacheIndexes = []
|
|
571
|
+
return {
|
|
572
|
+
count,
|
|
573
|
+
paddingStart,
|
|
574
|
+
scrollMargin,
|
|
575
|
+
getItemKey,
|
|
576
|
+
enabled,
|
|
577
|
+
}
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
key: false,
|
|
581
|
+
},
|
|
582
|
+
)
|
|
583
|
+
|
|
561
584
|
private getMeasurements = memo(
|
|
562
585
|
() => [this.getMeasurementOptions(), this.itemSizeCache],
|
|
563
|
-
(
|
|
586
|
+
(
|
|
587
|
+
{ count, paddingStart, scrollMargin, getItemKey, enabled },
|
|
588
|
+
itemSizeCache,
|
|
589
|
+
) => {
|
|
590
|
+
if (!enabled) {
|
|
591
|
+
this.measurementsCache = []
|
|
592
|
+
this.itemSizeCache.clear()
|
|
593
|
+
return []
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (this.measurementsCache.length === 0) {
|
|
597
|
+
this.measurementsCache = this.options.initialMeasurementsCache
|
|
598
|
+
this.measurementsCache.forEach((item) => {
|
|
599
|
+
this.itemSizeCache.set(item.key, item.size)
|
|
600
|
+
})
|
|
601
|
+
}
|
|
602
|
+
|
|
564
603
|
const min =
|
|
565
604
|
this.pendingMeasuredCacheIndexes.length > 0
|
|
566
605
|
? Math.min(...this.pendingMeasuredCacheIndexes)
|
|
@@ -614,7 +653,7 @@ export class Virtualizer<
|
|
|
614
653
|
)
|
|
615
654
|
|
|
616
655
|
calculateRange = memo(
|
|
617
|
-
() => [this.getMeasurements(), this.getSize(), this.
|
|
656
|
+
() => [this.getMeasurements(), this.getSize(), this.getScrollOffset()],
|
|
618
657
|
(measurements, outerSize, scrollOffset) => {
|
|
619
658
|
return (this.range =
|
|
620
659
|
measurements.length > 0 && outerSize > 0
|
|
@@ -672,7 +711,7 @@ export class Virtualizer<
|
|
|
672
711
|
node: TItemElement,
|
|
673
712
|
entry: ResizeObserverEntry | undefined,
|
|
674
713
|
) => {
|
|
675
|
-
const item = this.
|
|
714
|
+
const item = this.getMeasurements()[this.indexFromElement(node)]
|
|
676
715
|
|
|
677
716
|
if (!item || !node.isConnected) {
|
|
678
717
|
this.measureElementCache.forEach((cached, key) => {
|
|
@@ -707,13 +746,13 @@ export class Virtualizer<
|
|
|
707
746
|
if (
|
|
708
747
|
this.shouldAdjustScrollPositionOnItemSizeChange !== undefined
|
|
709
748
|
? this.shouldAdjustScrollPositionOnItemSizeChange(item, delta, this)
|
|
710
|
-
: item.start < this.
|
|
749
|
+
: item.start < this.getScrollOffset() + this.scrollAdjustments
|
|
711
750
|
) {
|
|
712
751
|
if (process.env.NODE_ENV !== 'production' && this.options.debug) {
|
|
713
752
|
console.info('correction', delta)
|
|
714
753
|
}
|
|
715
754
|
|
|
716
|
-
this._scrollToOffset(this.
|
|
755
|
+
this._scrollToOffset(this.getScrollOffset(), {
|
|
717
756
|
adjustments: (this.scrollAdjustments += delta),
|
|
718
757
|
behavior: undefined,
|
|
719
758
|
})
|
|
@@ -756,7 +795,9 @@ export class Virtualizer<
|
|
|
756
795
|
|
|
757
796
|
getVirtualItemForOffset = (offset: number) => {
|
|
758
797
|
const measurements = this.getMeasurements()
|
|
759
|
-
|
|
798
|
+
if (measurements.length === 0) {
|
|
799
|
+
return undefined
|
|
800
|
+
}
|
|
760
801
|
return notUndefined(
|
|
761
802
|
measurements[
|
|
762
803
|
findNearestBinarySearch(
|
|
@@ -771,11 +812,12 @@ export class Virtualizer<
|
|
|
771
812
|
|
|
772
813
|
getOffsetForAlignment = (toOffset: number, align: ScrollAlignment) => {
|
|
773
814
|
const size = this.getSize()
|
|
815
|
+
const scrollOffset = this.getScrollOffset()
|
|
774
816
|
|
|
775
817
|
if (align === 'auto') {
|
|
776
|
-
if (toOffset <=
|
|
818
|
+
if (toOffset <= scrollOffset) {
|
|
777
819
|
align = 'start'
|
|
778
|
-
} else if (toOffset >=
|
|
820
|
+
} else if (toOffset >= scrollOffset + size) {
|
|
779
821
|
align = 'end'
|
|
780
822
|
} else {
|
|
781
823
|
align = 'start'
|
|
@@ -799,7 +841,7 @@ export class Virtualizer<
|
|
|
799
841
|
: this.scrollElement[scrollSizeProp]
|
|
800
842
|
: 0
|
|
801
843
|
|
|
802
|
-
const maxOffset = scrollSize -
|
|
844
|
+
const maxOffset = scrollSize - size
|
|
803
845
|
|
|
804
846
|
return Math.max(Math.min(maxOffset, toOffset), 0)
|
|
805
847
|
}
|
|
@@ -807,28 +849,28 @@ export class Virtualizer<
|
|
|
807
849
|
getOffsetForIndex = (index: number, align: ScrollAlignment = 'auto') => {
|
|
808
850
|
index = Math.max(0, Math.min(index, this.options.count - 1))
|
|
809
851
|
|
|
810
|
-
const
|
|
852
|
+
const item = this.getMeasurements()[index]
|
|
853
|
+
if (!item) {
|
|
854
|
+
return undefined
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
const size = this.getSize()
|
|
858
|
+
const scrollOffset = this.getScrollOffset()
|
|
811
859
|
|
|
812
860
|
if (align === 'auto') {
|
|
813
|
-
if (
|
|
814
|
-
measurement.end >=
|
|
815
|
-
this.scrollOffset + this.getSize() - this.options.scrollPaddingEnd
|
|
816
|
-
) {
|
|
861
|
+
if (item.end >= scrollOffset + size - this.options.scrollPaddingEnd) {
|
|
817
862
|
align = 'end'
|
|
818
|
-
} else if (
|
|
819
|
-
measurement.start <=
|
|
820
|
-
this.scrollOffset + this.options.scrollPaddingStart
|
|
821
|
-
) {
|
|
863
|
+
} else if (item.start <= scrollOffset + this.options.scrollPaddingStart) {
|
|
822
864
|
align = 'start'
|
|
823
865
|
} else {
|
|
824
|
-
return [
|
|
866
|
+
return [scrollOffset, align] as const
|
|
825
867
|
}
|
|
826
868
|
}
|
|
827
869
|
|
|
828
870
|
const toOffset =
|
|
829
871
|
align === 'end'
|
|
830
|
-
?
|
|
831
|
-
:
|
|
872
|
+
? item.end + this.options.scrollPaddingEnd
|
|
873
|
+
: item.start - this.options.scrollPaddingStart
|
|
832
874
|
|
|
833
875
|
return [this.getOffsetForAlignment(toOffset, align), align] as const
|
|
834
876
|
}
|
|
@@ -874,9 +916,12 @@ export class Virtualizer<
|
|
|
874
916
|
)
|
|
875
917
|
}
|
|
876
918
|
|
|
877
|
-
const
|
|
919
|
+
const offsetAndAlign = this.getOffsetForIndex(index, initialAlign)
|
|
920
|
+
if (!offsetAndAlign) return
|
|
921
|
+
|
|
922
|
+
const [offset, align] = offsetAndAlign
|
|
878
923
|
|
|
879
|
-
this._scrollToOffset(
|
|
924
|
+
this._scrollToOffset(offset, { adjustments: undefined, behavior })
|
|
880
925
|
|
|
881
926
|
if (behavior !== 'smooth' && this.isDynamicMode() && this.targetWindow) {
|
|
882
927
|
this.scrollToIndexTimeoutId = this.targetWindow.setTimeout(() => {
|
|
@@ -887,9 +932,11 @@ export class Virtualizer<
|
|
|
887
932
|
)
|
|
888
933
|
|
|
889
934
|
if (elementInDOM) {
|
|
890
|
-
const [
|
|
935
|
+
const [latestOffset] = notUndefined(
|
|
936
|
+
this.getOffsetForIndex(index, align),
|
|
937
|
+
)
|
|
891
938
|
|
|
892
|
-
if (!approxEqual(
|
|
939
|
+
if (!approxEqual(latestOffset, this.getScrollOffset())) {
|
|
893
940
|
this.scrollToIndex(index, { align, behavior })
|
|
894
941
|
}
|
|
895
942
|
} else {
|
|
@@ -908,7 +955,7 @@ export class Virtualizer<
|
|
|
908
955
|
)
|
|
909
956
|
}
|
|
910
957
|
|
|
911
|
-
this._scrollToOffset(this.
|
|
958
|
+
this._scrollToOffset(this.getScrollOffset() + delta, {
|
|
912
959
|
adjustments: undefined,
|
|
913
960
|
behavior,
|
|
914
961
|
})
|