feature/comparison-slider #19
@@ -6,9 +6,11 @@
|
|||||||
- Keyboard navigation (ArrowUp/Down, Home, End)
|
- Keyboard navigation (ArrowUp/Down, Home, End)
|
||||||
- Fixed or dynamic item heights
|
- Fixed or dynamic item heights
|
||||||
- ARIA listbox/option pattern with single tab stop
|
- ARIA listbox/option pattern with single tab stop
|
||||||
|
- Custom shadcn ScrollArea scrollbar
|
||||||
-->
|
-->
|
||||||
<script lang="ts" generics="T">
|
<script lang="ts" generics="T">
|
||||||
import { createVirtualizer } from '$shared/lib';
|
import { createVirtualizer } from '$shared/lib';
|
||||||
|
import { ScrollArea } from '$shared/shadcn/ui/scroll-area';
|
||||||
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
||||||
import type { Snippet } from 'svelte';
|
import type { Snippet } from 'svelte';
|
||||||
|
|
||||||
@@ -100,6 +102,9 @@ let {
|
|||||||
children,
|
children,
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
|
// Reference to the ScrollArea viewport element for attaching the virtualizer
|
||||||
|
let viewportRef = $state<HTMLElement | null>(null);
|
||||||
|
|
||||||
const virtualizer = createVirtualizer(() => ({
|
const virtualizer = createVirtualizer(() => ({
|
||||||
count: items.length,
|
count: items.length,
|
||||||
data: items,
|
data: items,
|
||||||
@@ -107,6 +112,14 @@ const virtualizer = createVirtualizer(() => ({
|
|||||||
overscan,
|
overscan,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Attach virtualizer.container action to the viewport when it becomes available
|
||||||
|
$effect(() => {
|
||||||
|
if (viewportRef) {
|
||||||
|
const { destroy } = virtualizer.container(viewportRef);
|
||||||
|
return destroy;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
const visibleItems = virtualizer.items.map(item => items[item.index]);
|
const visibleItems = virtualizer.items.map(item => items[item.index]);
|
||||||
onVisibleItemsChange?.(visibleItems);
|
onVisibleItemsChange?.(visibleItems);
|
||||||
@@ -124,40 +137,28 @@ $effect(() => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<ScrollArea
|
||||||
use:virtualizer.container
|
bind:viewportRef
|
||||||
class={cn(
|
class={cn('relative rounded-md bg-background', 'h-150 w-full', className)}
|
||||||
'relative overflow-auto rounded-md bg-background',
|
orientation="vertical"
|
||||||
'h-150 w-full',
|
|
||||||
'scroll-smooth',
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
onfocusin={(e => {
|
|
||||||
// Prevent the browser from jumping the scroll when an inner element gets focus
|
|
||||||
e.preventDefault();
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<div
|
<div style:height="{virtualizer.totalSize}px" class="relative w-full">
|
||||||
style:height="{virtualizer.totalSize}px"
|
{#each virtualizer.items as item (item.key)}
|
||||||
class="w-full pointer-events-none"
|
<div
|
||||||
>
|
use:virtualizer.measureElement
|
||||||
</div>
|
data-index={item.index}
|
||||||
|
class="absolute top-0 left-0 w-full"
|
||||||
{#each virtualizer.items as item (item.key)}
|
style:transform="translateY({item.start}px)"
|
||||||
<div
|
>
|
||||||
use:virtualizer.measureElement
|
{#if item.index < items.length}
|
||||||
data-index={item.index}
|
{@render children({
|
||||||
class="absolute top-0 left-0 w-full"
|
|
||||||
style:transform="translateY({item.start}px)"
|
|
||||||
>
|
|
||||||
{#if item.index < items.length}
|
|
||||||
{@render children({
|
|
||||||
item: items[item.index],
|
item: items[item.index],
|
||||||
index: item.index,
|
index: item.index,
|
||||||
isVisible: item.isVisible,
|
isVisible: item.isVisible,
|
||||||
proximity: item.proximity,
|
proximity: item.proximity,
|
||||||
})}
|
})}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
</ScrollArea>
|
||||||
|
|||||||
Reference in New Issue
Block a user