@tanstack/virtual-core 3.0.0-beta.6 → 3.0.0-beta.61
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/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 +652 -0
- package/build/lib/index.esm.js.map +1 -0
- package/build/lib/index.js +667 -0
- package/build/lib/index.js.map +1 -0
- package/build/lib/index.mjs +652 -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 +604 -452
- 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 +15 -13
- package/src/index.ts +546 -233
- package/src/utils.ts +17 -5
- package/build/cjs/packages/virtual-core/src/index.js +0 -465
- 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 -559
- 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
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
type ScrollBehavior = 'auto' | 'smooth'
|
|
12
|
+
|
|
13
|
+
export interface ScrollToOptions {
|
|
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: (
|
|
@@ -220,7 +245,8 @@ export interface VirtualizerOptions<
|
|
|
220
245
|
initialRect?: Rect
|
|
221
246
|
onChange?: (instance: Virtualizer<TScrollElement, TItemElement>) => void
|
|
222
247
|
measureElement?: (
|
|
223
|
-
|
|
248
|
+
element: TItemElement,
|
|
249
|
+
entry: ResizeObserverEntry | undefined,
|
|
224
250
|
instance: Virtualizer<TScrollElement, TItemElement>,
|
|
225
251
|
) => number
|
|
226
252
|
overscan?: number
|
|
@@ -232,29 +258,70 @@ export interface VirtualizerOptions<
|
|
|
232
258
|
initialOffset?: number
|
|
233
259
|
getItemKey?: (index: number) => Key
|
|
234
260
|
rangeExtractor?: (range: Range) => number[]
|
|
235
|
-
|
|
261
|
+
scrollMargin?: number
|
|
262
|
+
scrollingDelay?: number
|
|
263
|
+
indexAttribute?: string
|
|
264
|
+
initialMeasurementsCache?: VirtualItem[]
|
|
265
|
+
lanes?: number
|
|
236
266
|
}
|
|
237
267
|
|
|
238
|
-
export class Virtualizer<
|
|
268
|
+
export class Virtualizer<
|
|
269
|
+
TScrollElement extends Element | Window,
|
|
270
|
+
TItemElement extends Element,
|
|
271
|
+
> {
|
|
239
272
|
private unsubs: (void | (() => void))[] = []
|
|
240
273
|
options!: Required<VirtualizerOptions<TScrollElement, TItemElement>>
|
|
241
274
|
scrollElement: TScrollElement | null = null
|
|
242
|
-
|
|
243
|
-
private
|
|
275
|
+
isScrolling: boolean = false
|
|
276
|
+
private isScrollingTimeoutId: ReturnType<typeof setTimeout> | null = null
|
|
277
|
+
private scrollToIndexTimeoutId: ReturnType<typeof setTimeout> | null = null
|
|
278
|
+
measurementsCache: VirtualItem[] = []
|
|
279
|
+
private itemSizeCache = new Map<Key, number>()
|
|
244
280
|
private pendingMeasuredCacheIndexes: number[] = []
|
|
245
281
|
private scrollRect: Rect
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
private
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
282
|
+
scrollOffset: number
|
|
283
|
+
scrollDirection: ScrollDirection | null = null
|
|
284
|
+
private scrollAdjustments: number = 0
|
|
285
|
+
measureElementCache = new Map<Key, TItemElement>()
|
|
286
|
+
private observer = (() => {
|
|
287
|
+
let _ro: ResizeObserver | null = null
|
|
288
|
+
|
|
289
|
+
const get = () => {
|
|
290
|
+
if (_ro) {
|
|
291
|
+
return _ro
|
|
292
|
+
} else if (typeof ResizeObserver !== 'undefined') {
|
|
293
|
+
return (_ro = new ResizeObserver((entries) => {
|
|
294
|
+
entries.forEach((entry) => {
|
|
295
|
+
this._measureElement(entry.target as TItemElement, entry)
|
|
296
|
+
})
|
|
297
|
+
}))
|
|
298
|
+
} else {
|
|
299
|
+
return null
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
disconnect: () => get()?.disconnect(),
|
|
305
|
+
observe: (target: Element) =>
|
|
306
|
+
get()?.observe(target, { box: 'border-box' }),
|
|
307
|
+
unobserve: (target: Element) => get()?.unobserve(target),
|
|
308
|
+
}
|
|
309
|
+
})()
|
|
310
|
+
range: { startIndex: number; endIndex: number } = {
|
|
311
|
+
startIndex: 0,
|
|
312
|
+
endIndex: 0,
|
|
313
|
+
}
|
|
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,10 +340,14 @@ 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
|
}
|
|
@@ -288,10 +359,13 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
288
359
|
private cleanup = () => {
|
|
289
360
|
this.unsubs.filter(Boolean).forEach((d) => d!())
|
|
290
361
|
this.unsubs = []
|
|
362
|
+
this.scrollElement = null
|
|
291
363
|
}
|
|
292
364
|
|
|
293
365
|
_didMount = () => {
|
|
366
|
+
this.measureElementCache.forEach(this.observer.observe)
|
|
294
367
|
return () => {
|
|
368
|
+
this.observer.disconnect()
|
|
295
369
|
this.cleanup()
|
|
296
370
|
}
|
|
297
371
|
}
|
|
@@ -304,17 +378,52 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
304
378
|
|
|
305
379
|
this.scrollElement = scrollElement
|
|
306
380
|
|
|
381
|
+
this._scrollToOffset(this.scrollOffset, {
|
|
382
|
+
adjustments: undefined,
|
|
383
|
+
behavior: undefined,
|
|
384
|
+
})
|
|
385
|
+
|
|
307
386
|
this.unsubs.push(
|
|
308
387
|
this.options.observeElementRect(this, (rect) => {
|
|
388
|
+
const prev = this.scrollRect
|
|
309
389
|
this.scrollRect = rect
|
|
310
|
-
|
|
390
|
+
if (
|
|
391
|
+
this.options.horizontal
|
|
392
|
+
? rect.width !== prev.width
|
|
393
|
+
: rect.height !== prev.height
|
|
394
|
+
) {
|
|
395
|
+
this.maybeNotify()
|
|
396
|
+
}
|
|
311
397
|
}),
|
|
312
398
|
)
|
|
313
399
|
|
|
314
400
|
this.unsubs.push(
|
|
315
401
|
this.options.observeElementOffset(this, (offset) => {
|
|
402
|
+
this.scrollAdjustments = 0
|
|
403
|
+
|
|
404
|
+
if (this.scrollOffset === offset) {
|
|
405
|
+
return
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (this.isScrollingTimeoutId !== null) {
|
|
409
|
+
clearTimeout(this.isScrollingTimeoutId)
|
|
410
|
+
this.isScrollingTimeoutId = null
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
this.isScrolling = true
|
|
414
|
+
this.scrollDirection =
|
|
415
|
+
this.scrollOffset < offset ? 'forward' : 'backward'
|
|
316
416
|
this.scrollOffset = offset
|
|
317
|
-
|
|
417
|
+
|
|
418
|
+
this.maybeNotify()
|
|
419
|
+
|
|
420
|
+
this.isScrollingTimeoutId = setTimeout(() => {
|
|
421
|
+
this.isScrollingTimeoutId = null
|
|
422
|
+
this.isScrolling = false
|
|
423
|
+
this.scrollDirection = null
|
|
424
|
+
|
|
425
|
+
this.maybeNotify()
|
|
426
|
+
}, this.options.scrollingDelay)
|
|
318
427
|
}),
|
|
319
428
|
)
|
|
320
429
|
}
|
|
@@ -324,14 +433,67 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
324
433
|
return this.scrollRect[this.options.horizontal ? 'width' : 'height']
|
|
325
434
|
}
|
|
326
435
|
|
|
327
|
-
private
|
|
436
|
+
private memoOptions = memo(
|
|
328
437
|
() => [
|
|
329
438
|
this.options.count,
|
|
330
439
|
this.options.paddingStart,
|
|
440
|
+
this.options.scrollMargin,
|
|
331
441
|
this.options.getItemKey,
|
|
332
|
-
this.itemMeasurementsCache,
|
|
333
442
|
],
|
|
334
|
-
(count, paddingStart,
|
|
443
|
+
(count, paddingStart, scrollMargin, getItemKey) => {
|
|
444
|
+
this.pendingMeasuredCacheIndexes = []
|
|
445
|
+
return {
|
|
446
|
+
count,
|
|
447
|
+
paddingStart,
|
|
448
|
+
scrollMargin,
|
|
449
|
+
getItemKey,
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
key: false,
|
|
454
|
+
},
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
private getFurthestMeasurement = (
|
|
458
|
+
measurements: VirtualItem[],
|
|
459
|
+
index: number,
|
|
460
|
+
) => {
|
|
461
|
+
const furthestMeasurementsFound = new Map<number, true>()
|
|
462
|
+
const furthestMeasurements = new Map<number, VirtualItem>()
|
|
463
|
+
for (let m = index - 1; m >= 0; m--) {
|
|
464
|
+
const measurement = measurements[m]!
|
|
465
|
+
|
|
466
|
+
if (furthestMeasurementsFound.has(measurement.lane)) {
|
|
467
|
+
continue
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const previousFurthestMeasurement = furthestMeasurements.get(
|
|
471
|
+
measurement.lane,
|
|
472
|
+
)
|
|
473
|
+
if (
|
|
474
|
+
previousFurthestMeasurement == null ||
|
|
475
|
+
measurement.end > previousFurthestMeasurement.end
|
|
476
|
+
) {
|
|
477
|
+
furthestMeasurements.set(measurement.lane, measurement)
|
|
478
|
+
} else if (measurement.end < previousFurthestMeasurement.end) {
|
|
479
|
+
furthestMeasurementsFound.set(measurement.lane, true)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (furthestMeasurementsFound.size === this.options.lanes) {
|
|
483
|
+
break
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return furthestMeasurements.size === this.options.lanes
|
|
488
|
+
? Array.from(furthestMeasurements.values()).sort(
|
|
489
|
+
(a, b) => a.end - b.end,
|
|
490
|
+
)[0]
|
|
491
|
+
: undefined
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
private getMeasurements = memo(
|
|
495
|
+
() => [this.memoOptions(), this.itemSizeCache],
|
|
496
|
+
({ count, paddingStart, scrollMargin, getItemKey }, itemSizeCache) => {
|
|
335
497
|
const min =
|
|
336
498
|
this.pendingMeasuredCacheIndexes.length > 0
|
|
337
499
|
? Math.min(...this.pendingMeasuredCacheIndexes)
|
|
@@ -342,42 +504,83 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
342
504
|
|
|
343
505
|
for (let i = min; i < count; i++) {
|
|
344
506
|
const key = getItemKey(i)
|
|
345
|
-
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
507
|
+
|
|
508
|
+
const furthestMeasurement =
|
|
509
|
+
this.options.lanes === 1
|
|
510
|
+
? measurements[i - 1]
|
|
511
|
+
: this.getFurthestMeasurement(measurements, i)
|
|
512
|
+
|
|
513
|
+
const start = furthestMeasurement
|
|
514
|
+
? furthestMeasurement.end
|
|
515
|
+
: paddingStart + scrollMargin
|
|
516
|
+
|
|
517
|
+
const measuredSize = itemSizeCache.get(key)
|
|
349
518
|
const size =
|
|
350
519
|
typeof measuredSize === 'number'
|
|
351
520
|
? measuredSize
|
|
352
521
|
: this.options.estimateSize(i)
|
|
522
|
+
|
|
353
523
|
const end = start + size
|
|
354
|
-
|
|
524
|
+
|
|
525
|
+
const lane = furthestMeasurement
|
|
526
|
+
? furthestMeasurement.lane
|
|
527
|
+
: i % this.options.lanes
|
|
528
|
+
|
|
529
|
+
measurements[i] = {
|
|
530
|
+
index: i,
|
|
531
|
+
start,
|
|
532
|
+
size,
|
|
533
|
+
end,
|
|
534
|
+
key,
|
|
535
|
+
lane,
|
|
536
|
+
}
|
|
355
537
|
}
|
|
356
538
|
|
|
357
539
|
this.measurementsCache = measurements
|
|
540
|
+
|
|
358
541
|
return measurements
|
|
359
542
|
},
|
|
360
543
|
{
|
|
361
|
-
key: process.env.NODE_ENV
|
|
544
|
+
key: process.env.NODE_ENV !== 'production' && 'getMeasurements',
|
|
362
545
|
debug: () => this.options.debug,
|
|
363
546
|
},
|
|
364
547
|
)
|
|
365
548
|
|
|
366
|
-
|
|
549
|
+
calculateRange = memo(
|
|
367
550
|
() => [this.getMeasurements(), this.getSize(), this.scrollOffset],
|
|
368
551
|
(measurements, outerSize, scrollOffset) => {
|
|
369
|
-
return calculateRange({
|
|
552
|
+
return (this.range = calculateRange({
|
|
370
553
|
measurements,
|
|
371
554
|
outerSize,
|
|
372
555
|
scrollOffset,
|
|
373
|
-
})
|
|
556
|
+
}))
|
|
374
557
|
},
|
|
375
558
|
{
|
|
376
|
-
key: process.env.NODE_ENV
|
|
559
|
+
key: process.env.NODE_ENV !== 'production' && 'calculateRange',
|
|
377
560
|
debug: () => this.options.debug,
|
|
378
561
|
},
|
|
379
562
|
)
|
|
380
563
|
|
|
564
|
+
private maybeNotify = memo(
|
|
565
|
+
() => {
|
|
566
|
+
const range = this.calculateRange()
|
|
567
|
+
|
|
568
|
+
return [range.startIndex, range.endIndex, this.isScrolling]
|
|
569
|
+
},
|
|
570
|
+
() => {
|
|
571
|
+
this.notify()
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
key: process.env.NODE_ENV !== 'production' && 'maybeNotify',
|
|
575
|
+
debug: () => this.options.debug,
|
|
576
|
+
initialDeps: [
|
|
577
|
+
this.range.startIndex,
|
|
578
|
+
this.range.endIndex,
|
|
579
|
+
this.isScrolling,
|
|
580
|
+
],
|
|
581
|
+
},
|
|
582
|
+
)
|
|
583
|
+
|
|
381
584
|
private getIndexes = memo(
|
|
382
585
|
() => [
|
|
383
586
|
this.options.rangeExtractor,
|
|
@@ -389,141 +592,186 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
389
592
|
return rangeExtractor({
|
|
390
593
|
...range,
|
|
391
594
|
overscan,
|
|
392
|
-
count
|
|
595
|
+
count,
|
|
393
596
|
})
|
|
394
597
|
},
|
|
395
598
|
{
|
|
396
|
-
key: process.env.NODE_ENV
|
|
599
|
+
key: process.env.NODE_ENV !== 'production' && 'getIndexes',
|
|
600
|
+
debug: () => this.options.debug,
|
|
397
601
|
},
|
|
398
602
|
)
|
|
399
603
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
this.getMeasurements(),
|
|
404
|
-
this.options.measureElement,
|
|
405
|
-
],
|
|
406
|
-
(indexes, measurements, measureElement) => {
|
|
407
|
-
const makeMeasureElement =
|
|
408
|
-
(index: number) => (measurableItem: TItemElement | null) => {
|
|
409
|
-
const item = this.measurementsCache[index]!
|
|
604
|
+
indexFromElement = (node: TItemElement) => {
|
|
605
|
+
const attributeName = this.options.indexAttribute
|
|
606
|
+
const indexStr = node.getAttribute(attributeName)
|
|
410
607
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
608
|
+
if (!indexStr) {
|
|
609
|
+
console.warn(
|
|
610
|
+
`Missing attribute name '${attributeName}={index}' on measured element.`,
|
|
611
|
+
)
|
|
612
|
+
return -1
|
|
613
|
+
}
|
|
414
614
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
615
|
+
return parseInt(indexStr, 10)
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
private _measureElement = (
|
|
619
|
+
node: TItemElement,
|
|
620
|
+
entry: ResizeObserverEntry | undefined,
|
|
621
|
+
) => {
|
|
622
|
+
const item = this.measurementsCache[this.indexFromElement(node)]
|
|
623
|
+
if (!item) {
|
|
624
|
+
this.measureElementCache.forEach((cached, key) => {
|
|
625
|
+
if (cached === node) {
|
|
626
|
+
this.observer.unobserve(node)
|
|
627
|
+
this.measureElementCache.delete(key)
|
|
628
|
+
}
|
|
629
|
+
})
|
|
630
|
+
return
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const prevNode = this.measureElementCache.get(item.key)
|
|
634
|
+
|
|
635
|
+
if (!node.isConnected) {
|
|
636
|
+
if (prevNode) {
|
|
637
|
+
this.observer.unobserve(prevNode)
|
|
638
|
+
this.measureElementCache.delete(item.key)
|
|
639
|
+
}
|
|
640
|
+
return
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if (prevNode !== node) {
|
|
644
|
+
if (prevNode) {
|
|
645
|
+
this.observer.unobserve(prevNode)
|
|
646
|
+
}
|
|
647
|
+
this.observer.observe(node)
|
|
648
|
+
this.measureElementCache.set(item.key, node)
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const measuredItemSize = this.options.measureElement(node, entry, this)
|
|
652
|
+
|
|
653
|
+
this.resizeItem(item, measuredItemSize)
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
resizeItem = (item: VirtualItem, size: number) => {
|
|
657
|
+
const itemSize = this.itemSizeCache.get(item.key) ?? item.size
|
|
658
|
+
const delta = size - itemSize
|
|
659
|
+
|
|
660
|
+
if (delta !== 0) {
|
|
661
|
+
if (item.start < this.scrollOffset) {
|
|
662
|
+
if (process.env.NODE_ENV !== 'production' && this.options.debug) {
|
|
663
|
+
console.info('correction', delta)
|
|
442
664
|
}
|
|
443
665
|
|
|
444
|
-
|
|
666
|
+
this._scrollToOffset(this.scrollOffset, {
|
|
667
|
+
adjustments: (this.scrollAdjustments += delta),
|
|
668
|
+
behavior: undefined,
|
|
669
|
+
})
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
this.pendingMeasuredCacheIndexes.push(item.index)
|
|
673
|
+
this.itemSizeCache = new Map(this.itemSizeCache.set(item.key, size))
|
|
674
|
+
|
|
675
|
+
this.notify()
|
|
676
|
+
}
|
|
677
|
+
}
|
|
445
678
|
|
|
446
|
-
|
|
679
|
+
measureElement = (node: TItemElement | null) => {
|
|
680
|
+
if (!node) {
|
|
681
|
+
return
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
this._measureElement(node, undefined)
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
getVirtualItems = memo(
|
|
688
|
+
() => [this.getIndexes(), this.getMeasurements()],
|
|
689
|
+
(indexes, measurements) => {
|
|
690
|
+
const virtualItems: VirtualItem[] = []
|
|
447
691
|
|
|
448
692
|
for (let k = 0, len = indexes.length; k < len; k++) {
|
|
449
693
|
const i = indexes[k]!
|
|
450
694
|
const measurement = measurements[i]!
|
|
451
695
|
|
|
452
|
-
|
|
453
|
-
...measurement,
|
|
454
|
-
measureElement: (currentMeasureElements[i] =
|
|
455
|
-
this.measureElementCache[i] ?? makeMeasureElement(i)),
|
|
456
|
-
}
|
|
457
|
-
virtualItems.push(item)
|
|
696
|
+
virtualItems.push(measurement)
|
|
458
697
|
}
|
|
459
698
|
|
|
460
|
-
this.measureElementCache = currentMeasureElements
|
|
461
|
-
|
|
462
699
|
return virtualItems
|
|
463
700
|
},
|
|
464
701
|
{
|
|
465
|
-
key: process.env.NODE_ENV
|
|
702
|
+
key: process.env.NODE_ENV !== 'production' && 'getIndexes',
|
|
703
|
+
debug: () => this.options.debug,
|
|
466
704
|
},
|
|
467
705
|
)
|
|
468
706
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
{ align }: ScrollToOffsetOptions = { align: 'start' },
|
|
472
|
-
) => {
|
|
473
|
-
const attempt = () => {
|
|
474
|
-
const offset = this.scrollOffset
|
|
475
|
-
const size = this.getSize()
|
|
476
|
-
|
|
477
|
-
if (align === 'auto') {
|
|
478
|
-
if (toOffset <= offset) {
|
|
479
|
-
align = 'start'
|
|
480
|
-
} else if (toOffset >= offset + size) {
|
|
481
|
-
align = 'end'
|
|
482
|
-
} else {
|
|
483
|
-
align = 'start'
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
if (align === 'start') {
|
|
488
|
-
this._scrollToOffset(toOffset, true)
|
|
489
|
-
} else if (align === 'end') {
|
|
490
|
-
this._scrollToOffset(toOffset - size, true)
|
|
491
|
-
} else if (align === 'center') {
|
|
492
|
-
this._scrollToOffset(toOffset - size / 2, true)
|
|
493
|
-
}
|
|
494
|
-
}
|
|
707
|
+
getVirtualItemForOffset = (offset: number) => {
|
|
708
|
+
const measurements = this.getMeasurements()
|
|
495
709
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
710
|
+
return notUndefined(
|
|
711
|
+
measurements[
|
|
712
|
+
findNearestBinarySearch(
|
|
713
|
+
0,
|
|
714
|
+
measurements.length - 1,
|
|
715
|
+
(index: number) => notUndefined(measurements[index]).start,
|
|
716
|
+
offset,
|
|
717
|
+
)
|
|
718
|
+
],
|
|
719
|
+
)
|
|
500
720
|
}
|
|
501
721
|
|
|
502
|
-
|
|
503
|
-
index: number,
|
|
504
|
-
{ align, ...rest }: ScrollToIndexOptions = { align: 'auto' },
|
|
505
|
-
) => {
|
|
506
|
-
const measurements = this.getMeasurements()
|
|
507
|
-
const offset = this.scrollOffset
|
|
722
|
+
getOffsetForAlignment = (toOffset: number, align: ScrollAlignment) => {
|
|
508
723
|
const size = this.getSize()
|
|
509
|
-
const { count } = this.options
|
|
510
724
|
|
|
511
|
-
|
|
725
|
+
if (align === 'auto') {
|
|
726
|
+
if (toOffset <= this.scrollOffset) {
|
|
727
|
+
align = 'start'
|
|
728
|
+
} else if (toOffset >= this.scrollOffset + size) {
|
|
729
|
+
align = 'end'
|
|
730
|
+
} else {
|
|
731
|
+
align = 'start'
|
|
732
|
+
}
|
|
733
|
+
}
|
|
512
734
|
|
|
513
|
-
if (
|
|
514
|
-
|
|
735
|
+
if (align === 'start') {
|
|
736
|
+
toOffset = toOffset
|
|
737
|
+
} else if (align === 'end') {
|
|
738
|
+
toOffset = toOffset - size
|
|
739
|
+
} else if (align === 'center') {
|
|
740
|
+
toOffset = toOffset - size / 2
|
|
515
741
|
}
|
|
516
742
|
|
|
743
|
+
const scrollSizeProp = this.options.horizontal
|
|
744
|
+
? 'scrollWidth'
|
|
745
|
+
: 'scrollHeight'
|
|
746
|
+
const scrollSize = this.scrollElement
|
|
747
|
+
? 'document' in this.scrollElement
|
|
748
|
+
? this.scrollElement.document.documentElement[scrollSizeProp]
|
|
749
|
+
: this.scrollElement[scrollSizeProp]
|
|
750
|
+
: 0
|
|
751
|
+
|
|
752
|
+
const maxOffset = scrollSize - this.getSize()
|
|
753
|
+
|
|
754
|
+
return Math.max(Math.min(maxOffset, toOffset), 0)
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
getOffsetForIndex = (index: number, align: ScrollAlignment = 'auto') => {
|
|
758
|
+
index = Math.max(0, Math.min(index, this.options.count - 1))
|
|
759
|
+
|
|
760
|
+
const measurement = notUndefined(this.getMeasurements()[index])
|
|
761
|
+
|
|
517
762
|
if (align === 'auto') {
|
|
518
|
-
if (
|
|
763
|
+
if (
|
|
764
|
+
measurement.end >=
|
|
765
|
+
this.scrollOffset + this.getSize() - this.options.scrollPaddingEnd
|
|
766
|
+
) {
|
|
519
767
|
align = 'end'
|
|
520
768
|
} else if (
|
|
521
769
|
measurement.start <=
|
|
522
|
-
|
|
770
|
+
this.scrollOffset + this.options.scrollPaddingStart
|
|
523
771
|
) {
|
|
524
772
|
align = 'start'
|
|
525
773
|
} else {
|
|
526
|
-
return
|
|
774
|
+
return [this.scrollOffset, align] as const
|
|
527
775
|
}
|
|
528
776
|
}
|
|
529
777
|
|
|
@@ -532,46 +780,111 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
532
780
|
? measurement.end + this.options.scrollPaddingEnd
|
|
533
781
|
: measurement.start - this.options.scrollPaddingStart
|
|
534
782
|
|
|
535
|
-
this.
|
|
783
|
+
return [this.getOffsetForAlignment(toOffset, align), align] as const
|
|
536
784
|
}
|
|
537
785
|
|
|
538
|
-
|
|
539
|
-
(this.getMeasurements()[this.options.count - 1]?.end ||
|
|
540
|
-
this.options.paddingStart) + this.options.paddingEnd
|
|
786
|
+
private isDynamicMode = () => this.measureElementCache.size > 0
|
|
541
787
|
|
|
542
|
-
private
|
|
543
|
-
|
|
788
|
+
private cancelScrollToIndex = () => {
|
|
789
|
+
if (this.scrollToIndexTimeoutId !== null) {
|
|
790
|
+
clearTimeout(this.scrollToIndexTimeoutId)
|
|
791
|
+
this.scrollToIndexTimeoutId = null
|
|
792
|
+
}
|
|
793
|
+
}
|
|
544
794
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
)
|
|
795
|
+
scrollToOffset = (
|
|
796
|
+
toOffset: number,
|
|
797
|
+
{ align = 'start', behavior }: ScrollToOffsetOptions = {},
|
|
798
|
+
) => {
|
|
799
|
+
this.cancelScrollToIndex()
|
|
551
800
|
|
|
552
|
-
|
|
801
|
+
if (behavior === 'smooth' && this.isDynamicMode()) {
|
|
802
|
+
console.warn(
|
|
803
|
+
'The `smooth` scroll behavior is not fully supported with dynamic size.',
|
|
804
|
+
)
|
|
805
|
+
}
|
|
553
806
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
807
|
+
this._scrollToOffset(this.getOffsetForAlignment(toOffset, align), {
|
|
808
|
+
adjustments: undefined,
|
|
809
|
+
behavior,
|
|
810
|
+
})
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
scrollToIndex = (
|
|
814
|
+
index: number,
|
|
815
|
+
{ align: initialAlign = 'auto', behavior }: ScrollToIndexOptions = {},
|
|
816
|
+
) => {
|
|
817
|
+
index = Math.max(0, Math.min(index, this.options.count - 1))
|
|
818
|
+
|
|
819
|
+
this.cancelScrollToIndex()
|
|
560
820
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
821
|
+
if (behavior === 'smooth' && this.isDynamicMode()) {
|
|
822
|
+
console.warn(
|
|
823
|
+
'The `smooth` scroll behavior is not fully supported with dynamic size.',
|
|
824
|
+
)
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
const [toOffset, align] = this.getOffsetForIndex(index, initialAlign)
|
|
828
|
+
|
|
829
|
+
this._scrollToOffset(toOffset, { adjustments: undefined, behavior })
|
|
830
|
+
|
|
831
|
+
if (behavior !== 'smooth' && this.isDynamicMode()) {
|
|
832
|
+
this.scrollToIndexTimeoutId = setTimeout(() => {
|
|
833
|
+
this.scrollToIndexTimeoutId = null
|
|
834
|
+
|
|
835
|
+
const elementInDOM = this.measureElementCache.has(
|
|
836
|
+
this.options.getItemKey(index),
|
|
837
|
+
)
|
|
838
|
+
|
|
839
|
+
if (elementInDOM) {
|
|
840
|
+
const [toOffset] = this.getOffsetForIndex(index, align)
|
|
841
|
+
|
|
842
|
+
if (!approxEqual(toOffset, this.scrollOffset)) {
|
|
843
|
+
this.scrollToIndex(index, { align, behavior })
|
|
844
|
+
}
|
|
845
|
+
} else {
|
|
846
|
+
this.scrollToIndex(index, { align, behavior })
|
|
564
847
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
848
|
+
})
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
scrollBy = (delta: number, { behavior }: ScrollToOffsetOptions = {}) => {
|
|
853
|
+
this.cancelScrollToIndex()
|
|
854
|
+
|
|
855
|
+
if (behavior === 'smooth' && this.isDynamicMode()) {
|
|
856
|
+
console.warn(
|
|
857
|
+
'The `smooth` scroll behavior is not fully supported with dynamic size.',
|
|
858
|
+
)
|
|
568
859
|
}
|
|
569
860
|
|
|
570
|
-
|
|
861
|
+
this._scrollToOffset(this.scrollOffset + delta, {
|
|
862
|
+
adjustments: undefined,
|
|
863
|
+
behavior,
|
|
864
|
+
})
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
getTotalSize = () =>
|
|
868
|
+
(this.getMeasurements()[this.options.count - 1]?.end ||
|
|
869
|
+
this.options.paddingStart) -
|
|
870
|
+
this.options.scrollMargin +
|
|
871
|
+
this.options.paddingEnd
|
|
872
|
+
|
|
873
|
+
private _scrollToOffset = (
|
|
874
|
+
offset: number,
|
|
875
|
+
{
|
|
876
|
+
adjustments,
|
|
877
|
+
behavior,
|
|
878
|
+
}: {
|
|
879
|
+
adjustments: number | undefined
|
|
880
|
+
behavior: ScrollBehavior | undefined
|
|
881
|
+
},
|
|
882
|
+
) => {
|
|
883
|
+
this.options.scrollToFn(offset, { behavior, adjustments }, this)
|
|
571
884
|
}
|
|
572
885
|
|
|
573
886
|
measure = () => {
|
|
574
|
-
this.
|
|
887
|
+
this.itemSizeCache = new Map()
|
|
575
888
|
this.notify()
|
|
576
889
|
}
|
|
577
890
|
}
|
|
@@ -607,7 +920,7 @@ function calculateRange({
|
|
|
607
920
|
outerSize,
|
|
608
921
|
scrollOffset,
|
|
609
922
|
}: {
|
|
610
|
-
measurements:
|
|
923
|
+
measurements: VirtualItem[]
|
|
611
924
|
outerSize: number
|
|
612
925
|
scrollOffset: number
|
|
613
926
|
}) {
|