@tanstack/virtual-core 3.0.0-beta.18 → 3.0.0-beta.19
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/cjs/index.js +63 -19
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.js +63 -19
- package/build/esm/index.js.map +1 -1
- package/build/stats.html +1 -1
- package/build/stats.json +14 -14
- package/build/types/index.d.ts +26 -7
- package/build/umd/index.development.js +63 -19
- 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 +71 -26
package/src/index.ts
CHANGED
|
@@ -84,7 +84,7 @@ export const observeElementRect = (
|
|
|
84
84
|
const observer = new ResizeObserver((entries) => {
|
|
85
85
|
cb({
|
|
86
86
|
width: entries[0]?.contentRect.width as number,
|
|
87
|
-
height: entries[0]?.contentRect.height as number
|
|
87
|
+
height: entries[0]?.contentRect.height as number,
|
|
88
88
|
})
|
|
89
89
|
})
|
|
90
90
|
|
|
@@ -148,7 +148,10 @@ const createOffsetObserver = (mode: ObserverMode) => {
|
|
|
148
148
|
let prevY: number = instance.scrollElement[propY]
|
|
149
149
|
|
|
150
150
|
const scroll = () => {
|
|
151
|
-
|
|
151
|
+
const offset =
|
|
152
|
+
instance.scrollElement[instance.options.horizontal ? propX : propY]
|
|
153
|
+
|
|
154
|
+
cb(Math.max(0, offset - instance.options.scrollMargin))
|
|
152
155
|
}
|
|
153
156
|
|
|
154
157
|
scroll()
|
|
@@ -191,22 +194,26 @@ export const measureElement = (
|
|
|
191
194
|
|
|
192
195
|
export const windowScroll = (
|
|
193
196
|
offset: number,
|
|
194
|
-
canSmooth: boolean,
|
|
197
|
+
{ canSmooth, sync }: { canSmooth: boolean; sync: boolean },
|
|
195
198
|
instance: Virtualizer<any, any>,
|
|
196
199
|
) => {
|
|
200
|
+
const toOffset = sync ? offset : offset + instance.options.scrollMargin
|
|
201
|
+
|
|
197
202
|
;(instance.scrollElement as Window)?.scrollTo?.({
|
|
198
|
-
[instance.options.horizontal ? 'left' : 'top']:
|
|
203
|
+
[instance.options.horizontal ? 'left' : 'top']: toOffset,
|
|
199
204
|
behavior: canSmooth ? 'smooth' : undefined,
|
|
200
205
|
})
|
|
201
206
|
}
|
|
202
207
|
|
|
203
208
|
export const elementScroll = (
|
|
204
209
|
offset: number,
|
|
205
|
-
canSmooth: boolean,
|
|
210
|
+
{ canSmooth, sync }: { canSmooth: boolean; sync: boolean },
|
|
206
211
|
instance: Virtualizer<any, any>,
|
|
207
212
|
) => {
|
|
213
|
+
const toOffset = sync ? offset : offset + instance.options.scrollMargin
|
|
214
|
+
|
|
208
215
|
;(instance.scrollElement as Element)?.scrollTo?.({
|
|
209
|
-
[instance.options.horizontal ? 'left' : 'top']:
|
|
216
|
+
[instance.options.horizontal ? 'left' : 'top']: toOffset,
|
|
210
217
|
behavior: canSmooth ? 'smooth' : undefined,
|
|
211
218
|
})
|
|
212
219
|
}
|
|
@@ -223,7 +230,7 @@ export interface VirtualizerOptions<
|
|
|
223
230
|
// Required from the framework adapter (but can be overridden)
|
|
224
231
|
scrollToFn: (
|
|
225
232
|
offset: number,
|
|
226
|
-
canSmooth: boolean,
|
|
233
|
+
options: { canSmooth: boolean; sync: boolean },
|
|
227
234
|
instance: Virtualizer<TScrollElement, TItemElement>,
|
|
228
235
|
) => void
|
|
229
236
|
observeElementRect: (
|
|
@@ -253,12 +260,16 @@ export interface VirtualizerOptions<
|
|
|
253
260
|
getItemKey?: (index: number) => Key
|
|
254
261
|
rangeExtractor?: (range: Range) => number[]
|
|
255
262
|
enableSmoothScroll?: boolean
|
|
263
|
+
scrollMargin?: number
|
|
264
|
+
scrollingDelay?: number
|
|
256
265
|
}
|
|
257
266
|
|
|
258
267
|
export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
259
268
|
private unsubs: (void | (() => void))[] = []
|
|
260
269
|
options!: Required<VirtualizerOptions<TScrollElement, TItemElement>>
|
|
261
270
|
scrollElement: TScrollElement | null = null
|
|
271
|
+
isScrolling: boolean = false
|
|
272
|
+
private isScrollingTimeoutId: ReturnType<typeof setTimeout> | null = null
|
|
262
273
|
private measurementsCache: Item[] = []
|
|
263
274
|
private itemMeasurementsCache: Record<Key, number> = {}
|
|
264
275
|
private pendingMeasuredCacheIndexes: number[] = []
|
|
@@ -270,7 +281,7 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
270
281
|
number,
|
|
271
282
|
(measurableItem: TItemElement | null) => void
|
|
272
283
|
> = {}
|
|
273
|
-
|
|
284
|
+
range: { startIndex: number; endIndex: number } = {
|
|
274
285
|
startIndex: 0,
|
|
275
286
|
endIndex: 0,
|
|
276
287
|
}
|
|
@@ -303,6 +314,8 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
303
314
|
onChange: () => {},
|
|
304
315
|
measureElement,
|
|
305
316
|
initialRect: { width: 0, height: 0 },
|
|
317
|
+
scrollMargin: 0,
|
|
318
|
+
scrollingDelay: 150,
|
|
306
319
|
...opts,
|
|
307
320
|
}
|
|
308
321
|
}
|
|
@@ -330,7 +343,10 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
330
343
|
this.cleanup()
|
|
331
344
|
|
|
332
345
|
this.scrollElement = scrollElement
|
|
333
|
-
this._scrollToOffset(this.scrollOffset,
|
|
346
|
+
this._scrollToOffset(this.scrollOffset, {
|
|
347
|
+
canSmooth: false,
|
|
348
|
+
sync: true,
|
|
349
|
+
})
|
|
334
350
|
|
|
335
351
|
this.unsubs.push(
|
|
336
352
|
this.options.observeElementRect(this, (rect) => {
|
|
@@ -341,10 +357,30 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
341
357
|
|
|
342
358
|
this.unsubs.push(
|
|
343
359
|
this.options.observeElementOffset(this, (offset) => {
|
|
344
|
-
this.
|
|
360
|
+
if (this.isScrollingTimeoutId !== null) {
|
|
361
|
+
clearTimeout(this.isScrollingTimeoutId)
|
|
362
|
+
this.isScrollingTimeoutId = null
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (this.scrollOffset !== offset) {
|
|
366
|
+
this.isScrolling = true
|
|
367
|
+
this.scrollOffset = offset
|
|
368
|
+
|
|
369
|
+
this.isScrollingTimeoutId = setTimeout(() => {
|
|
370
|
+
this.isScrollingTimeoutId = null
|
|
371
|
+
this.isScrolling = false
|
|
372
|
+
|
|
373
|
+
this.notify()
|
|
374
|
+
}, this.options.scrollingDelay)
|
|
375
|
+
} else {
|
|
376
|
+
this.isScrolling = false
|
|
377
|
+
}
|
|
378
|
+
|
|
345
379
|
this.calculateRange()
|
|
346
380
|
}),
|
|
347
381
|
)
|
|
382
|
+
} else if (!this.isScrolling) {
|
|
383
|
+
this.calculateRange()
|
|
348
384
|
}
|
|
349
385
|
}
|
|
350
386
|
|
|
@@ -391,7 +427,7 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
391
427
|
},
|
|
392
428
|
)
|
|
393
429
|
|
|
394
|
-
|
|
430
|
+
calculateRange = memo(
|
|
395
431
|
() => [this.getMeasurements(), this.getSize(), this.scrollOffset],
|
|
396
432
|
(measurements, outerSize, scrollOffset) => {
|
|
397
433
|
const range = calculateRange({
|
|
@@ -461,7 +497,10 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
461
497
|
if (!this.destinationOffset) {
|
|
462
498
|
this._scrollToOffset(
|
|
463
499
|
this.scrollOffset + (measuredItemSize - itemSize),
|
|
464
|
-
|
|
500
|
+
{
|
|
501
|
+
canSmooth: false,
|
|
502
|
+
sync: false,
|
|
503
|
+
},
|
|
465
504
|
)
|
|
466
505
|
}
|
|
467
506
|
}
|
|
@@ -503,7 +542,10 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
503
542
|
|
|
504
543
|
scrollToOffset = (
|
|
505
544
|
toOffset: number,
|
|
506
|
-
{
|
|
545
|
+
{
|
|
546
|
+
align = 'start',
|
|
547
|
+
smoothScroll = this.options.enableSmoothScroll,
|
|
548
|
+
}: ScrollToOffsetOptions = {},
|
|
507
549
|
) => {
|
|
508
550
|
const offset = this.scrollOffset
|
|
509
551
|
const size = this.getSize()
|
|
@@ -518,18 +560,22 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
518
560
|
}
|
|
519
561
|
}
|
|
520
562
|
|
|
563
|
+
const options = {
|
|
564
|
+
canSmooth: smoothScroll,
|
|
565
|
+
sync: false,
|
|
566
|
+
}
|
|
521
567
|
if (align === 'start') {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
568
|
+
this._scrollToOffset(toOffset, options)
|
|
569
|
+
} else if (align === 'end') {
|
|
570
|
+
this._scrollToOffset(toOffset - size, options)
|
|
571
|
+
} else if (align === 'center') {
|
|
572
|
+
this._scrollToOffset(toOffset - size / 2, options)
|
|
527
573
|
}
|
|
528
574
|
}
|
|
529
575
|
|
|
530
576
|
scrollToIndex = (
|
|
531
577
|
index: number,
|
|
532
|
-
{ align = 'auto',
|
|
578
|
+
{ align = 'auto', ...rest }: ScrollToIndexOptions = {},
|
|
533
579
|
) => {
|
|
534
580
|
const measurements = this.getMeasurements()
|
|
535
581
|
const offset = this.scrollOffset
|
|
@@ -560,22 +606,21 @@ export class Virtualizer<TScrollElement = unknown, TItemElement = unknown> {
|
|
|
560
606
|
? measurement.end + this.options.scrollPaddingEnd
|
|
561
607
|
: measurement.start - this.options.scrollPaddingStart
|
|
562
608
|
|
|
563
|
-
this.scrollToOffset(toOffset, { align,
|
|
609
|
+
this.scrollToOffset(toOffset, { align, ...rest })
|
|
564
610
|
}
|
|
565
611
|
|
|
566
612
|
getTotalSize = () =>
|
|
567
613
|
(this.getMeasurements()[this.options.count - 1]?.end ||
|
|
568
614
|
this.options.paddingStart) + this.options.paddingEnd
|
|
569
615
|
|
|
570
|
-
private _scrollToOffset = (
|
|
616
|
+
private _scrollToOffset = (
|
|
617
|
+
offset: number,
|
|
618
|
+
options: { canSmooth: boolean; sync: boolean },
|
|
619
|
+
) => {
|
|
571
620
|
clearTimeout(this.scrollCheckFrame)
|
|
572
621
|
|
|
573
622
|
this.destinationOffset = offset
|
|
574
|
-
this.options.scrollToFn(
|
|
575
|
-
offset,
|
|
576
|
-
canSmooth,
|
|
577
|
-
this,
|
|
578
|
-
)
|
|
623
|
+
this.options.scrollToFn(offset, options, this)
|
|
579
624
|
|
|
580
625
|
let scrollCheckFrame: ReturnType<typeof setTimeout>
|
|
581
626
|
|