feat(VirtualList): add onJump callback for scroll-beyond-loaded detection

This commit is contained in:
Ilia Mashkov
2026-04-20 22:14:44 +03:00
parent 67fc9dee72
commit b831861662
@@ -62,6 +62,10 @@ interface Props extends
* Near bottom callback
*/
onNearBottom?: (lastVisibleIndex: number) => void;
/**
* Fires when scroll position exceeds loaded content — user jumped beyond data
*/
onJump?: (targetIndex: number) => void;
/**
* Item render snippet
*/
@@ -95,6 +99,7 @@ let {
class: className,
onVisibleItemsChange,
onNearBottom,
onJump,
children,
useWindowScroll = false,
isLoading = false,
@@ -170,6 +175,10 @@ const throttledNearBottom = throttle((lastVisibleIndex: number) => {
onNearBottom?.(lastVisibleIndex);
}, 200); // 200ms throttle
const throttledOnJump = throttle((targetIndex: number) => {
onJump?.(targetIndex);
}, 200);
// Calculate top/bottom padding for spacer elements
// In CSS Grid, gap creates space BETWEEN elements.
// The top spacer should place the first row at its virtual offset.
@@ -227,6 +236,26 @@ $effect(() => {
}
}
});
$effect(() => {
// Fire onJump when scroll is beyond the loaded content boundary.
// Target index estimates which item the user scrolled to.
if (!onJump || !virtualizer.containerHeight || virtualizer.scrollOffset <= 0) {
return;
}
const isAhead = virtualizer.scrollOffset > virtualizer.totalSize;
if (!isAhead) {
return;
}
const estimatedItemHeight = typeof itemHeight === 'number' ? itemHeight : 80;
// Include visible rows + overscan so the bottom of the viewport is fully covered
const topItemIndex = Math.floor(virtualizer.scrollOffset / estimatedItemHeight) * columns;
const visibleRows = Math.ceil(virtualizer.containerHeight / estimatedItemHeight);
const targetIndex = topItemIndex + (visibleRows + overscan) * columns;
throttledOnJump(targetIndex);
});
</script>
{#snippet content()}