feat(VirtualList): incoroprate new logic related to window scroll and separation of isVisible flag

This commit is contained in:
Ilia Mashkov
2026-02-02 12:16:04 +03:00
parent 4a94f7bd09
commit eaf9d069c5

View File

@@ -76,6 +76,18 @@ interface Props {
* ``` * ```
*/ */
onNearBottom?: (lastVisibleIndex: number) => void; onNearBottom?: (lastVisibleIndex: number) => void;
/**
* Snippet for rendering individual list items.
*
* The snippet receives an object containing:
* - `item`: The item from the items array (type T)
* - `index`: The current item's index in the array
*
* This pattern provides type safety and flexibility for
* rendering different item types without prop drilling.
*
* @template T - The type of items in the list
*/
/** /**
* Snippet for rendering individual list items. * Snippet for rendering individual list items.
* *
@@ -89,8 +101,13 @@ interface Props {
* @template T - The type of items in the list * @template T - The type of items in the list
*/ */
children: Snippet< children: Snippet<
[{ item: T; index: number; isVisible: boolean; proximity: number }] [{ item: T; index: number; isFullyVisible: boolean; isPartiallyVisible: boolean; proximity: number }]
>; >;
/**
* Whether to use the window as the scroll container.
* @default false
*/
useWindowScroll?: boolean;
} }
let { let {
@@ -102,6 +119,7 @@ let {
onVisibleItemsChange, onVisibleItemsChange,
onNearBottom, onNearBottom,
children, children,
useWindowScroll = false,
}: Props = $props(); }: Props = $props();
// Reference to the ScrollArea viewport element for attaching the virtualizer // Reference to the ScrollArea viewport element for attaching the virtualizer
@@ -112,6 +130,7 @@ const virtualizer = createVirtualizer(() => ({
data: items, data: items,
estimateSize: typeof itemHeight === 'function' ? itemHeight : () => itemHeight, estimateSize: typeof itemHeight === 'function' ? itemHeight : () => itemHeight,
overscan, overscan,
useWindowScroll,
})); }));
// Attach virtualizer.container action to the viewport when it becomes available // Attach virtualizer.container action to the viewport when it becomes available
@@ -139,9 +158,38 @@ $effect(() => {
}); });
</script> </script>
{#if useWindowScroll}
<div class={cn('relative w-full', className)} bind:this={viewportRef}>
<div style:height="{virtualizer.totalSize}px" class="relative w-full">
{#each virtualizer.items as item (item.key)}
<div
use:virtualizer.measureElement
data-index={item.index}
class="absolute top-0 left-0 w-full"
style:transform="translateY({item.start}px)"
>
{#if item.index < items.length}
{@render children({
// TODO: Fix indenation rule for this case
item: items[item.index],
index: item.index,
isFullyVisible: item.isFullyVisible,
isPartiallyVisible: item.isPartiallyVisible,
proximity: item.proximity,
})}
{/if}
</div>
{/each}
</div>
</div>
{:else}
<ScrollArea <ScrollArea
bind:viewportRef bind:viewportRef
class={cn('relative rounded-md bg-background', 'h-150 w-full', className)} class={cn(
'relative rounded-md bg-background',
'h-150 w-full',
className,
)}
orientation="vertical" orientation="vertical"
> >
<div style:height="{virtualizer.totalSize}px" class="relative w-full"> <div style:height="{virtualizer.totalSize}px" class="relative w-full">
@@ -158,7 +206,8 @@ $effect(() => {
// TODO: Fix indenation rule for this case // TODO: Fix indenation rule for this case
item: items[item.index], item: items[item.index],
index: item.index, index: item.index,
isVisible: item.isVisible, isFullyVisible: item.isFullyVisible,
isPartiallyVisible: item.isPartiallyVisible,
proximity: item.proximity, proximity: item.proximity,
})} })}
{/if} {/if}
@@ -166,3 +215,4 @@ $effect(() => {
{/each} {/each}
</div> </div>
</ScrollArea> </ScrollArea>
{/if}