@tanstack/virtual-core 3.0.0-beta.8 → 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 +592 -454
- 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 +551 -244
- package/src/utils.ts +17 -5
- package/build/cjs/packages/virtual-core/src/index.js +0 -466
- 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 -560
- 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,152 +59,176 @@ export const defaultRangeExtractor = (range: Range) => {
|
|
|
58
59
|
return arr
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
export const observeElementRect = (
|
|
62
|
-
instance: Virtualizer<
|
|
62
|
+
export const observeElementRect = <T extends Element>(
|
|
63
|
+
instance: Virtualizer<T, any>,
|
|
63
64
|
cb: (rect: Rect) => void,
|
|
64
65
|
) => {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
if (!instance.scrollElement) {
|
|
66
|
+
const element = instance.scrollElement
|
|
67
|
+
if (!element) {
|
|
70
68
|
return
|
|
71
69
|
}
|
|
72
70
|
|
|
73
|
-
|
|
71
|
+
const handler = (rect: Rect) => {
|
|
72
|
+
const { width, height } = rect
|
|
73
|
+
cb({ width: Math.round(width), height: Math.round(height) })
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
handler(element.getBoundingClientRect())
|
|
74
77
|
|
|
75
|
-
observer
|
|
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
|
+
})
|
|
89
|
+
|
|
90
|
+
observer.observe(element, { box: 'border-box' })
|
|
76
91
|
|
|
77
92
|
return () => {
|
|
78
|
-
observer.unobserve()
|
|
93
|
+
observer.unobserve(element)
|
|
79
94
|
}
|
|
80
95
|
}
|
|
81
96
|
|
|
82
97
|
export const observeWindowRect = (
|
|
83
|
-
instance: Virtualizer<
|
|
98
|
+
instance: Virtualizer<Window, any>,
|
|
84
99
|
cb: (rect: Rect) => void,
|
|
85
100
|
) => {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
width: instance.scrollElement.innerWidth,
|
|
89
|
-
height: instance.scrollElement.innerHeight,
|
|
90
|
-
})
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (!instance.scrollElement) {
|
|
101
|
+
const element = instance.scrollElement
|
|
102
|
+
if (!element) {
|
|
94
103
|
return
|
|
95
104
|
}
|
|
96
105
|
|
|
97
|
-
|
|
106
|
+
const handler = () => {
|
|
107
|
+
cb({ width: element.innerWidth, height: element.innerHeight })
|
|
108
|
+
}
|
|
109
|
+
handler()
|
|
98
110
|
|
|
99
|
-
|
|
100
|
-
capture: false,
|
|
111
|
+
element.addEventListener('resize', handler, {
|
|
101
112
|
passive: true,
|
|
102
113
|
})
|
|
103
114
|
|
|
104
115
|
return () => {
|
|
105
|
-
|
|
116
|
+
element.removeEventListener('resize', handler)
|
|
106
117
|
}
|
|
107
118
|
}
|
|
108
119
|
|
|
109
|
-
export const observeElementOffset = (
|
|
110
|
-
instance: Virtualizer<
|
|
120
|
+
export const observeElementOffset = <T extends Element>(
|
|
121
|
+
instance: Virtualizer<T, any>,
|
|
111
122
|
cb: (offset: number) => void,
|
|
112
123
|
) => {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
instance.scrollElement[
|
|
116
|
-
instance.options.horizontal ? 'scrollLeft' : 'scrollTop'
|
|
117
|
-
],
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
if (!instance.scrollElement) {
|
|
124
|
+
const element = instance.scrollElement
|
|
125
|
+
if (!element) {
|
|
121
126
|
return
|
|
122
127
|
}
|
|
123
128
|
|
|
124
|
-
|
|
129
|
+
const handler = () => {
|
|
130
|
+
cb(element[instance.options.horizontal ? 'scrollLeft' : 'scrollTop'])
|
|
131
|
+
}
|
|
132
|
+
handler()
|
|
125
133
|
|
|
126
|
-
|
|
127
|
-
capture: false,
|
|
134
|
+
element.addEventListener('scroll', handler, {
|
|
128
135
|
passive: true,
|
|
129
136
|
})
|
|
130
137
|
|
|
131
138
|
return () => {
|
|
132
|
-
|
|
139
|
+
element.removeEventListener('scroll', handler)
|
|
133
140
|
}
|
|
134
141
|
}
|
|
135
142
|
|
|
136
143
|
export const observeWindowOffset = (
|
|
137
|
-
instance: Virtualizer<
|
|
144
|
+
instance: Virtualizer<Window, any>,
|
|
138
145
|
cb: (offset: number) => void,
|
|
139
146
|
) => {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
instance.scrollElement[
|
|
143
|
-
instance.options.horizontal ? 'scrollX' : 'scrollY'
|
|
144
|
-
],
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
if (!instance.scrollElement) {
|
|
147
|
+
const element = instance.scrollElement
|
|
148
|
+
if (!element) {
|
|
148
149
|
return
|
|
149
150
|
}
|
|
150
151
|
|
|
151
|
-
|
|
152
|
+
const handler = () => {
|
|
153
|
+
cb(element[instance.options.horizontal ? 'scrollX' : 'scrollY'])
|
|
154
|
+
}
|
|
155
|
+
handler()
|
|
152
156
|
|
|
153
|
-
|
|
154
|
-
capture: false,
|
|
157
|
+
element.addEventListener('scroll', handler, {
|
|
155
158
|
passive: true,
|
|
156
159
|
})
|
|
157
160
|
|
|
158
161
|
return () => {
|
|
159
|
-
|
|
162
|
+
element.removeEventListener('scroll', handler)
|
|
160
163
|
}
|
|
161
164
|
}
|
|
162
165
|
|
|
163
|
-
export const measureElement = (
|
|
164
|
-
element:
|
|
165
|
-
|
|
166
|
+
export const measureElement = <TItemElement extends Element>(
|
|
167
|
+
element: TItemElement,
|
|
168
|
+
entry: ResizeObserverEntry | undefined,
|
|
169
|
+
instance: Virtualizer<any, TItemElement>,
|
|
166
170
|
) => {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
+
)
|
|
170
185
|
}
|
|
171
186
|
|
|
172
|
-
export const windowScroll = (
|
|
187
|
+
export const windowScroll = <T extends Window>(
|
|
173
188
|
offset: number,
|
|
174
|
-
|
|
175
|
-
|
|
189
|
+
{
|
|
190
|
+
adjustments = 0,
|
|
191
|
+
behavior,
|
|
192
|
+
}: { adjustments?: number; behavior?: ScrollBehavior },
|
|
193
|
+
instance: Virtualizer<T, any>,
|
|
176
194
|
) => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
195
|
+
const toOffset = offset + adjustments
|
|
196
|
+
|
|
197
|
+
instance.scrollElement?.scrollTo?.({
|
|
198
|
+
[instance.options.horizontal ? 'left' : 'top']: toOffset,
|
|
199
|
+
behavior,
|
|
180
200
|
})
|
|
181
201
|
}
|
|
182
202
|
|
|
183
|
-
export const elementScroll = (
|
|
203
|
+
export const elementScroll = <T extends Element>(
|
|
184
204
|
offset: number,
|
|
185
|
-
|
|
186
|
-
|
|
205
|
+
{
|
|
206
|
+
adjustments = 0,
|
|
207
|
+
behavior,
|
|
208
|
+
}: { adjustments?: number; behavior?: ScrollBehavior },
|
|
209
|
+
instance: Virtualizer<T, any>,
|
|
187
210
|
) => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
211
|
+
const toOffset = offset + adjustments
|
|
212
|
+
|
|
213
|
+
instance.scrollElement?.scrollTo?.({
|
|
214
|
+
[instance.options.horizontal ? 'left' : 'top']: toOffset,
|
|
215
|
+
behavior,
|
|
191
216
|
})
|
|
192
217
|
}
|
|
193
218
|
|
|
194
219
|
export interface VirtualizerOptions<
|
|
195
|
-
TScrollElement
|
|
196
|
-
TItemElement
|
|
220
|
+
TScrollElement extends Element | Window,
|
|
221
|
+
TItemElement extends Element,
|
|
197
222
|
> {
|
|
198
223
|
// Required from the user
|
|
199
224
|
count: number
|
|
200
|
-
getScrollElement: () => TScrollElement
|
|
225
|
+
getScrollElement: () => TScrollElement | null
|
|
201
226
|
estimateSize: (index: number) => number
|
|
202
227
|
|
|
203
228
|
// Required from the framework adapter (but can be overridden)
|
|
204
229
|
scrollToFn: (
|
|
205
230
|
offset: number,
|
|
206
|
-
|
|
231
|
+
options: { adjustments?: number; behavior?: ScrollBehavior },
|
|
207
232
|
instance: Virtualizer<TScrollElement, TItemElement>,
|
|
208
233
|
) => void
|
|
209
234
|
observeElementRect: (
|
|
@@ -218,9 +243,13 @@ export interface VirtualizerOptions<
|
|
|
218
243
|
// Optional
|
|
219
244
|
debug?: any
|
|
220
245
|
initialRect?: Rect
|
|
221
|
-
onChange?: (
|
|
246
|
+
onChange?: (
|
|
247
|
+
instance: Virtualizer<TScrollElement, TItemElement>,
|
|
248
|
+
sync: boolean,
|
|
249
|
+
) => void
|
|
222
250
|
measureElement?: (
|
|
223
|
-
|
|
251
|
+
element: TItemElement,
|
|
252
|
+
entry: ResizeObserverEntry | undefined,
|
|
224
253
|
instance: Virtualizer<TScrollElement, TItemElement>,
|
|
225
254
|
) => number
|
|
226
255
|
overscan?: number
|
|
@@ -232,29 +261,67 @@ export interface VirtualizerOptions<
|
|
|
232
261
|
initialOffset?: number
|
|
233
262
|
getItemKey?: (index: number) => Key
|
|
234
263
|
rangeExtractor?: (range: Range) => number[]
|
|
235
|
-
|
|
264
|
+
scrollMargin?: number
|
|
265
|
+
scrollingDelay?: number
|
|
266
|
+
indexAttribute?: string
|
|
267
|
+
initialMeasurementsCache?: VirtualItem[]
|
|
268
|
+
lanes?: number
|
|
236
269
|
}
|
|
237
270
|
|
|
238
|
-
export class Virtualizer<
|
|
271
|
+
export class Virtualizer<
|
|
272
|
+
TScrollElement extends Element | Window,
|
|
273
|
+
TItemElement extends Element,
|
|
274
|
+
> {
|
|
239
275
|
private unsubs: (void | (() => void))[] = []
|
|
240
276
|
options!: Required<VirtualizerOptions<TScrollElement, TItemElement>>
|
|
241
277
|
scrollElement: TScrollElement | null = null
|
|
242
|
-
|
|
243
|
-
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>()
|
|
244
283
|
private pendingMeasuredCacheIndexes: number[] = []
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
private
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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
|
|
253
314
|
|
|
254
315
|
constructor(opts: VirtualizerOptions<TScrollElement, TItemElement>) {
|
|
255
316
|
this.setOptions(opts)
|
|
256
317
|
this.scrollRect = this.options.initialRect
|
|
257
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()
|
|
258
325
|
}
|
|
259
326
|
|
|
260
327
|
setOptions = (opts: VirtualizerOptions<TScrollElement, TItemElement>) => {
|
|
@@ -273,18 +340,46 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
273
340
|
horizontal: false,
|
|
274
341
|
getItemKey: defaultKeyExtractor,
|
|
275
342
|
rangeExtractor: defaultRangeExtractor,
|
|
276
|
-
enableSmoothScroll: true,
|
|
277
343
|
onChange: () => {},
|
|
278
344
|
measureElement,
|
|
279
345
|
initialRect: { width: 0, height: 0 },
|
|
346
|
+
scrollMargin: 0,
|
|
347
|
+
scrollingDelay: 150,
|
|
348
|
+
indexAttribute: 'data-index',
|
|
349
|
+
initialMeasurementsCache: [],
|
|
350
|
+
lanes: 1,
|
|
280
351
|
...opts,
|
|
281
352
|
}
|
|
282
353
|
}
|
|
283
354
|
|
|
284
|
-
private notify = () => {
|
|
285
|
-
this.options.onChange?.(this)
|
|
355
|
+
private notify = (sync: boolean) => {
|
|
356
|
+
this.options.onChange?.(this, sync)
|
|
286
357
|
}
|
|
287
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
|
+
|
|
288
383
|
private cleanup = () => {
|
|
289
384
|
this.unsubs.filter(Boolean).forEach((d) => d!())
|
|
290
385
|
this.unsubs = []
|
|
@@ -292,7 +387,9 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
292
387
|
}
|
|
293
388
|
|
|
294
389
|
_didMount = () => {
|
|
390
|
+
this.measureElementCache.forEach(this.observer.observe)
|
|
295
391
|
return () => {
|
|
392
|
+
this.observer.disconnect()
|
|
296
393
|
this.cleanup()
|
|
297
394
|
}
|
|
298
395
|
}
|
|
@@ -305,17 +402,45 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
305
402
|
|
|
306
403
|
this.scrollElement = scrollElement
|
|
307
404
|
|
|
405
|
+
this._scrollToOffset(this.scrollOffset, {
|
|
406
|
+
adjustments: undefined,
|
|
407
|
+
behavior: undefined,
|
|
408
|
+
})
|
|
409
|
+
|
|
308
410
|
this.unsubs.push(
|
|
309
411
|
this.options.observeElementRect(this, (rect) => {
|
|
310
412
|
this.scrollRect = rect
|
|
311
|
-
this.
|
|
413
|
+
this.maybeNotify()
|
|
312
414
|
}),
|
|
313
415
|
)
|
|
314
416
|
|
|
315
417
|
this.unsubs.push(
|
|
316
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'
|
|
317
433
|
this.scrollOffset = offset
|
|
318
|
-
|
|
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)
|
|
319
444
|
}),
|
|
320
445
|
)
|
|
321
446
|
}
|
|
@@ -325,14 +450,67 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
325
450
|
return this.scrollRect[this.options.horizontal ? 'width' : 'height']
|
|
326
451
|
}
|
|
327
452
|
|
|
328
|
-
private
|
|
453
|
+
private memoOptions = memo(
|
|
329
454
|
() => [
|
|
330
455
|
this.options.count,
|
|
331
456
|
this.options.paddingStart,
|
|
457
|
+
this.options.scrollMargin,
|
|
332
458
|
this.options.getItemKey,
|
|
333
|
-
this.itemMeasurementsCache,
|
|
334
459
|
],
|
|
335
|
-
(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) => {
|
|
336
514
|
const min =
|
|
337
515
|
this.pendingMeasuredCacheIndexes.length > 0
|
|
338
516
|
? Math.min(...this.pendingMeasuredCacheIndexes)
|
|
@@ -343,38 +521,62 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
343
521
|
|
|
344
522
|
for (let i = min; i < count; i++) {
|
|
345
523
|
const key = getItemKey(i)
|
|
346
|
-
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
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)
|
|
350
535
|
const size =
|
|
351
536
|
typeof measuredSize === 'number'
|
|
352
537
|
? measuredSize
|
|
353
538
|
: this.options.estimateSize(i)
|
|
539
|
+
|
|
354
540
|
const end = start + size
|
|
355
|
-
|
|
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
|
+
}
|
|
356
554
|
}
|
|
357
555
|
|
|
358
556
|
this.measurementsCache = measurements
|
|
557
|
+
|
|
359
558
|
return measurements
|
|
360
559
|
},
|
|
361
560
|
{
|
|
362
|
-
key: process.env.NODE_ENV
|
|
561
|
+
key: process.env.NODE_ENV !== 'production' && 'getMeasurements',
|
|
363
562
|
debug: () => this.options.debug,
|
|
364
563
|
},
|
|
365
564
|
)
|
|
366
565
|
|
|
367
|
-
|
|
566
|
+
calculateRange = memo(
|
|
368
567
|
() => [this.getMeasurements(), this.getSize(), this.scrollOffset],
|
|
369
568
|
(measurements, outerSize, scrollOffset) => {
|
|
370
|
-
return
|
|
371
|
-
measurements
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
569
|
+
return (this.range =
|
|
570
|
+
measurements.length > 0 && outerSize > 0
|
|
571
|
+
? calculateRange({
|
|
572
|
+
measurements,
|
|
573
|
+
outerSize,
|
|
574
|
+
scrollOffset,
|
|
575
|
+
})
|
|
576
|
+
: null)
|
|
375
577
|
},
|
|
376
578
|
{
|
|
377
|
-
key: process.env.NODE_ENV
|
|
579
|
+
key: process.env.NODE_ENV !== 'production' && 'calculateRange',
|
|
378
580
|
debug: () => this.options.debug,
|
|
379
581
|
},
|
|
380
582
|
)
|
|
@@ -387,144 +589,184 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
387
589
|
this.options.count,
|
|
388
590
|
],
|
|
389
591
|
(rangeExtractor, range, overscan, count) => {
|
|
390
|
-
return
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
592
|
+
return range === null
|
|
593
|
+
? []
|
|
594
|
+
: rangeExtractor({
|
|
595
|
+
...range,
|
|
596
|
+
overscan,
|
|
597
|
+
count,
|
|
598
|
+
})
|
|
395
599
|
},
|
|
396
600
|
{
|
|
397
|
-
key: process.env.NODE_ENV
|
|
601
|
+
key: process.env.NODE_ENV !== 'production' && 'getIndexes',
|
|
602
|
+
debug: () => this.options.debug,
|
|
398
603
|
},
|
|
399
604
|
)
|
|
400
605
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
this.getMeasurements(),
|
|
405
|
-
this.options.measureElement,
|
|
406
|
-
],
|
|
407
|
-
(indexes, measurements, measureElement) => {
|
|
408
|
-
const makeMeasureElement =
|
|
409
|
-
(index: number) => (measurableItem: TItemElement | null) => {
|
|
410
|
-
const item = this.measurementsCache[index]!
|
|
606
|
+
indexFromElement = (node: TItemElement) => {
|
|
607
|
+
const attributeName = this.options.indexAttribute
|
|
608
|
+
const indexStr = node.getAttribute(attributeName)
|
|
411
609
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
610
|
+
if (!indexStr) {
|
|
611
|
+
console.warn(
|
|
612
|
+
`Missing attribute name '${attributeName}={index}' on measured element.`,
|
|
613
|
+
)
|
|
614
|
+
return -1
|
|
615
|
+
}
|
|
415
616
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
this.scrollOffset + (measuredItemSize - itemSize),
|
|
431
|
-
false,
|
|
432
|
-
)
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
this.pendingMeasuredCacheIndexes.push(index)
|
|
437
|
-
this.itemMeasurementsCache = {
|
|
438
|
-
...this.itemMeasurementsCache,
|
|
439
|
-
[item.key]: measuredItemSize,
|
|
440
|
-
}
|
|
441
|
-
this.notify()
|
|
442
|
-
}
|
|
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)
|
|
443
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
|
+
}
|
|
444
650
|
|
|
445
|
-
|
|
651
|
+
resizeItem = (item: VirtualItem, size: number) => {
|
|
652
|
+
const itemSize = this.itemSizeCache.get(item.key) ?? item.size
|
|
653
|
+
const delta = size - itemSize
|
|
446
654
|
|
|
447
|
-
|
|
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)
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
this._scrollToOffset(this.scrollOffset, {
|
|
662
|
+
adjustments: (this.scrollAdjustments += delta),
|
|
663
|
+
behavior: undefined,
|
|
664
|
+
})
|
|
665
|
+
}
|
|
666
|
+
|
|
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[] = []
|
|
448
686
|
|
|
449
687
|
for (let k = 0, len = indexes.length; k < len; k++) {
|
|
450
688
|
const i = indexes[k]!
|
|
451
689
|
const measurement = measurements[i]!
|
|
452
690
|
|
|
453
|
-
|
|
454
|
-
...measurement,
|
|
455
|
-
measureElement: (currentMeasureElements[i] =
|
|
456
|
-
this.measureElementCache[i] ?? makeMeasureElement(i)),
|
|
457
|
-
}
|
|
458
|
-
virtualItems.push(item)
|
|
691
|
+
virtualItems.push(measurement)
|
|
459
692
|
}
|
|
460
693
|
|
|
461
|
-
this.measureElementCache = currentMeasureElements
|
|
462
|
-
|
|
463
694
|
return virtualItems
|
|
464
695
|
},
|
|
465
696
|
{
|
|
466
|
-
key: process.env.NODE_ENV
|
|
697
|
+
key: process.env.NODE_ENV !== 'production' && 'getIndexes',
|
|
698
|
+
debug: () => this.options.debug,
|
|
467
699
|
},
|
|
468
700
|
)
|
|
469
701
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
{ align }: ScrollToOffsetOptions = { align: 'start' },
|
|
473
|
-
) => {
|
|
474
|
-
const attempt = () => {
|
|
475
|
-
const offset = this.scrollOffset
|
|
476
|
-
const size = this.getSize()
|
|
477
|
-
|
|
478
|
-
if (align === 'auto') {
|
|
479
|
-
if (toOffset <= offset) {
|
|
480
|
-
align = 'start'
|
|
481
|
-
} else if (toOffset >= offset + size) {
|
|
482
|
-
align = 'end'
|
|
483
|
-
} else {
|
|
484
|
-
align = 'start'
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
if (align === 'start') {
|
|
489
|
-
this._scrollToOffset(toOffset, true)
|
|
490
|
-
} else if (align === 'end') {
|
|
491
|
-
this._scrollToOffset(toOffset - size, true)
|
|
492
|
-
} else if (align === 'center') {
|
|
493
|
-
this._scrollToOffset(toOffset - size / 2, true)
|
|
494
|
-
}
|
|
495
|
-
}
|
|
702
|
+
getVirtualItemForOffset = (offset: number) => {
|
|
703
|
+
const measurements = this.getMeasurements()
|
|
496
704
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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
|
+
)
|
|
501
715
|
}
|
|
502
716
|
|
|
503
|
-
|
|
504
|
-
index: number,
|
|
505
|
-
{ align, ...rest }: ScrollToIndexOptions = { align: 'auto' },
|
|
506
|
-
) => {
|
|
507
|
-
const measurements = this.getMeasurements()
|
|
508
|
-
const offset = this.scrollOffset
|
|
717
|
+
getOffsetForAlignment = (toOffset: number, align: ScrollAlignment) => {
|
|
509
718
|
const size = this.getSize()
|
|
510
|
-
const { count } = this.options
|
|
511
719
|
|
|
512
|
-
|
|
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
|
+
}
|
|
513
729
|
|
|
514
|
-
if (
|
|
515
|
-
|
|
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
|
|
516
736
|
}
|
|
517
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
|
+
|
|
518
757
|
if (align === 'auto') {
|
|
519
|
-
if (
|
|
758
|
+
if (
|
|
759
|
+
measurement.end >=
|
|
760
|
+
this.scrollOffset + this.getSize() - this.options.scrollPaddingEnd
|
|
761
|
+
) {
|
|
520
762
|
align = 'end'
|
|
521
763
|
} else if (
|
|
522
764
|
measurement.start <=
|
|
523
|
-
|
|
765
|
+
this.scrollOffset + this.options.scrollPaddingStart
|
|
524
766
|
) {
|
|
525
767
|
align = 'start'
|
|
526
768
|
} else {
|
|
527
|
-
return
|
|
769
|
+
return [this.scrollOffset, align] as const
|
|
528
770
|
}
|
|
529
771
|
}
|
|
530
772
|
|
|
@@ -533,47 +775,112 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
533
775
|
? measurement.end + this.options.scrollPaddingEnd
|
|
534
776
|
: measurement.start - this.options.scrollPaddingStart
|
|
535
777
|
|
|
536
|
-
this.
|
|
778
|
+
return [this.getOffsetForAlignment(toOffset, align), align] as const
|
|
537
779
|
}
|
|
538
780
|
|
|
539
|
-
|
|
540
|
-
(this.getMeasurements()[this.options.count - 1]?.end ||
|
|
541
|
-
this.options.paddingStart) + this.options.paddingEnd
|
|
781
|
+
private isDynamicMode = () => this.measureElementCache.size > 0
|
|
542
782
|
|
|
543
|
-
private
|
|
544
|
-
|
|
783
|
+
private cancelScrollToIndex = () => {
|
|
784
|
+
if (this.scrollToIndexTimeoutId !== null) {
|
|
785
|
+
clearTimeout(this.scrollToIndexTimeoutId)
|
|
786
|
+
this.scrollToIndexTimeoutId = null
|
|
787
|
+
}
|
|
788
|
+
}
|
|
545
789
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
)
|
|
790
|
+
scrollToOffset = (
|
|
791
|
+
toOffset: number,
|
|
792
|
+
{ align = 'start', behavior }: ScrollToOffsetOptions = {},
|
|
793
|
+
) => {
|
|
794
|
+
this.cancelScrollToIndex()
|
|
552
795
|
|
|
553
|
-
|
|
796
|
+
if (behavior === 'smooth' && this.isDynamicMode()) {
|
|
797
|
+
console.warn(
|
|
798
|
+
'The `smooth` scroll behavior is not fully supported with dynamic size.',
|
|
799
|
+
)
|
|
800
|
+
}
|
|
554
801
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
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()
|
|
561
815
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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 })
|
|
825
|
+
|
|
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 })
|
|
565
842
|
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
843
|
+
})
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
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
|
+
)
|
|
569
854
|
}
|
|
570
855
|
|
|
571
|
-
|
|
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)
|
|
572
879
|
}
|
|
573
880
|
|
|
574
881
|
measure = () => {
|
|
575
|
-
this.
|
|
576
|
-
this.notify()
|
|
882
|
+
this.itemSizeCache = new Map()
|
|
883
|
+
this.notify(false)
|
|
577
884
|
}
|
|
578
885
|
}
|
|
579
886
|
|
|
@@ -608,7 +915,7 @@ function calculateRange({
|
|
|
608
915
|
outerSize,
|
|
609
916
|
scrollOffset,
|
|
610
917
|
}: {
|
|
611
|
-
measurements:
|
|
918
|
+
measurements: VirtualItem[]
|
|
612
919
|
outerSize: number
|
|
613
920
|
scrollOffset: number
|
|
614
921
|
}) {
|