@tanstack/virtual-core 3.0.0-beta.9 → 3.0.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/LICENSE +21 -0
- package/build/lib/_virtual/_rollupPluginBabelHelpers.esm.js +27 -0
- package/build/lib/_virtual/_rollupPluginBabelHelpers.esm.js.map +1 -0
- package/build/lib/_virtual/_rollupPluginBabelHelpers.js +31 -0
- package/build/lib/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
- package/build/lib/_virtual/_rollupPluginBabelHelpers.mjs +27 -0
- package/build/lib/_virtual/_rollupPluginBabelHelpers.mjs.map +1 -0
- package/build/lib/index.d.ts +126 -0
- package/build/lib/index.esm.js +639 -0
- package/build/lib/index.esm.js.map +1 -0
- package/build/lib/index.js +654 -0
- package/build/lib/index.js.map +1 -0
- package/build/lib/index.mjs +639 -0
- package/build/lib/index.mjs.map +1 -0
- package/build/lib/utils.d.ts +10 -0
- package/build/lib/utils.esm.js +58 -0
- package/build/lib/utils.esm.js.map +1 -0
- package/build/{cjs/packages/virtual-core/src → lib}/utils.js +28 -21
- package/build/lib/utils.js.map +1 -0
- package/build/lib/utils.mjs +58 -0
- package/build/lib/utils.mjs.map +1 -0
- package/build/umd/index.development.js +600 -493
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +1 -1
- package/build/umd/index.production.js.map +1 -1
- package/package.json +16 -14
- package/src/index.ts +566 -278
- package/src/utils.ts +17 -5
- package/build/cjs/packages/virtual-core/src/index.js +0 -497
- package/build/cjs/packages/virtual-core/src/index.js.map +0 -1
- package/build/cjs/packages/virtual-core/src/utils.js.map +0 -1
- package/build/esm/index.js +0 -591
- package/build/esm/index.js.map +0 -1
- package/build/stats-html.html +0 -2689
- package/build/stats.json +0 -101
- package/build/types/index.d.ts +0 -88
- package/build/types/utils.d.ts +0 -7
package/src/index.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { memo } from './utils'
|
|
1
|
+
import { approxEqual, memo, notUndefined } from './utils'
|
|
3
2
|
|
|
4
3
|
export * from './utils'
|
|
5
4
|
|
|
6
5
|
//
|
|
7
6
|
|
|
7
|
+
type ScrollDirection = 'forward' | 'backward'
|
|
8
|
+
|
|
8
9
|
type ScrollAlignment = 'start' | 'center' | 'end' | 'auto'
|
|
9
10
|
|
|
11
|
+
type ScrollBehavior = 'auto' | 'smooth'
|
|
12
|
+
|
|
10
13
|
export interface ScrollToOptions {
|
|
11
|
-
align
|
|
14
|
+
align?: ScrollAlignment
|
|
15
|
+
behavior?: ScrollBehavior
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
type ScrollToOffsetOptions = ScrollToOptions
|
|
@@ -24,12 +28,13 @@ export interface Range {
|
|
|
24
28
|
|
|
25
29
|
type Key = number | string
|
|
26
30
|
|
|
27
|
-
interface
|
|
31
|
+
export interface VirtualItem {
|
|
28
32
|
key: Key
|
|
29
33
|
index: number
|
|
30
34
|
start: number
|
|
31
35
|
end: number
|
|
32
36
|
size: number
|
|
37
|
+
lane: number
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
interface Rect {
|
|
@@ -37,10 +42,6 @@ interface Rect {
|
|
|
37
42
|
height: number
|
|
38
43
|
}
|
|
39
44
|
|
|
40
|
-
export interface VirtualItem<TItemElement> extends Item {
|
|
41
|
-
measureElement: (el: TItemElement | null) => void
|
|
42
|
-
}
|
|
43
|
-
|
|
44
45
|
//
|
|
45
46
|
|
|
46
47
|
export const defaultKeyExtractor = (index: number) => index
|
|
@@ -58,171 +59,176 @@ export const defaultRangeExtractor = (range: Range) => {
|
|
|
58
59
|
return arr
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
const
|
|
62
|
-
instance: Virtualizer<
|
|
62
|
+
export const observeElementRect = <T extends Element>(
|
|
63
|
+
instance: Virtualizer<T, any>,
|
|
63
64
|
cb: (rect: Rect) => void,
|
|
64
65
|
) => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
instance.options.horizontal
|
|
70
|
-
? rect.width !== prev.width
|
|
71
|
-
: rect.height !== prev.height
|
|
72
|
-
) {
|
|
73
|
-
cb(rect)
|
|
74
|
-
}
|
|
66
|
+
const element = instance.scrollElement
|
|
67
|
+
if (!element) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
75
70
|
|
|
76
|
-
|
|
71
|
+
const handler = (rect: Rect) => {
|
|
72
|
+
const { width, height } = rect
|
|
73
|
+
cb({ width: Math.round(width), height: Math.round(height) })
|
|
77
74
|
}
|
|
78
|
-
}
|
|
79
75
|
|
|
80
|
-
|
|
81
|
-
instance: Virtualizer<any, any>,
|
|
82
|
-
cb: (rect: Rect) => void,
|
|
83
|
-
) => {
|
|
84
|
-
const onResize = memoRectCallback(instance, cb)
|
|
76
|
+
handler(element.getBoundingClientRect())
|
|
85
77
|
|
|
86
|
-
const observer =
|
|
87
|
-
|
|
78
|
+
const observer = new ResizeObserver((entries) => {
|
|
79
|
+
const entry = entries[0]
|
|
80
|
+
if (entry?.borderBoxSize) {
|
|
81
|
+
const box = entry.borderBoxSize[0]
|
|
82
|
+
if (box) {
|
|
83
|
+
handler({ width: box.inlineSize, height: box.blockSize })
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
handler(element.getBoundingClientRect())
|
|
88
88
|
})
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
onResize(instance.scrollElement.getBoundingClientRect())
|
|
95
|
-
|
|
96
|
-
observer.observe()
|
|
90
|
+
observer.observe(element, { box: 'border-box' })
|
|
97
91
|
|
|
98
92
|
return () => {
|
|
99
|
-
observer.unobserve()
|
|
93
|
+
observer.unobserve(element)
|
|
100
94
|
}
|
|
101
95
|
}
|
|
102
96
|
|
|
103
97
|
export const observeWindowRect = (
|
|
104
|
-
instance: Virtualizer<
|
|
98
|
+
instance: Virtualizer<Window, any>,
|
|
105
99
|
cb: (rect: Rect) => void,
|
|
106
100
|
) => {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
memoizedCallback({
|
|
110
|
-
width: instance.scrollElement.innerWidth,
|
|
111
|
-
height: instance.scrollElement.innerHeight,
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
if (!instance.scrollElement) {
|
|
101
|
+
const element = instance.scrollElement
|
|
102
|
+
if (!element) {
|
|
115
103
|
return
|
|
116
104
|
}
|
|
117
105
|
|
|
118
|
-
|
|
106
|
+
const handler = () => {
|
|
107
|
+
cb({ width: element.innerWidth, height: element.innerHeight })
|
|
108
|
+
}
|
|
109
|
+
handler()
|
|
119
110
|
|
|
120
|
-
|
|
121
|
-
capture: false,
|
|
111
|
+
element.addEventListener('resize', handler, {
|
|
122
112
|
passive: true,
|
|
123
113
|
})
|
|
124
114
|
|
|
125
115
|
return () => {
|
|
126
|
-
|
|
116
|
+
element.removeEventListener('resize', handler)
|
|
127
117
|
}
|
|
128
118
|
}
|
|
129
119
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return (instance: Virtualizer<any, any>, cb: (offset: number) => void) => {
|
|
139
|
-
if (!instance.scrollElement) {
|
|
140
|
-
return
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const propX = scrollProps[mode][0]
|
|
144
|
-
const propY = scrollProps[mode][1]
|
|
145
|
-
|
|
146
|
-
let prevX: number = instance.scrollElement[propX]
|
|
147
|
-
let prevY: number = instance.scrollElement[propY]
|
|
120
|
+
export const observeElementOffset = <T extends Element>(
|
|
121
|
+
instance: Virtualizer<T, any>,
|
|
122
|
+
cb: (offset: number) => void,
|
|
123
|
+
) => {
|
|
124
|
+
const element = instance.scrollElement
|
|
125
|
+
if (!element) {
|
|
126
|
+
return
|
|
127
|
+
}
|
|
148
128
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
129
|
+
const handler = () => {
|
|
130
|
+
cb(element[instance.options.horizontal ? 'scrollLeft' : 'scrollTop'])
|
|
131
|
+
}
|
|
132
|
+
handler()
|
|
152
133
|
|
|
153
|
-
|
|
134
|
+
element.addEventListener('scroll', handler, {
|
|
135
|
+
passive: true,
|
|
136
|
+
})
|
|
154
137
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
138
|
+
return () => {
|
|
139
|
+
element.removeEventListener('scroll', handler)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
159
142
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
143
|
+
export const observeWindowOffset = (
|
|
144
|
+
instance: Virtualizer<Window, any>,
|
|
145
|
+
cb: (offset: number) => void,
|
|
146
|
+
) => {
|
|
147
|
+
const element = instance.scrollElement
|
|
148
|
+
if (!element) {
|
|
149
|
+
return
|
|
150
|
+
}
|
|
163
151
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
152
|
+
const handler = () => {
|
|
153
|
+
cb(element[instance.options.horizontal ? 'scrollX' : 'scrollY'])
|
|
154
|
+
}
|
|
155
|
+
handler()
|
|
167
156
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
})
|
|
157
|
+
element.addEventListener('scroll', handler, {
|
|
158
|
+
passive: true,
|
|
159
|
+
})
|
|
172
160
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
161
|
+
return () => {
|
|
162
|
+
element.removeEventListener('scroll', handler)
|
|
176
163
|
}
|
|
177
164
|
}
|
|
178
165
|
|
|
179
|
-
export const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
element: unknown,
|
|
184
|
-
instance: Virtualizer<any, any>,
|
|
166
|
+
export const measureElement = <TItemElement extends Element>(
|
|
167
|
+
element: TItemElement,
|
|
168
|
+
entry: ResizeObserverEntry | undefined,
|
|
169
|
+
instance: Virtualizer<any, TItemElement>,
|
|
185
170
|
) => {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
171
|
+
if (entry?.borderBoxSize) {
|
|
172
|
+
const box = entry.borderBoxSize[0]
|
|
173
|
+
if (box) {
|
|
174
|
+
const size = Math.round(
|
|
175
|
+
box[instance.options.horizontal ? 'inlineSize' : 'blockSize'],
|
|
176
|
+
)
|
|
177
|
+
return size
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return Math.round(
|
|
181
|
+
element.getBoundingClientRect()[
|
|
182
|
+
instance.options.horizontal ? 'width' : 'height'
|
|
183
|
+
],
|
|
184
|
+
)
|
|
189
185
|
}
|
|
190
186
|
|
|
191
|
-
export const windowScroll = (
|
|
187
|
+
export const windowScroll = <T extends Window>(
|
|
192
188
|
offset: number,
|
|
193
|
-
|
|
194
|
-
|
|
189
|
+
{
|
|
190
|
+
adjustments = 0,
|
|
191
|
+
behavior,
|
|
192
|
+
}: { adjustments?: number; behavior?: ScrollBehavior },
|
|
193
|
+
instance: Virtualizer<T, any>,
|
|
195
194
|
) => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
195
|
+
const toOffset = offset + adjustments
|
|
196
|
+
|
|
197
|
+
instance.scrollElement?.scrollTo?.({
|
|
198
|
+
[instance.options.horizontal ? 'left' : 'top']: toOffset,
|
|
199
|
+
behavior,
|
|
199
200
|
})
|
|
200
201
|
}
|
|
201
202
|
|
|
202
|
-
export const elementScroll = (
|
|
203
|
+
export const elementScroll = <T extends Element>(
|
|
203
204
|
offset: number,
|
|
204
|
-
|
|
205
|
-
|
|
205
|
+
{
|
|
206
|
+
adjustments = 0,
|
|
207
|
+
behavior,
|
|
208
|
+
}: { adjustments?: number; behavior?: ScrollBehavior },
|
|
209
|
+
instance: Virtualizer<T, any>,
|
|
206
210
|
) => {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
211
|
+
const toOffset = offset + adjustments
|
|
212
|
+
|
|
213
|
+
instance.scrollElement?.scrollTo?.({
|
|
214
|
+
[instance.options.horizontal ? 'left' : 'top']: toOffset,
|
|
215
|
+
behavior,
|
|
210
216
|
})
|
|
211
217
|
}
|
|
212
218
|
|
|
213
219
|
export interface VirtualizerOptions<
|
|
214
|
-
TScrollElement
|
|
215
|
-
TItemElement
|
|
220
|
+
TScrollElement extends Element | Window,
|
|
221
|
+
TItemElement extends Element,
|
|
216
222
|
> {
|
|
217
223
|
// Required from the user
|
|
218
224
|
count: number
|
|
219
|
-
getScrollElement: () => TScrollElement
|
|
225
|
+
getScrollElement: () => TScrollElement | null
|
|
220
226
|
estimateSize: (index: number) => number
|
|
221
227
|
|
|
222
228
|
// Required from the framework adapter (but can be overridden)
|
|
223
229
|
scrollToFn: (
|
|
224
230
|
offset: number,
|
|
225
|
-
|
|
231
|
+
options: { adjustments?: number; behavior?: ScrollBehavior },
|
|
226
232
|
instance: Virtualizer<TScrollElement, TItemElement>,
|
|
227
233
|
) => void
|
|
228
234
|
observeElementRect: (
|
|
@@ -237,9 +243,13 @@ export interface VirtualizerOptions<
|
|
|
237
243
|
// Optional
|
|
238
244
|
debug?: any
|
|
239
245
|
initialRect?: Rect
|
|
240
|
-
onChange?: (
|
|
246
|
+
onChange?: (
|
|
247
|
+
instance: Virtualizer<TScrollElement, TItemElement>,
|
|
248
|
+
sync: boolean,
|
|
249
|
+
) => void
|
|
241
250
|
measureElement?: (
|
|
242
|
-
|
|
251
|
+
element: TItemElement,
|
|
252
|
+
entry: ResizeObserverEntry | undefined,
|
|
243
253
|
instance: Virtualizer<TScrollElement, TItemElement>,
|
|
244
254
|
) => number
|
|
245
255
|
overscan?: number
|
|
@@ -251,29 +261,67 @@ export interface VirtualizerOptions<
|
|
|
251
261
|
initialOffset?: number
|
|
252
262
|
getItemKey?: (index: number) => Key
|
|
253
263
|
rangeExtractor?: (range: Range) => number[]
|
|
254
|
-
|
|
264
|
+
scrollMargin?: number
|
|
265
|
+
scrollingDelay?: number
|
|
266
|
+
indexAttribute?: string
|
|
267
|
+
initialMeasurementsCache?: VirtualItem[]
|
|
268
|
+
lanes?: number
|
|
255
269
|
}
|
|
256
270
|
|
|
257
|
-
export class Virtualizer<
|
|
271
|
+
export class Virtualizer<
|
|
272
|
+
TScrollElement extends Element | Window,
|
|
273
|
+
TItemElement extends Element,
|
|
274
|
+
> {
|
|
258
275
|
private unsubs: (void | (() => void))[] = []
|
|
259
276
|
options!: Required<VirtualizerOptions<TScrollElement, TItemElement>>
|
|
260
277
|
scrollElement: TScrollElement | null = null
|
|
261
|
-
|
|
262
|
-
private
|
|
278
|
+
isScrolling: boolean = false
|
|
279
|
+
private isScrollingTimeoutId: ReturnType<typeof setTimeout> | null = null
|
|
280
|
+
private scrollToIndexTimeoutId: ReturnType<typeof setTimeout> | null = null
|
|
281
|
+
measurementsCache: VirtualItem[] = []
|
|
282
|
+
private itemSizeCache = new Map<Key, number>()
|
|
263
283
|
private pendingMeasuredCacheIndexes: number[] = []
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
private
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
284
|
+
scrollRect: Rect
|
|
285
|
+
scrollOffset: number
|
|
286
|
+
scrollDirection: ScrollDirection | null = null
|
|
287
|
+
private scrollAdjustments: number = 0
|
|
288
|
+
measureElementCache = new Map<Key, TItemElement>()
|
|
289
|
+
private observer = (() => {
|
|
290
|
+
let _ro: ResizeObserver | null = null
|
|
291
|
+
|
|
292
|
+
const get = () => {
|
|
293
|
+
if (_ro) {
|
|
294
|
+
return _ro
|
|
295
|
+
} else if (typeof ResizeObserver !== 'undefined') {
|
|
296
|
+
return (_ro = new ResizeObserver((entries) => {
|
|
297
|
+
entries.forEach((entry) => {
|
|
298
|
+
this._measureElement(entry.target as TItemElement, entry)
|
|
299
|
+
})
|
|
300
|
+
}))
|
|
301
|
+
} else {
|
|
302
|
+
return null
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
disconnect: () => get()?.disconnect(),
|
|
308
|
+
observe: (target: Element) =>
|
|
309
|
+
get()?.observe(target, { box: 'border-box' }),
|
|
310
|
+
unobserve: (target: Element) => get()?.unobserve(target),
|
|
311
|
+
}
|
|
312
|
+
})()
|
|
313
|
+
range: { startIndex: number; endIndex: number } | null = null
|
|
272
314
|
|
|
273
315
|
constructor(opts: VirtualizerOptions<TScrollElement, TItemElement>) {
|
|
274
316
|
this.setOptions(opts)
|
|
275
317
|
this.scrollRect = this.options.initialRect
|
|
276
318
|
this.scrollOffset = this.options.initialOffset
|
|
319
|
+
this.measurementsCache = this.options.initialMeasurementsCache
|
|
320
|
+
this.measurementsCache.forEach((item) => {
|
|
321
|
+
this.itemSizeCache.set(item.key, item.size)
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
this.maybeNotify()
|
|
277
325
|
}
|
|
278
326
|
|
|
279
327
|
setOptions = (opts: VirtualizerOptions<TScrollElement, TItemElement>) => {
|
|
@@ -292,18 +340,46 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
292
340
|
horizontal: false,
|
|
293
341
|
getItemKey: defaultKeyExtractor,
|
|
294
342
|
rangeExtractor: defaultRangeExtractor,
|
|
295
|
-
enableSmoothScroll: true,
|
|
296
343
|
onChange: () => {},
|
|
297
344
|
measureElement,
|
|
298
345
|
initialRect: { width: 0, height: 0 },
|
|
346
|
+
scrollMargin: 0,
|
|
347
|
+
scrollingDelay: 150,
|
|
348
|
+
indexAttribute: 'data-index',
|
|
349
|
+
initialMeasurementsCache: [],
|
|
350
|
+
lanes: 1,
|
|
299
351
|
...opts,
|
|
300
352
|
}
|
|
301
353
|
}
|
|
302
354
|
|
|
303
|
-
private notify = () => {
|
|
304
|
-
this.options.onChange?.(this)
|
|
355
|
+
private notify = (sync: boolean) => {
|
|
356
|
+
this.options.onChange?.(this, sync)
|
|
305
357
|
}
|
|
306
358
|
|
|
359
|
+
private maybeNotify = memo(
|
|
360
|
+
() => {
|
|
361
|
+
this.calculateRange()
|
|
362
|
+
|
|
363
|
+
return [
|
|
364
|
+
this.isScrolling,
|
|
365
|
+
this.range ? this.range.startIndex : null,
|
|
366
|
+
this.range ? this.range.endIndex : null,
|
|
367
|
+
]
|
|
368
|
+
},
|
|
369
|
+
(isScrolling) => {
|
|
370
|
+
this.notify(isScrolling)
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
key: process.env.NODE_ENV !== 'production' && 'maybeNotify',
|
|
374
|
+
debug: () => this.options.debug,
|
|
375
|
+
initialDeps: [
|
|
376
|
+
this.isScrolling,
|
|
377
|
+
this.range ? this.range.startIndex : null,
|
|
378
|
+
this.range ? this.range.endIndex : null,
|
|
379
|
+
] as [boolean, number | null, number | null],
|
|
380
|
+
},
|
|
381
|
+
)
|
|
382
|
+
|
|
307
383
|
private cleanup = () => {
|
|
308
384
|
this.unsubs.filter(Boolean).forEach((d) => d!())
|
|
309
385
|
this.unsubs = []
|
|
@@ -311,7 +387,9 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
311
387
|
}
|
|
312
388
|
|
|
313
389
|
_didMount = () => {
|
|
390
|
+
this.measureElementCache.forEach(this.observer.observe)
|
|
314
391
|
return () => {
|
|
392
|
+
this.observer.disconnect()
|
|
315
393
|
this.cleanup()
|
|
316
394
|
}
|
|
317
395
|
}
|
|
@@ -324,17 +402,45 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
324
402
|
|
|
325
403
|
this.scrollElement = scrollElement
|
|
326
404
|
|
|
405
|
+
this._scrollToOffset(this.scrollOffset, {
|
|
406
|
+
adjustments: undefined,
|
|
407
|
+
behavior: undefined,
|
|
408
|
+
})
|
|
409
|
+
|
|
327
410
|
this.unsubs.push(
|
|
328
411
|
this.options.observeElementRect(this, (rect) => {
|
|
329
412
|
this.scrollRect = rect
|
|
330
|
-
this.
|
|
413
|
+
this.maybeNotify()
|
|
331
414
|
}),
|
|
332
415
|
)
|
|
333
416
|
|
|
334
417
|
this.unsubs.push(
|
|
335
418
|
this.options.observeElementOffset(this, (offset) => {
|
|
419
|
+
this.scrollAdjustments = 0
|
|
420
|
+
|
|
421
|
+
if (this.scrollOffset === offset) {
|
|
422
|
+
return
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (this.isScrollingTimeoutId !== null) {
|
|
426
|
+
clearTimeout(this.isScrollingTimeoutId)
|
|
427
|
+
this.isScrollingTimeoutId = null
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
this.isScrolling = true
|
|
431
|
+
this.scrollDirection =
|
|
432
|
+
this.scrollOffset < offset ? 'forward' : 'backward'
|
|
336
433
|
this.scrollOffset = offset
|
|
337
|
-
|
|
434
|
+
|
|
435
|
+
this.maybeNotify()
|
|
436
|
+
|
|
437
|
+
this.isScrollingTimeoutId = setTimeout(() => {
|
|
438
|
+
this.isScrollingTimeoutId = null
|
|
439
|
+
this.isScrolling = false
|
|
440
|
+
this.scrollDirection = null
|
|
441
|
+
|
|
442
|
+
this.maybeNotify()
|
|
443
|
+
}, this.options.scrollingDelay)
|
|
338
444
|
}),
|
|
339
445
|
)
|
|
340
446
|
}
|
|
@@ -344,14 +450,67 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
344
450
|
return this.scrollRect[this.options.horizontal ? 'width' : 'height']
|
|
345
451
|
}
|
|
346
452
|
|
|
347
|
-
private
|
|
453
|
+
private memoOptions = memo(
|
|
348
454
|
() => [
|
|
349
455
|
this.options.count,
|
|
350
456
|
this.options.paddingStart,
|
|
457
|
+
this.options.scrollMargin,
|
|
351
458
|
this.options.getItemKey,
|
|
352
|
-
this.itemMeasurementsCache,
|
|
353
459
|
],
|
|
354
|
-
(count, paddingStart,
|
|
460
|
+
(count, paddingStart, scrollMargin, getItemKey) => {
|
|
461
|
+
this.pendingMeasuredCacheIndexes = []
|
|
462
|
+
return {
|
|
463
|
+
count,
|
|
464
|
+
paddingStart,
|
|
465
|
+
scrollMargin,
|
|
466
|
+
getItemKey,
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
key: false,
|
|
471
|
+
},
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
private getFurthestMeasurement = (
|
|
475
|
+
measurements: VirtualItem[],
|
|
476
|
+
index: number,
|
|
477
|
+
) => {
|
|
478
|
+
const furthestMeasurementsFound = new Map<number, true>()
|
|
479
|
+
const furthestMeasurements = new Map<number, VirtualItem>()
|
|
480
|
+
for (let m = index - 1; m >= 0; m--) {
|
|
481
|
+
const measurement = measurements[m]!
|
|
482
|
+
|
|
483
|
+
if (furthestMeasurementsFound.has(measurement.lane)) {
|
|
484
|
+
continue
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const previousFurthestMeasurement = furthestMeasurements.get(
|
|
488
|
+
measurement.lane,
|
|
489
|
+
)
|
|
490
|
+
if (
|
|
491
|
+
previousFurthestMeasurement == null ||
|
|
492
|
+
measurement.end > previousFurthestMeasurement.end
|
|
493
|
+
) {
|
|
494
|
+
furthestMeasurements.set(measurement.lane, measurement)
|
|
495
|
+
} else if (measurement.end < previousFurthestMeasurement.end) {
|
|
496
|
+
furthestMeasurementsFound.set(measurement.lane, true)
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (furthestMeasurementsFound.size === this.options.lanes) {
|
|
500
|
+
break
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return furthestMeasurements.size === this.options.lanes
|
|
505
|
+
? Array.from(furthestMeasurements.values()).sort(
|
|
506
|
+
(a, b) => a.end - b.end,
|
|
507
|
+
)[0]
|
|
508
|
+
: undefined
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
private getMeasurements = memo(
|
|
512
|
+
() => [this.memoOptions(), this.itemSizeCache],
|
|
513
|
+
({ count, paddingStart, scrollMargin, getItemKey }, itemSizeCache) => {
|
|
355
514
|
const min =
|
|
356
515
|
this.pendingMeasuredCacheIndexes.length > 0
|
|
357
516
|
? Math.min(...this.pendingMeasuredCacheIndexes)
|
|
@@ -362,38 +521,62 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
362
521
|
|
|
363
522
|
for (let i = min; i < count; i++) {
|
|
364
523
|
const key = getItemKey(i)
|
|
365
|
-
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
524
|
+
|
|
525
|
+
const furthestMeasurement =
|
|
526
|
+
this.options.lanes === 1
|
|
527
|
+
? measurements[i - 1]
|
|
528
|
+
: this.getFurthestMeasurement(measurements, i)
|
|
529
|
+
|
|
530
|
+
const start = furthestMeasurement
|
|
531
|
+
? furthestMeasurement.end
|
|
532
|
+
: paddingStart + scrollMargin
|
|
533
|
+
|
|
534
|
+
const measuredSize = itemSizeCache.get(key)
|
|
369
535
|
const size =
|
|
370
536
|
typeof measuredSize === 'number'
|
|
371
537
|
? measuredSize
|
|
372
538
|
: this.options.estimateSize(i)
|
|
539
|
+
|
|
373
540
|
const end = start + size
|
|
374
|
-
|
|
541
|
+
|
|
542
|
+
const lane = furthestMeasurement
|
|
543
|
+
? furthestMeasurement.lane
|
|
544
|
+
: i % this.options.lanes
|
|
545
|
+
|
|
546
|
+
measurements[i] = {
|
|
547
|
+
index: i,
|
|
548
|
+
start,
|
|
549
|
+
size,
|
|
550
|
+
end,
|
|
551
|
+
key,
|
|
552
|
+
lane,
|
|
553
|
+
}
|
|
375
554
|
}
|
|
376
555
|
|
|
377
556
|
this.measurementsCache = measurements
|
|
557
|
+
|
|
378
558
|
return measurements
|
|
379
559
|
},
|
|
380
560
|
{
|
|
381
|
-
key: process.env.NODE_ENV
|
|
561
|
+
key: process.env.NODE_ENV !== 'production' && 'getMeasurements',
|
|
382
562
|
debug: () => this.options.debug,
|
|
383
563
|
},
|
|
384
564
|
)
|
|
385
565
|
|
|
386
|
-
|
|
566
|
+
calculateRange = memo(
|
|
387
567
|
() => [this.getMeasurements(), this.getSize(), this.scrollOffset],
|
|
388
568
|
(measurements, outerSize, scrollOffset) => {
|
|
389
|
-
return
|
|
390
|
-
measurements
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
569
|
+
return (this.range =
|
|
570
|
+
measurements.length > 0 && outerSize > 0
|
|
571
|
+
? calculateRange({
|
|
572
|
+
measurements,
|
|
573
|
+
outerSize,
|
|
574
|
+
scrollOffset,
|
|
575
|
+
})
|
|
576
|
+
: null)
|
|
394
577
|
},
|
|
395
578
|
{
|
|
396
|
-
key: process.env.NODE_ENV
|
|
579
|
+
key: process.env.NODE_ENV !== 'production' && 'calculateRange',
|
|
397
580
|
debug: () => this.options.debug,
|
|
398
581
|
},
|
|
399
582
|
)
|
|
@@ -406,144 +589,184 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
406
589
|
this.options.count,
|
|
407
590
|
],
|
|
408
591
|
(rangeExtractor, range, overscan, count) => {
|
|
409
|
-
return
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
592
|
+
return range === null
|
|
593
|
+
? []
|
|
594
|
+
: rangeExtractor({
|
|
595
|
+
...range,
|
|
596
|
+
overscan,
|
|
597
|
+
count,
|
|
598
|
+
})
|
|
414
599
|
},
|
|
415
600
|
{
|
|
416
|
-
key: process.env.NODE_ENV
|
|
601
|
+
key: process.env.NODE_ENV !== 'production' && 'getIndexes',
|
|
602
|
+
debug: () => this.options.debug,
|
|
417
603
|
},
|
|
418
604
|
)
|
|
419
605
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
this.getMeasurements(),
|
|
424
|
-
this.options.measureElement,
|
|
425
|
-
],
|
|
426
|
-
(indexes, measurements, measureElement) => {
|
|
427
|
-
const makeMeasureElement =
|
|
428
|
-
(index: number) => (measurableItem: TItemElement | null) => {
|
|
429
|
-
const item = this.measurementsCache[index]!
|
|
606
|
+
indexFromElement = (node: TItemElement) => {
|
|
607
|
+
const attributeName = this.options.indexAttribute
|
|
608
|
+
const indexStr = node.getAttribute(attributeName)
|
|
430
609
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
610
|
+
if (!indexStr) {
|
|
611
|
+
console.warn(
|
|
612
|
+
`Missing attribute name '${attributeName}={index}' on measured element.`,
|
|
613
|
+
)
|
|
614
|
+
return -1
|
|
615
|
+
}
|
|
434
616
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
617
|
+
return parseInt(indexStr, 10)
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
private _measureElement = (
|
|
621
|
+
node: TItemElement,
|
|
622
|
+
entry: ResizeObserverEntry | undefined,
|
|
623
|
+
) => {
|
|
624
|
+
const item = this.measurementsCache[this.indexFromElement(node)]
|
|
625
|
+
|
|
626
|
+
if (!item || !node.isConnected) {
|
|
627
|
+
this.measureElementCache.forEach((cached, key) => {
|
|
628
|
+
if (cached === node) {
|
|
629
|
+
this.observer.unobserve(node)
|
|
630
|
+
this.measureElementCache.delete(key)
|
|
631
|
+
}
|
|
632
|
+
})
|
|
633
|
+
return
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const prevNode = this.measureElementCache.get(item.key)
|
|
637
|
+
|
|
638
|
+
if (prevNode !== node) {
|
|
639
|
+
if (prevNode) {
|
|
640
|
+
this.observer.unobserve(prevNode)
|
|
641
|
+
}
|
|
642
|
+
this.observer.observe(node)
|
|
643
|
+
this.measureElementCache.set(item.key, node)
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const measuredItemSize = this.options.measureElement(node, entry, this)
|
|
647
|
+
|
|
648
|
+
this.resizeItem(item, measuredItemSize)
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
resizeItem = (item: VirtualItem, size: number) => {
|
|
652
|
+
const itemSize = this.itemSizeCache.get(item.key) ?? item.size
|
|
653
|
+
const delta = size - itemSize
|
|
654
|
+
|
|
655
|
+
if (delta !== 0) {
|
|
656
|
+
if (item.start < this.scrollOffset) {
|
|
657
|
+
if (process.env.NODE_ENV !== 'production' && this.options.debug) {
|
|
658
|
+
console.info('correction', delta)
|
|
462
659
|
}
|
|
463
660
|
|
|
464
|
-
|
|
661
|
+
this._scrollToOffset(this.scrollOffset, {
|
|
662
|
+
adjustments: (this.scrollAdjustments += delta),
|
|
663
|
+
behavior: undefined,
|
|
664
|
+
})
|
|
665
|
+
}
|
|
465
666
|
|
|
466
|
-
|
|
667
|
+
this.pendingMeasuredCacheIndexes.push(item.index)
|
|
668
|
+
this.itemSizeCache = new Map(this.itemSizeCache.set(item.key, size))
|
|
669
|
+
|
|
670
|
+
this.notify(false)
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
measureElement = (node: TItemElement | null) => {
|
|
675
|
+
if (!node) {
|
|
676
|
+
return
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
this._measureElement(node, undefined)
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
getVirtualItems = memo(
|
|
683
|
+
() => [this.getIndexes(), this.getMeasurements()],
|
|
684
|
+
(indexes, measurements) => {
|
|
685
|
+
const virtualItems: VirtualItem[] = []
|
|
467
686
|
|
|
468
687
|
for (let k = 0, len = indexes.length; k < len; k++) {
|
|
469
688
|
const i = indexes[k]!
|
|
470
689
|
const measurement = measurements[i]!
|
|
471
690
|
|
|
472
|
-
|
|
473
|
-
...measurement,
|
|
474
|
-
measureElement: (currentMeasureElements[i] =
|
|
475
|
-
this.measureElementCache[i] ?? makeMeasureElement(i)),
|
|
476
|
-
}
|
|
477
|
-
virtualItems.push(item)
|
|
691
|
+
virtualItems.push(measurement)
|
|
478
692
|
}
|
|
479
693
|
|
|
480
|
-
this.measureElementCache = currentMeasureElements
|
|
481
|
-
|
|
482
694
|
return virtualItems
|
|
483
695
|
},
|
|
484
696
|
{
|
|
485
|
-
key: process.env.NODE_ENV
|
|
697
|
+
key: process.env.NODE_ENV !== 'production' && 'getIndexes',
|
|
698
|
+
debug: () => this.options.debug,
|
|
486
699
|
},
|
|
487
700
|
)
|
|
488
701
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
{ align }: ScrollToOffsetOptions = { align: 'start' },
|
|
492
|
-
) => {
|
|
493
|
-
const attempt = () => {
|
|
494
|
-
const offset = this.scrollOffset
|
|
495
|
-
const size = this.getSize()
|
|
496
|
-
|
|
497
|
-
if (align === 'auto') {
|
|
498
|
-
if (toOffset <= offset) {
|
|
499
|
-
align = 'start'
|
|
500
|
-
} else if (toOffset >= offset + size) {
|
|
501
|
-
align = 'end'
|
|
502
|
-
} else {
|
|
503
|
-
align = 'start'
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
if (align === 'start') {
|
|
508
|
-
this._scrollToOffset(toOffset, true)
|
|
509
|
-
} else if (align === 'end') {
|
|
510
|
-
this._scrollToOffset(toOffset - size, true)
|
|
511
|
-
} else if (align === 'center') {
|
|
512
|
-
this._scrollToOffset(toOffset - size / 2, true)
|
|
513
|
-
}
|
|
514
|
-
}
|
|
702
|
+
getVirtualItemForOffset = (offset: number) => {
|
|
703
|
+
const measurements = this.getMeasurements()
|
|
515
704
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
705
|
+
return notUndefined(
|
|
706
|
+
measurements[
|
|
707
|
+
findNearestBinarySearch(
|
|
708
|
+
0,
|
|
709
|
+
measurements.length - 1,
|
|
710
|
+
(index: number) => notUndefined(measurements[index]).start,
|
|
711
|
+
offset,
|
|
712
|
+
)
|
|
713
|
+
],
|
|
714
|
+
)
|
|
520
715
|
}
|
|
521
716
|
|
|
522
|
-
|
|
523
|
-
index: number,
|
|
524
|
-
{ align, ...rest }: ScrollToIndexOptions = { align: 'auto' },
|
|
525
|
-
) => {
|
|
526
|
-
const measurements = this.getMeasurements()
|
|
527
|
-
const offset = this.scrollOffset
|
|
717
|
+
getOffsetForAlignment = (toOffset: number, align: ScrollAlignment) => {
|
|
528
718
|
const size = this.getSize()
|
|
529
|
-
const { count } = this.options
|
|
530
719
|
|
|
531
|
-
|
|
720
|
+
if (align === 'auto') {
|
|
721
|
+
if (toOffset <= this.scrollOffset) {
|
|
722
|
+
align = 'start'
|
|
723
|
+
} else if (toOffset >= this.scrollOffset + size) {
|
|
724
|
+
align = 'end'
|
|
725
|
+
} else {
|
|
726
|
+
align = 'start'
|
|
727
|
+
}
|
|
728
|
+
}
|
|
532
729
|
|
|
533
|
-
if (
|
|
534
|
-
|
|
730
|
+
if (align === 'start') {
|
|
731
|
+
toOffset = toOffset
|
|
732
|
+
} else if (align === 'end') {
|
|
733
|
+
toOffset = toOffset - size
|
|
734
|
+
} else if (align === 'center') {
|
|
735
|
+
toOffset = toOffset - size / 2
|
|
535
736
|
}
|
|
536
737
|
|
|
738
|
+
const scrollSizeProp = this.options.horizontal
|
|
739
|
+
? 'scrollWidth'
|
|
740
|
+
: 'scrollHeight'
|
|
741
|
+
const scrollSize = this.scrollElement
|
|
742
|
+
? 'document' in this.scrollElement
|
|
743
|
+
? this.scrollElement.document.documentElement[scrollSizeProp]
|
|
744
|
+
: this.scrollElement[scrollSizeProp]
|
|
745
|
+
: 0
|
|
746
|
+
|
|
747
|
+
const maxOffset = scrollSize - this.getSize()
|
|
748
|
+
|
|
749
|
+
return Math.max(Math.min(maxOffset, toOffset), 0)
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
getOffsetForIndex = (index: number, align: ScrollAlignment = 'auto') => {
|
|
753
|
+
index = Math.max(0, Math.min(index, this.options.count - 1))
|
|
754
|
+
|
|
755
|
+
const measurement = notUndefined(this.getMeasurements()[index])
|
|
756
|
+
|
|
537
757
|
if (align === 'auto') {
|
|
538
|
-
if (
|
|
758
|
+
if (
|
|
759
|
+
measurement.end >=
|
|
760
|
+
this.scrollOffset + this.getSize() - this.options.scrollPaddingEnd
|
|
761
|
+
) {
|
|
539
762
|
align = 'end'
|
|
540
763
|
} else if (
|
|
541
764
|
measurement.start <=
|
|
542
|
-
|
|
765
|
+
this.scrollOffset + this.options.scrollPaddingStart
|
|
543
766
|
) {
|
|
544
767
|
align = 'start'
|
|
545
768
|
} else {
|
|
546
|
-
return
|
|
769
|
+
return [this.scrollOffset, align] as const
|
|
547
770
|
}
|
|
548
771
|
}
|
|
549
772
|
|
|
@@ -552,47 +775,112 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
552
775
|
? measurement.end + this.options.scrollPaddingEnd
|
|
553
776
|
: measurement.start - this.options.scrollPaddingStart
|
|
554
777
|
|
|
555
|
-
this.
|
|
778
|
+
return [this.getOffsetForAlignment(toOffset, align), align] as const
|
|
556
779
|
}
|
|
557
780
|
|
|
558
|
-
|
|
559
|
-
(this.getMeasurements()[this.options.count - 1]?.end ||
|
|
560
|
-
this.options.paddingStart) + this.options.paddingEnd
|
|
781
|
+
private isDynamicMode = () => this.measureElementCache.size > 0
|
|
561
782
|
|
|
562
|
-
private
|
|
563
|
-
|
|
783
|
+
private cancelScrollToIndex = () => {
|
|
784
|
+
if (this.scrollToIndexTimeoutId !== null) {
|
|
785
|
+
clearTimeout(this.scrollToIndexTimeoutId)
|
|
786
|
+
this.scrollToIndexTimeoutId = null
|
|
787
|
+
}
|
|
788
|
+
}
|
|
564
789
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
)
|
|
790
|
+
scrollToOffset = (
|
|
791
|
+
toOffset: number,
|
|
792
|
+
{ align = 'start', behavior }: ScrollToOffsetOptions = {},
|
|
793
|
+
) => {
|
|
794
|
+
this.cancelScrollToIndex()
|
|
571
795
|
|
|
572
|
-
|
|
796
|
+
if (behavior === 'smooth' && this.isDynamicMode()) {
|
|
797
|
+
console.warn(
|
|
798
|
+
'The `smooth` scroll behavior is not fully supported with dynamic size.',
|
|
799
|
+
)
|
|
800
|
+
}
|
|
573
801
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
802
|
+
this._scrollToOffset(this.getOffsetForAlignment(toOffset, align), {
|
|
803
|
+
adjustments: undefined,
|
|
804
|
+
behavior,
|
|
805
|
+
})
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
scrollToIndex = (
|
|
809
|
+
index: number,
|
|
810
|
+
{ align: initialAlign = 'auto', behavior }: ScrollToIndexOptions = {},
|
|
811
|
+
) => {
|
|
812
|
+
index = Math.max(0, Math.min(index, this.options.count - 1))
|
|
813
|
+
|
|
814
|
+
this.cancelScrollToIndex()
|
|
815
|
+
|
|
816
|
+
if (behavior === 'smooth' && this.isDynamicMode()) {
|
|
817
|
+
console.warn(
|
|
818
|
+
'The `smooth` scroll behavior is not fully supported with dynamic size.',
|
|
819
|
+
)
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const [toOffset, align] = this.getOffsetForIndex(index, initialAlign)
|
|
823
|
+
|
|
824
|
+
this._scrollToOffset(toOffset, { adjustments: undefined, behavior })
|
|
580
825
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
826
|
+
if (behavior !== 'smooth' && this.isDynamicMode()) {
|
|
827
|
+
this.scrollToIndexTimeoutId = setTimeout(() => {
|
|
828
|
+
this.scrollToIndexTimeoutId = null
|
|
829
|
+
|
|
830
|
+
const elementInDOM = this.measureElementCache.has(
|
|
831
|
+
this.options.getItemKey(index),
|
|
832
|
+
)
|
|
833
|
+
|
|
834
|
+
if (elementInDOM) {
|
|
835
|
+
const [toOffset] = this.getOffsetForIndex(index, align)
|
|
836
|
+
|
|
837
|
+
if (!approxEqual(toOffset, this.scrollOffset)) {
|
|
838
|
+
this.scrollToIndex(index, { align, behavior })
|
|
839
|
+
}
|
|
840
|
+
} else {
|
|
841
|
+
this.scrollToIndex(index, { align, behavior })
|
|
584
842
|
}
|
|
585
|
-
|
|
586
|
-
check()
|
|
587
|
-
}, 100)
|
|
843
|
+
})
|
|
588
844
|
}
|
|
845
|
+
}
|
|
589
846
|
|
|
590
|
-
|
|
847
|
+
scrollBy = (delta: number, { behavior }: ScrollToOffsetOptions = {}) => {
|
|
848
|
+
this.cancelScrollToIndex()
|
|
849
|
+
|
|
850
|
+
if (behavior === 'smooth' && this.isDynamicMode()) {
|
|
851
|
+
console.warn(
|
|
852
|
+
'The `smooth` scroll behavior is not fully supported with dynamic size.',
|
|
853
|
+
)
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
this._scrollToOffset(this.scrollOffset + delta, {
|
|
857
|
+
adjustments: undefined,
|
|
858
|
+
behavior,
|
|
859
|
+
})
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
getTotalSize = () =>
|
|
863
|
+
(this.getMeasurements()[this.options.count - 1]?.end ||
|
|
864
|
+
this.options.paddingStart) -
|
|
865
|
+
this.options.scrollMargin +
|
|
866
|
+
this.options.paddingEnd
|
|
867
|
+
|
|
868
|
+
private _scrollToOffset = (
|
|
869
|
+
offset: number,
|
|
870
|
+
{
|
|
871
|
+
adjustments,
|
|
872
|
+
behavior,
|
|
873
|
+
}: {
|
|
874
|
+
adjustments: number | undefined
|
|
875
|
+
behavior: ScrollBehavior | undefined
|
|
876
|
+
},
|
|
877
|
+
) => {
|
|
878
|
+
this.options.scrollToFn(offset, { behavior, adjustments }, this)
|
|
591
879
|
}
|
|
592
880
|
|
|
593
881
|
measure = () => {
|
|
594
|
-
this.
|
|
595
|
-
this.notify()
|
|
882
|
+
this.itemSizeCache = new Map()
|
|
883
|
+
this.notify(false)
|
|
596
884
|
}
|
|
597
885
|
}
|
|
598
886
|
|
|
@@ -627,7 +915,7 @@ function calculateRange({
|
|
|
627
915
|
outerSize,
|
|
628
916
|
scrollOffset,
|
|
629
917
|
}: {
|
|
630
|
-
measurements:
|
|
918
|
+
measurements: VirtualItem[]
|
|
631
919
|
outerSize: number
|
|
632
920
|
scrollOffset: number
|
|
633
921
|
}) {
|