@tanstack/virtual-core 3.0.0-beta.44 → 3.0.0-beta.47
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/index.d.ts +8 -8
- package/build/lib/index.esm.js +117 -113
- package/build/lib/index.esm.js.map +1 -1
- package/build/lib/index.js +117 -113
- package/build/lib/index.js.map +1 -1
- package/build/lib/index.mjs +117 -113
- package/build/lib/index.mjs.map +1 -1
- package/build/umd/index.development.js +117 -113
- 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 +1 -1
- package/src/index.ts +123 -133
package/src/index.ts
CHANGED
|
@@ -58,141 +58,124 @@ export const defaultRangeExtractor = (range: Range) => {
|
|
|
58
58
|
return arr
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
const
|
|
62
|
-
instance: Virtualizer<
|
|
61
|
+
export const observeElementRect = <T extends Element>(
|
|
62
|
+
instance: Virtualizer<T, any>,
|
|
63
63
|
cb: (rect: Rect) => void,
|
|
64
64
|
) => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
instance.options.horizontal
|
|
70
|
-
? rect.width !== prev.width
|
|
71
|
-
: rect.height !== prev.height
|
|
72
|
-
) {
|
|
73
|
-
cb(rect)
|
|
74
|
-
}
|
|
65
|
+
const element = instance.scrollElement
|
|
66
|
+
if (!element) {
|
|
67
|
+
return
|
|
68
|
+
}
|
|
75
69
|
|
|
76
|
-
|
|
70
|
+
const handler = (rect: { width: number; height: number }) => {
|
|
71
|
+
const { width, height } = rect
|
|
72
|
+
cb({ width: Math.round(width), height: Math.round(height) })
|
|
77
73
|
}
|
|
78
|
-
}
|
|
79
74
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
cb: (rect: Rect) => void,
|
|
83
|
-
) => {
|
|
75
|
+
handler(element.getBoundingClientRect())
|
|
76
|
+
|
|
84
77
|
const observer = new ResizeObserver((entries) => {
|
|
85
78
|
const entry = entries[0]
|
|
86
79
|
if (entry) {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
width:
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
} else {
|
|
93
|
-
cb({ width: 0, height: 0 })
|
|
80
|
+
const box = entry.borderBoxSize[0]
|
|
81
|
+
if (box) {
|
|
82
|
+
handler({ width: box.inlineSize, height: box.blockSize })
|
|
83
|
+
return
|
|
84
|
+
}
|
|
94
85
|
}
|
|
86
|
+
handler(element.getBoundingClientRect())
|
|
95
87
|
})
|
|
96
88
|
|
|
97
|
-
|
|
98
|
-
return
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
cb(instance.scrollElement.getBoundingClientRect())
|
|
102
|
-
|
|
103
|
-
observer.observe(instance.scrollElement)
|
|
89
|
+
observer.observe(element, { box: 'border-box' })
|
|
104
90
|
|
|
105
91
|
return () => {
|
|
106
|
-
observer.unobserve(
|
|
92
|
+
observer.unobserve(element)
|
|
107
93
|
}
|
|
108
94
|
}
|
|
109
95
|
|
|
110
96
|
export const observeWindowRect = (
|
|
111
|
-
instance: Virtualizer<
|
|
97
|
+
instance: Virtualizer<Window, any>,
|
|
112
98
|
cb: (rect: Rect) => void,
|
|
113
99
|
) => {
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
memoizedCallback({
|
|
117
|
-
width: instance.scrollElement.innerWidth,
|
|
118
|
-
height: instance.scrollElement.innerHeight,
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
if (!instance.scrollElement) {
|
|
100
|
+
const element = instance.scrollElement
|
|
101
|
+
if (!element) {
|
|
122
102
|
return
|
|
123
103
|
}
|
|
124
104
|
|
|
125
|
-
|
|
105
|
+
const handler = () => {
|
|
106
|
+
cb({ width: element.innerWidth, height: element.innerHeight })
|
|
107
|
+
}
|
|
108
|
+
handler()
|
|
126
109
|
|
|
127
|
-
|
|
128
|
-
capture: false,
|
|
110
|
+
element.addEventListener('resize', handler, {
|
|
129
111
|
passive: true,
|
|
130
112
|
})
|
|
131
113
|
|
|
132
114
|
return () => {
|
|
133
|
-
|
|
115
|
+
element.removeEventListener('resize', handler)
|
|
134
116
|
}
|
|
135
117
|
}
|
|
136
118
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return (instance: Virtualizer<any, any>, cb: (offset: number) => void) => {
|
|
146
|
-
if (!instance.scrollElement) {
|
|
147
|
-
return
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const propX = scrollProps[mode][0]
|
|
151
|
-
const propY = scrollProps[mode][1]
|
|
152
|
-
|
|
153
|
-
let prevX: number = instance.scrollElement[propX]
|
|
154
|
-
let prevY: number = instance.scrollElement[propY]
|
|
155
|
-
|
|
156
|
-
const scroll = () => {
|
|
157
|
-
const offset =
|
|
158
|
-
instance.scrollElement[instance.options.horizontal ? propX : propY]
|
|
119
|
+
export const observeElementOffset = <T extends Element>(
|
|
120
|
+
instance: Virtualizer<T, any>,
|
|
121
|
+
cb: (offset: number) => void,
|
|
122
|
+
) => {
|
|
123
|
+
const element = instance.scrollElement
|
|
124
|
+
if (!element) {
|
|
125
|
+
return
|
|
126
|
+
}
|
|
159
127
|
|
|
160
|
-
|
|
161
|
-
|
|
128
|
+
const handler = () => {
|
|
129
|
+
cb(element[instance.options.horizontal ? 'scrollLeft' : 'scrollTop'])
|
|
130
|
+
}
|
|
131
|
+
handler()
|
|
162
132
|
|
|
163
|
-
|
|
133
|
+
element.addEventListener('scroll', handler, {
|
|
134
|
+
passive: true,
|
|
135
|
+
})
|
|
164
136
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
137
|
+
return () => {
|
|
138
|
+
element.removeEventListener('scroll', handler)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
169
141
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
142
|
+
export const observeWindowOffset = (
|
|
143
|
+
instance: Virtualizer<Window, any>,
|
|
144
|
+
cb: (offset: number) => void,
|
|
145
|
+
) => {
|
|
146
|
+
const element = instance.scrollElement
|
|
147
|
+
if (!element) {
|
|
148
|
+
return
|
|
149
|
+
}
|
|
173
150
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
151
|
+
const handler = () => {
|
|
152
|
+
cb(element[instance.options.horizontal ? 'scrollX' : 'scrollY'])
|
|
153
|
+
}
|
|
154
|
+
handler()
|
|
177
155
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
})
|
|
156
|
+
element.addEventListener('scroll', handler, {
|
|
157
|
+
passive: true,
|
|
158
|
+
})
|
|
182
159
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
160
|
+
return () => {
|
|
161
|
+
element.removeEventListener('scroll', handler)
|
|
186
162
|
}
|
|
187
163
|
}
|
|
188
164
|
|
|
189
|
-
export const observeElementOffset = createOffsetObserver('element')
|
|
190
|
-
export const observeWindowOffset = createOffsetObserver('window')
|
|
191
|
-
|
|
192
165
|
export const measureElement = <TItemElement extends Element>(
|
|
193
166
|
element: TItemElement,
|
|
167
|
+
entry: ResizeObserverEntry | undefined,
|
|
194
168
|
instance: Virtualizer<any, TItemElement>,
|
|
195
169
|
) => {
|
|
170
|
+
if (entry) {
|
|
171
|
+
const box = entry.borderBoxSize[0]
|
|
172
|
+
if (box) {
|
|
173
|
+
const size = Math.round(
|
|
174
|
+
box[instance.options.horizontal ? 'inlineSize' : 'blockSize'],
|
|
175
|
+
)
|
|
176
|
+
return size
|
|
177
|
+
}
|
|
178
|
+
}
|
|
196
179
|
return Math.round(
|
|
197
180
|
element.getBoundingClientRect()[
|
|
198
181
|
instance.options.horizontal ? 'width' : 'height'
|
|
@@ -261,7 +244,8 @@ export interface VirtualizerOptions<
|
|
|
261
244
|
initialRect?: Rect
|
|
262
245
|
onChange?: (instance: Virtualizer<TScrollElement, TItemElement>) => void
|
|
263
246
|
measureElement?: (
|
|
264
|
-
|
|
247
|
+
element: TItemElement,
|
|
248
|
+
entry: ResizeObserverEntry | undefined,
|
|
265
249
|
instance: Virtualizer<TScrollElement, TItemElement>,
|
|
266
250
|
) => number
|
|
267
251
|
overscan?: number
|
|
@@ -290,32 +274,36 @@ export class Virtualizer<
|
|
|
290
274
|
private isScrollingTimeoutId: ReturnType<typeof setTimeout> | null = null
|
|
291
275
|
private scrollToIndexTimeoutId: ReturnType<typeof setTimeout> | null = null
|
|
292
276
|
measurementsCache: VirtualItem[] = []
|
|
293
|
-
private itemSizeCache
|
|
277
|
+
private itemSizeCache = new Map<Key, number>()
|
|
294
278
|
private pendingMeasuredCacheIndexes: number[] = []
|
|
295
279
|
private scrollRect: Rect
|
|
296
280
|
scrollOffset: number
|
|
297
281
|
scrollDirection: ScrollDirection | null = null
|
|
298
282
|
private scrollAdjustments: number = 0
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
TItemElement & { __virtualizerSkipFirstNotSync?: boolean }
|
|
302
|
-
> = {}
|
|
303
|
-
private getResizeObserver = (() => {
|
|
283
|
+
measureElementCache = new Map<Key, TItemElement>()
|
|
284
|
+
private observer = (() => {
|
|
304
285
|
let _ro: ResizeObserver | null = null
|
|
305
286
|
|
|
306
|
-
|
|
287
|
+
const get = () => {
|
|
307
288
|
if (_ro) {
|
|
308
289
|
return _ro
|
|
309
290
|
} else if (typeof ResizeObserver !== 'undefined') {
|
|
310
291
|
return (_ro = new ResizeObserver((entries) => {
|
|
311
292
|
entries.forEach((entry) => {
|
|
312
|
-
this._measureElement(entry.target as TItemElement,
|
|
293
|
+
this._measureElement(entry.target as TItemElement, entry)
|
|
313
294
|
})
|
|
314
295
|
}))
|
|
315
296
|
} else {
|
|
316
297
|
return null
|
|
317
298
|
}
|
|
318
299
|
}
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
disconnect: () => get()?.disconnect(),
|
|
303
|
+
observe: (target: Element) =>
|
|
304
|
+
get()?.observe(target, { box: 'border-box' }),
|
|
305
|
+
unobserve: (target: Element) => get()?.unobserve(target),
|
|
306
|
+
}
|
|
319
307
|
})()
|
|
320
308
|
range: { startIndex: number; endIndex: number } = {
|
|
321
309
|
startIndex: 0,
|
|
@@ -328,7 +316,7 @@ export class Virtualizer<
|
|
|
328
316
|
this.scrollOffset = this.options.initialOffset
|
|
329
317
|
this.measurementsCache = this.options.initialMeasurementsCache
|
|
330
318
|
this.measurementsCache.forEach((item) => {
|
|
331
|
-
this.itemSizeCache
|
|
319
|
+
this.itemSizeCache.set(item.key, item.size)
|
|
332
320
|
})
|
|
333
321
|
|
|
334
322
|
this.maybeNotify()
|
|
@@ -372,12 +360,9 @@ export class Virtualizer<
|
|
|
372
360
|
}
|
|
373
361
|
|
|
374
362
|
_didMount = () => {
|
|
375
|
-
|
|
376
|
-
Object.values(this.measureElementCache).forEach((node) => ro?.observe(node))
|
|
377
|
-
|
|
363
|
+
this.measureElementCache.forEach(this.observer.observe)
|
|
378
364
|
return () => {
|
|
379
|
-
|
|
380
|
-
|
|
365
|
+
this.observer.disconnect()
|
|
381
366
|
this.cleanup()
|
|
382
367
|
}
|
|
383
368
|
}
|
|
@@ -397,8 +382,15 @@ export class Virtualizer<
|
|
|
397
382
|
|
|
398
383
|
this.unsubs.push(
|
|
399
384
|
this.options.observeElementRect(this, (rect) => {
|
|
385
|
+
const prev = this.scrollRect
|
|
400
386
|
this.scrollRect = rect
|
|
401
|
-
|
|
387
|
+
if (
|
|
388
|
+
this.options.horizontal
|
|
389
|
+
? rect.width !== prev.width
|
|
390
|
+
: rect.height !== prev.height
|
|
391
|
+
) {
|
|
392
|
+
this.maybeNotify()
|
|
393
|
+
}
|
|
402
394
|
}),
|
|
403
395
|
)
|
|
404
396
|
|
|
@@ -457,7 +449,7 @@ export class Virtualizer<
|
|
|
457
449
|
|
|
458
450
|
for (let i = min; i < count; i++) {
|
|
459
451
|
const key = getItemKey(i)
|
|
460
|
-
const measuredSize = itemSizeCache
|
|
452
|
+
const measuredSize = itemSizeCache.get(key)
|
|
461
453
|
const start = measurements[i - 1]
|
|
462
454
|
? measurements[i - 1]!.end
|
|
463
455
|
: paddingStart + scrollMargin
|
|
@@ -540,7 +532,10 @@ export class Virtualizer<
|
|
|
540
532
|
return parseInt(indexStr, 10)
|
|
541
533
|
}
|
|
542
534
|
|
|
543
|
-
private _measureElement = (
|
|
535
|
+
private _measureElement = (
|
|
536
|
+
node: TItemElement,
|
|
537
|
+
entry: ResizeObserverEntry | undefined,
|
|
538
|
+
) => {
|
|
544
539
|
const index = this.indexFromElement(node)
|
|
545
540
|
|
|
546
541
|
const item = this.measurementsCache[index]
|
|
@@ -548,34 +543,27 @@ export class Virtualizer<
|
|
|
548
543
|
return
|
|
549
544
|
}
|
|
550
545
|
|
|
551
|
-
const prevNode = this.measureElementCache
|
|
552
|
-
|
|
553
|
-
const ro = this.getResizeObserver()
|
|
546
|
+
const prevNode = this.measureElementCache.get(item.key)
|
|
554
547
|
|
|
555
548
|
if (!node.isConnected) {
|
|
556
|
-
|
|
549
|
+
this.observer.unobserve(node)
|
|
557
550
|
if (node === prevNode) {
|
|
558
|
-
|
|
551
|
+
this.measureElementCache.delete(item.key)
|
|
559
552
|
}
|
|
560
553
|
return
|
|
561
554
|
}
|
|
562
555
|
|
|
563
556
|
if (prevNode !== node) {
|
|
564
557
|
if (prevNode) {
|
|
565
|
-
|
|
566
|
-
}
|
|
567
|
-
ro?.observe(node)
|
|
568
|
-
this.measureElementCache[item.key] = node
|
|
569
|
-
} else {
|
|
570
|
-
if (!sync && !prevNode.__virtualizerSkipFirstNotSync) {
|
|
571
|
-
prevNode.__virtualizerSkipFirstNotSync = true
|
|
572
|
-
return
|
|
558
|
+
this.observer.unobserve(prevNode)
|
|
573
559
|
}
|
|
560
|
+
this.observer.observe(node)
|
|
561
|
+
this.measureElementCache.set(item.key, node)
|
|
574
562
|
}
|
|
575
563
|
|
|
576
|
-
const measuredItemSize = this.options.measureElement(node, this)
|
|
564
|
+
const measuredItemSize = this.options.measureElement(node, entry, this)
|
|
577
565
|
|
|
578
|
-
const itemSize = this.itemSizeCache
|
|
566
|
+
const itemSize = this.itemSizeCache.get(item.key) ?? item.size
|
|
579
567
|
|
|
580
568
|
const delta = measuredItemSize - itemSize
|
|
581
569
|
|
|
@@ -592,10 +580,11 @@ export class Virtualizer<
|
|
|
592
580
|
}
|
|
593
581
|
|
|
594
582
|
this.pendingMeasuredCacheIndexes.push(index)
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
583
|
+
|
|
584
|
+
this.itemSizeCache = new Map(
|
|
585
|
+
this.itemSizeCache.set(item.key, measuredItemSize),
|
|
586
|
+
)
|
|
587
|
+
|
|
599
588
|
this.notify()
|
|
600
589
|
}
|
|
601
590
|
}
|
|
@@ -605,7 +594,7 @@ export class Virtualizer<
|
|
|
605
594
|
return
|
|
606
595
|
}
|
|
607
596
|
|
|
608
|
-
this._measureElement(node,
|
|
597
|
+
this._measureElement(node, undefined)
|
|
609
598
|
}
|
|
610
599
|
|
|
611
600
|
getVirtualItems = memo(
|
|
@@ -692,7 +681,7 @@ export class Virtualizer<
|
|
|
692
681
|
return [this.getOffsetForAlignment(toOffset, align), align] as const
|
|
693
682
|
}
|
|
694
683
|
|
|
695
|
-
private isDynamicMode = () =>
|
|
684
|
+
private isDynamicMode = () => this.measureElementCache.size > 0
|
|
696
685
|
|
|
697
686
|
private cancelScrollToIndex = () => {
|
|
698
687
|
if (this.scrollToIndexTimeoutId !== null) {
|
|
@@ -741,8 +730,9 @@ export class Virtualizer<
|
|
|
741
730
|
this.scrollToIndexTimeoutId = setTimeout(() => {
|
|
742
731
|
this.scrollToIndexTimeoutId = null
|
|
743
732
|
|
|
744
|
-
const elementInDOM =
|
|
745
|
-
|
|
733
|
+
const elementInDOM = this.measureElementCache.has(
|
|
734
|
+
this.options.getItemKey(index),
|
|
735
|
+
)
|
|
746
736
|
|
|
747
737
|
if (elementInDOM) {
|
|
748
738
|
const [toOffset] = this.getOffsetForIndex(index, align)
|
|
@@ -792,7 +782,7 @@ export class Virtualizer<
|
|
|
792
782
|
}
|
|
793
783
|
|
|
794
784
|
measure = () => {
|
|
795
|
-
this.itemSizeCache =
|
|
785
|
+
this.itemSizeCache = new Map()
|
|
796
786
|
this.notify()
|
|
797
787
|
}
|
|
798
788
|
}
|