@tanstack/virtual-core 3.13.20 → 3.13.22
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 +152 -106
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +14 -6
- package/dist/esm/index.d.ts +14 -6
- package/dist/esm/index.js +152 -106
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +218 -126
package/dist/cjs/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './utils.cjs';
|
|
2
2
|
type ScrollDirection = 'forward' | 'backward';
|
|
3
3
|
type ScrollAlignment = 'start' | 'center' | 'end' | 'auto';
|
|
4
|
-
type ScrollBehavior = 'auto' | 'smooth';
|
|
4
|
+
type ScrollBehavior = 'auto' | 'smooth' | 'instant';
|
|
5
5
|
export interface ScrollToOptions {
|
|
6
6
|
align?: ScrollAlignment;
|
|
7
7
|
behavior?: ScrollBehavior;
|
|
@@ -83,7 +83,7 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
|
|
|
83
83
|
scrollElement: TScrollElement | null;
|
|
84
84
|
targetWindow: (Window & typeof globalThis) | null;
|
|
85
85
|
isScrolling: boolean;
|
|
86
|
-
private
|
|
86
|
+
private scrollState;
|
|
87
87
|
measurementsCache: Array<VirtualItem>;
|
|
88
88
|
private itemSizeCache;
|
|
89
89
|
private laneAssignments;
|
|
@@ -97,6 +97,7 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
|
|
|
97
97
|
private scrollAdjustments;
|
|
98
98
|
shouldAdjustScrollPositionOnItemSizeChange: undefined | ((item: VirtualItem, delta: number, instance: Virtualizer<TScrollElement, TItemElement>) => boolean);
|
|
99
99
|
elementsCache: Map<Key, TItemElement>;
|
|
100
|
+
private now;
|
|
100
101
|
private observer;
|
|
101
102
|
range: {
|
|
102
103
|
startIndex: number;
|
|
@@ -109,6 +110,9 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
|
|
|
109
110
|
private cleanup;
|
|
110
111
|
_didMount: () => () => void;
|
|
111
112
|
_willUpdate: () => void;
|
|
113
|
+
private rafId;
|
|
114
|
+
private scheduleScrollReconcile;
|
|
115
|
+
private reconcileScroll;
|
|
112
116
|
private getSize;
|
|
113
117
|
private getScrollOffset;
|
|
114
118
|
private getFurthestMeasurement;
|
|
@@ -126,9 +130,14 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
|
|
|
126
130
|
updateDeps(newDeps: [(range: Range) => number[], number, number, number | null, number | null]): void;
|
|
127
131
|
};
|
|
128
132
|
indexFromElement: (node: TItemElement) => number;
|
|
129
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Determines if an item at the given index should be measured during smooth scroll.
|
|
135
|
+
* During smooth scroll, only items within a buffer range around the target are measured
|
|
136
|
+
* to prevent items far from the target from pushing it away.
|
|
137
|
+
*/
|
|
138
|
+
private shouldMeasureDuringScroll;
|
|
139
|
+
measureElement: (node: TItemElement | null) => void;
|
|
130
140
|
resizeItem: (index: number, size: number) => void;
|
|
131
|
-
measureElement: (node: TItemElement | null | undefined) => void;
|
|
132
141
|
getVirtualItems: {
|
|
133
142
|
(): VirtualItem[];
|
|
134
143
|
updateDeps(newDeps: [number[], VirtualItem[]]): void;
|
|
@@ -137,9 +146,8 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
|
|
|
137
146
|
private getMaxScrollOffset;
|
|
138
147
|
getOffsetForAlignment: (toOffset: number, align: ScrollAlignment, itemSize?: number) => number;
|
|
139
148
|
getOffsetForIndex: (index: number, align?: ScrollAlignment) => readonly [number, "auto"] | readonly [number, "start" | "center" | "end"] | undefined;
|
|
140
|
-
private isDynamicMode;
|
|
141
149
|
scrollToOffset: (toOffset: number, { align, behavior }?: ScrollToOffsetOptions) => void;
|
|
142
|
-
scrollToIndex: (index: number, { align: initialAlign, behavior }?: ScrollToIndexOptions) => void;
|
|
150
|
+
scrollToIndex: (index: number, { align: initialAlign, behavior, }?: ScrollToIndexOptions) => void;
|
|
143
151
|
scrollBy: (delta: number, { behavior }?: ScrollToOffsetOptions) => void;
|
|
144
152
|
getTotalSize: () => number;
|
|
145
153
|
private _scrollToOffset;
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './utils.js';
|
|
2
2
|
type ScrollDirection = 'forward' | 'backward';
|
|
3
3
|
type ScrollAlignment = 'start' | 'center' | 'end' | 'auto';
|
|
4
|
-
type ScrollBehavior = 'auto' | 'smooth';
|
|
4
|
+
type ScrollBehavior = 'auto' | 'smooth' | 'instant';
|
|
5
5
|
export interface ScrollToOptions {
|
|
6
6
|
align?: ScrollAlignment;
|
|
7
7
|
behavior?: ScrollBehavior;
|
|
@@ -83,7 +83,7 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
|
|
|
83
83
|
scrollElement: TScrollElement | null;
|
|
84
84
|
targetWindow: (Window & typeof globalThis) | null;
|
|
85
85
|
isScrolling: boolean;
|
|
86
|
-
private
|
|
86
|
+
private scrollState;
|
|
87
87
|
measurementsCache: Array<VirtualItem>;
|
|
88
88
|
private itemSizeCache;
|
|
89
89
|
private laneAssignments;
|
|
@@ -97,6 +97,7 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
|
|
|
97
97
|
private scrollAdjustments;
|
|
98
98
|
shouldAdjustScrollPositionOnItemSizeChange: undefined | ((item: VirtualItem, delta: number, instance: Virtualizer<TScrollElement, TItemElement>) => boolean);
|
|
99
99
|
elementsCache: Map<Key, TItemElement>;
|
|
100
|
+
private now;
|
|
100
101
|
private observer;
|
|
101
102
|
range: {
|
|
102
103
|
startIndex: number;
|
|
@@ -109,6 +110,9 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
|
|
|
109
110
|
private cleanup;
|
|
110
111
|
_didMount: () => () => void;
|
|
111
112
|
_willUpdate: () => void;
|
|
113
|
+
private rafId;
|
|
114
|
+
private scheduleScrollReconcile;
|
|
115
|
+
private reconcileScroll;
|
|
112
116
|
private getSize;
|
|
113
117
|
private getScrollOffset;
|
|
114
118
|
private getFurthestMeasurement;
|
|
@@ -126,9 +130,14 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
|
|
|
126
130
|
updateDeps(newDeps: [(range: Range) => number[], number, number, number | null, number | null]): void;
|
|
127
131
|
};
|
|
128
132
|
indexFromElement: (node: TItemElement) => number;
|
|
129
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Determines if an item at the given index should be measured during smooth scroll.
|
|
135
|
+
* During smooth scroll, only items within a buffer range around the target are measured
|
|
136
|
+
* to prevent items far from the target from pushing it away.
|
|
137
|
+
*/
|
|
138
|
+
private shouldMeasureDuringScroll;
|
|
139
|
+
measureElement: (node: TItemElement | null) => void;
|
|
130
140
|
resizeItem: (index: number, size: number) => void;
|
|
131
|
-
measureElement: (node: TItemElement | null | undefined) => void;
|
|
132
141
|
getVirtualItems: {
|
|
133
142
|
(): VirtualItem[];
|
|
134
143
|
updateDeps(newDeps: [number[], VirtualItem[]]): void;
|
|
@@ -137,9 +146,8 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
|
|
|
137
146
|
private getMaxScrollOffset;
|
|
138
147
|
getOffsetForAlignment: (toOffset: number, align: ScrollAlignment, itemSize?: number) => number;
|
|
139
148
|
getOffsetForIndex: (index: number, align?: ScrollAlignment) => readonly [number, "auto"] | readonly [number, "start" | "center" | "end"] | undefined;
|
|
140
|
-
private isDynamicMode;
|
|
141
149
|
scrollToOffset: (toOffset: number, { align, behavior }?: ScrollToOffsetOptions) => void;
|
|
142
|
-
scrollToIndex: (index: number, { align: initialAlign, behavior }?: ScrollToIndexOptions) => void;
|
|
150
|
+
scrollToIndex: (index: number, { align: initialAlign, behavior, }?: ScrollToIndexOptions) => void;
|
|
143
151
|
scrollBy: (delta: number, { behavior }?: ScrollToOffsetOptions) => void;
|
|
144
152
|
getTotalSize: () => number;
|
|
145
153
|
private _scrollToOffset;
|
package/dist/esm/index.js
CHANGED
|
@@ -181,7 +181,7 @@ class Virtualizer {
|
|
|
181
181
|
this.scrollElement = null;
|
|
182
182
|
this.targetWindow = null;
|
|
183
183
|
this.isScrolling = false;
|
|
184
|
-
this.
|
|
184
|
+
this.scrollState = null;
|
|
185
185
|
this.measurementsCache = [];
|
|
186
186
|
this.itemSizeCache = /* @__PURE__ */ new Map();
|
|
187
187
|
this.laneAssignments = /* @__PURE__ */ new Map();
|
|
@@ -194,6 +194,10 @@ class Virtualizer {
|
|
|
194
194
|
this.scrollDirection = null;
|
|
195
195
|
this.scrollAdjustments = 0;
|
|
196
196
|
this.elementsCache = /* @__PURE__ */ new Map();
|
|
197
|
+
this.now = () => {
|
|
198
|
+
var _a, _b, _c;
|
|
199
|
+
return ((_c = (_b = (_a = this.targetWindow) == null ? void 0 : _a.performance) == null ? void 0 : _b.now) == null ? void 0 : _c.call(_b)) ?? Date.now();
|
|
200
|
+
};
|
|
197
201
|
this.observer = /* @__PURE__ */ (() => {
|
|
198
202
|
let _ro = null;
|
|
199
203
|
const get = () => {
|
|
@@ -206,7 +210,19 @@ class Virtualizer {
|
|
|
206
210
|
return _ro = new this.targetWindow.ResizeObserver((entries) => {
|
|
207
211
|
entries.forEach((entry) => {
|
|
208
212
|
const run = () => {
|
|
209
|
-
|
|
213
|
+
const node = entry.target;
|
|
214
|
+
const index = this.indexFromElement(node);
|
|
215
|
+
if (!node.isConnected) {
|
|
216
|
+
this.observer.unobserve(node);
|
|
217
|
+
this.elementsCache.delete(this.options.getItemKey(index));
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (this.shouldMeasureDuringScroll(index)) {
|
|
221
|
+
this.resizeItem(
|
|
222
|
+
index,
|
|
223
|
+
this.options.measureElement(node, entry, this)
|
|
224
|
+
);
|
|
225
|
+
}
|
|
210
226
|
};
|
|
211
227
|
this.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
|
|
212
228
|
});
|
|
@@ -291,6 +307,11 @@ class Virtualizer {
|
|
|
291
307
|
this.unsubs.filter(Boolean).forEach((d) => d());
|
|
292
308
|
this.unsubs = [];
|
|
293
309
|
this.observer.disconnect();
|
|
310
|
+
if (this.rafId != null && this.targetWindow) {
|
|
311
|
+
this.targetWindow.cancelAnimationFrame(this.rafId);
|
|
312
|
+
this.rafId = null;
|
|
313
|
+
}
|
|
314
|
+
this.scrollState = null;
|
|
294
315
|
this.scrollElement = null;
|
|
295
316
|
this.targetWindow = null;
|
|
296
317
|
};
|
|
@@ -329,6 +350,9 @@ class Virtualizer {
|
|
|
329
350
|
this.scrollDirection = isScrolling ? this.getScrollOffset() < offset ? "forward" : "backward" : null;
|
|
330
351
|
this.scrollOffset = offset;
|
|
331
352
|
this.isScrolling = isScrolling;
|
|
353
|
+
if (this.scrollState) {
|
|
354
|
+
this.scheduleScrollReconcile();
|
|
355
|
+
}
|
|
332
356
|
this.maybeNotify();
|
|
333
357
|
})
|
|
334
358
|
);
|
|
@@ -338,6 +362,7 @@ class Virtualizer {
|
|
|
338
362
|
});
|
|
339
363
|
}
|
|
340
364
|
};
|
|
365
|
+
this.rafId = null;
|
|
341
366
|
this.getSize = () => {
|
|
342
367
|
if (!this.options.enabled) {
|
|
343
368
|
this.scrollRect = null;
|
|
@@ -556,17 +581,38 @@ class Virtualizer {
|
|
|
556
581
|
}
|
|
557
582
|
return parseInt(indexStr, 10);
|
|
558
583
|
};
|
|
559
|
-
this.
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
return;
|
|
584
|
+
this.shouldMeasureDuringScroll = (index) => {
|
|
585
|
+
var _a;
|
|
586
|
+
if (!this.scrollState || this.scrollState.behavior !== "smooth") {
|
|
587
|
+
return true;
|
|
563
588
|
}
|
|
564
|
-
const index = this.
|
|
565
|
-
|
|
566
|
-
|
|
589
|
+
const scrollIndex = this.scrollState.index ?? ((_a = this.getVirtualItemForOffset(this.scrollState.lastTargetOffset)) == null ? void 0 : _a.index);
|
|
590
|
+
if (scrollIndex !== void 0 && this.range) {
|
|
591
|
+
const bufferSize = Math.max(
|
|
592
|
+
this.options.overscan,
|
|
593
|
+
Math.ceil((this.range.endIndex - this.range.startIndex) / 2)
|
|
594
|
+
);
|
|
595
|
+
const minIndex = Math.max(0, scrollIndex - bufferSize);
|
|
596
|
+
const maxIndex = Math.min(
|
|
597
|
+
this.options.count - 1,
|
|
598
|
+
scrollIndex + bufferSize
|
|
599
|
+
);
|
|
600
|
+
return index >= minIndex && index <= maxIndex;
|
|
601
|
+
}
|
|
602
|
+
return true;
|
|
603
|
+
};
|
|
604
|
+
this.measureElement = (node) => {
|
|
605
|
+
if (!node) {
|
|
606
|
+
this.elementsCache.forEach((cached, key2) => {
|
|
607
|
+
if (!cached.isConnected) {
|
|
608
|
+
this.observer.unobserve(cached);
|
|
609
|
+
this.elementsCache.delete(key2);
|
|
610
|
+
}
|
|
611
|
+
});
|
|
567
612
|
return;
|
|
568
613
|
}
|
|
569
|
-
const
|
|
614
|
+
const index = this.indexFromElement(node);
|
|
615
|
+
const key = this.options.getItemKey(index);
|
|
570
616
|
const prevNode = this.elementsCache.get(key);
|
|
571
617
|
if (prevNode !== node) {
|
|
572
618
|
if (prevNode) {
|
|
@@ -575,17 +621,18 @@ class Virtualizer {
|
|
|
575
621
|
this.observer.observe(node);
|
|
576
622
|
this.elementsCache.set(key, node);
|
|
577
623
|
}
|
|
578
|
-
this.
|
|
624
|
+
if ((!this.isScrolling || this.scrollState) && this.shouldMeasureDuringScroll(index)) {
|
|
625
|
+
this.resizeItem(index, this.options.measureElement(node, void 0, this));
|
|
626
|
+
}
|
|
579
627
|
};
|
|
580
628
|
this.resizeItem = (index, size) => {
|
|
629
|
+
var _a;
|
|
581
630
|
const item = this.measurementsCache[index];
|
|
582
|
-
if (!item)
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
631
|
+
if (!item) return;
|
|
585
632
|
const itemSize = this.itemSizeCache.get(item.key) ?? item.size;
|
|
586
633
|
const delta = size - itemSize;
|
|
587
634
|
if (delta !== 0) {
|
|
588
|
-
if (this.shouldAdjustScrollPositionOnItemSizeChange !== void 0 ? this.shouldAdjustScrollPositionOnItemSizeChange(item, delta, this) : item.start < this.getScrollOffset() + this.scrollAdjustments) {
|
|
635
|
+
if (((_a = this.scrollState) == null ? void 0 : _a.behavior) !== "smooth" && (this.shouldAdjustScrollPositionOnItemSizeChange !== void 0 ? this.shouldAdjustScrollPositionOnItemSizeChange(item, delta, this) : item.start < this.getScrollOffset() + this.scrollAdjustments)) {
|
|
589
636
|
if (process.env.NODE_ENV !== "production" && this.options.debug) {
|
|
590
637
|
console.info("correction", delta);
|
|
591
638
|
}
|
|
@@ -599,18 +646,6 @@ class Virtualizer {
|
|
|
599
646
|
this.notify(false);
|
|
600
647
|
}
|
|
601
648
|
};
|
|
602
|
-
this.measureElement = (node) => {
|
|
603
|
-
if (!node) {
|
|
604
|
-
this.elementsCache.forEach((cached, key) => {
|
|
605
|
-
if (!cached.isConnected) {
|
|
606
|
-
this.observer.unobserve(cached);
|
|
607
|
-
this.elementsCache.delete(key);
|
|
608
|
-
}
|
|
609
|
-
});
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
this._measureElement(node, void 0);
|
|
613
|
-
};
|
|
614
649
|
this.getVirtualItems = memo(
|
|
615
650
|
() => [this.getVirtualIndexes(), this.getMeasurements()],
|
|
616
651
|
(indexes, measurements) => {
|
|
@@ -667,12 +702,10 @@ class Virtualizer {
|
|
|
667
702
|
};
|
|
668
703
|
this.getOffsetForIndex = (index, align = "auto") => {
|
|
669
704
|
index = Math.max(0, Math.min(index, this.options.count - 1));
|
|
670
|
-
const item = this.measurementsCache[index];
|
|
671
|
-
if (!item) {
|
|
672
|
-
return void 0;
|
|
673
|
-
}
|
|
674
705
|
const size = this.getSize();
|
|
675
706
|
const scrollOffset = this.getScrollOffset();
|
|
707
|
+
const item = this.measurementsCache[index];
|
|
708
|
+
if (!item) return;
|
|
676
709
|
if (align === "auto") {
|
|
677
710
|
if (item.end >= scrollOffset + size - this.options.scrollPaddingEnd) {
|
|
678
711
|
align = "end";
|
|
@@ -691,85 +724,55 @@ class Virtualizer {
|
|
|
691
724
|
align
|
|
692
725
|
];
|
|
693
726
|
};
|
|
694
|
-
this.
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
}
|
|
727
|
+
this.scrollToOffset = (toOffset, { align = "start", behavior = "auto" } = {}) => {
|
|
728
|
+
const offset = this.getOffsetForAlignment(toOffset, align);
|
|
729
|
+
const now = this.now();
|
|
730
|
+
this.scrollState = {
|
|
731
|
+
index: null,
|
|
732
|
+
align,
|
|
733
|
+
behavior,
|
|
734
|
+
startedAt: now,
|
|
735
|
+
lastTargetOffset: offset,
|
|
736
|
+
stableFrames: 0
|
|
737
|
+
};
|
|
738
|
+
this._scrollToOffset(offset, { adjustments: void 0, behavior });
|
|
739
|
+
this.scheduleScrollReconcile();
|
|
705
740
|
};
|
|
706
|
-
this.scrollToIndex = (index, {
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
);
|
|
711
|
-
}
|
|
741
|
+
this.scrollToIndex = (index, {
|
|
742
|
+
align: initialAlign = "auto",
|
|
743
|
+
behavior = "auto"
|
|
744
|
+
} = {}) => {
|
|
712
745
|
index = Math.max(0, Math.min(index, this.options.count - 1));
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
if (!this.targetWindow) return;
|
|
727
|
-
const verify = () => {
|
|
728
|
-
if (this.currentScrollToIndex !== index) return;
|
|
729
|
-
const currentOffset = this.getScrollOffset();
|
|
730
|
-
const afterInfo = this.getOffsetForIndex(index, align);
|
|
731
|
-
if (!afterInfo) {
|
|
732
|
-
console.warn("Failed to get offset for index:", index);
|
|
733
|
-
return;
|
|
734
|
-
}
|
|
735
|
-
if (!approxEqual(afterInfo[0], currentOffset)) {
|
|
736
|
-
scheduleRetry(align);
|
|
737
|
-
}
|
|
738
|
-
};
|
|
739
|
-
if (this.isDynamicMode()) {
|
|
740
|
-
this.targetWindow.requestAnimationFrame(verify);
|
|
741
|
-
} else {
|
|
742
|
-
verify();
|
|
743
|
-
}
|
|
744
|
-
});
|
|
745
|
-
};
|
|
746
|
-
const scheduleRetry = (align) => {
|
|
747
|
-
if (!this.targetWindow) return;
|
|
748
|
-
if (this.currentScrollToIndex !== index) return;
|
|
749
|
-
attempts++;
|
|
750
|
-
if (attempts < maxAttempts) {
|
|
751
|
-
if (process.env.NODE_ENV !== "production" && this.options.debug) {
|
|
752
|
-
console.info("Schedule retry", attempts, maxAttempts);
|
|
753
|
-
}
|
|
754
|
-
this.targetWindow.requestAnimationFrame(() => tryScroll(align));
|
|
755
|
-
} else {
|
|
756
|
-
console.warn(
|
|
757
|
-
`Failed to scroll to index ${index} after ${maxAttempts} attempts.`
|
|
758
|
-
);
|
|
759
|
-
}
|
|
746
|
+
const offsetInfo = this.getOffsetForIndex(index, initialAlign);
|
|
747
|
+
if (!offsetInfo) {
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
const [offset, align] = offsetInfo;
|
|
751
|
+
const now = this.now();
|
|
752
|
+
this.scrollState = {
|
|
753
|
+
index,
|
|
754
|
+
align,
|
|
755
|
+
behavior,
|
|
756
|
+
startedAt: now,
|
|
757
|
+
lastTargetOffset: offset,
|
|
758
|
+
stableFrames: 0
|
|
760
759
|
};
|
|
761
|
-
|
|
760
|
+
this._scrollToOffset(offset, { adjustments: void 0, behavior });
|
|
761
|
+
this.scheduleScrollReconcile();
|
|
762
762
|
};
|
|
763
|
-
this.scrollBy = (delta, { behavior } = {}) => {
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
763
|
+
this.scrollBy = (delta, { behavior = "auto" } = {}) => {
|
|
764
|
+
const offset = this.getScrollOffset() + delta;
|
|
765
|
+
const now = this.now();
|
|
766
|
+
this.scrollState = {
|
|
767
|
+
index: null,
|
|
768
|
+
align: "start",
|
|
769
|
+
behavior,
|
|
770
|
+
startedAt: now,
|
|
771
|
+
lastTargetOffset: offset,
|
|
772
|
+
stableFrames: 0
|
|
773
|
+
};
|
|
774
|
+
this._scrollToOffset(offset, { adjustments: void 0, behavior });
|
|
775
|
+
this.scheduleScrollReconcile();
|
|
773
776
|
};
|
|
774
777
|
this.getTotalSize = () => {
|
|
775
778
|
var _a;
|
|
@@ -809,6 +812,49 @@ class Virtualizer {
|
|
|
809
812
|
};
|
|
810
813
|
this.setOptions(opts);
|
|
811
814
|
}
|
|
815
|
+
scheduleScrollReconcile() {
|
|
816
|
+
if (!this.targetWindow) {
|
|
817
|
+
this.scrollState = null;
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if (this.rafId != null) return;
|
|
821
|
+
this.rafId = this.targetWindow.requestAnimationFrame(() => {
|
|
822
|
+
this.rafId = null;
|
|
823
|
+
this.reconcileScroll();
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
reconcileScroll() {
|
|
827
|
+
if (!this.scrollState) return;
|
|
828
|
+
const el = this.scrollElement;
|
|
829
|
+
if (!el) return;
|
|
830
|
+
const MAX_RECONCILE_MS = 5e3;
|
|
831
|
+
if (this.now() - this.scrollState.startedAt > MAX_RECONCILE_MS) {
|
|
832
|
+
this.scrollState = null;
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
const offsetInfo = this.scrollState.index != null ? this.getOffsetForIndex(this.scrollState.index, this.scrollState.align) : void 0;
|
|
836
|
+
const targetOffset = offsetInfo ? offsetInfo[0] : this.scrollState.lastTargetOffset;
|
|
837
|
+
const STABLE_FRAMES = 1;
|
|
838
|
+
const targetChanged = targetOffset !== this.scrollState.lastTargetOffset;
|
|
839
|
+
if (!targetChanged && approxEqual(targetOffset, this.getScrollOffset())) {
|
|
840
|
+
this.scrollState.stableFrames++;
|
|
841
|
+
if (this.scrollState.stableFrames >= STABLE_FRAMES) {
|
|
842
|
+
this.scrollState = null;
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
} else {
|
|
846
|
+
this.scrollState.stableFrames = 0;
|
|
847
|
+
if (targetChanged) {
|
|
848
|
+
this.scrollState.lastTargetOffset = targetOffset;
|
|
849
|
+
this.scrollState.behavior = "auto";
|
|
850
|
+
this._scrollToOffset(targetOffset, {
|
|
851
|
+
adjustments: void 0,
|
|
852
|
+
behavior: "auto"
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
this.scheduleScrollReconcile();
|
|
857
|
+
}
|
|
812
858
|
}
|
|
813
859
|
const findNearestBinarySearch = (low, high, getCurrentValue, value) => {
|
|
814
860
|
while (low <= high) {
|