Open jithujoshyjy opened 1 year ago
Here's a minimal yet incomplete solution that I have come up with:
<script lang="ts">
import { onMount } from "svelte";
export let data: unknown[] = [],
containerTag = "div",
containerHeight: string,
itemHeight: string,
renderAhead: number = 5,
itemTag = "div",
scrollbarWidth = "unset";
const itemCount = data.length;
let scrollTop = 0,
rowHeight = 0,
totalContentHeight = 0,
startNodeIdx = 0,
viewportHeight = 0,
visibleNodeCount = 1,
offsetY = 0;
let slice = data.slice(startNodeIdx, startNodeIdx + visibleNodeCount);
let virtualBoxNode: HTMLElement;
const handleScroll = (evt: UIEvent) =>
requestAnimationFrame(updateOnScroll);
onMount(() => {
const firstChild = virtualBoxNode.firstElementChild;
if (!firstChild) return;
viewportHeight = parseFloat(getComputedStyle(virtualBoxNode).height);
rowHeight = parseFloat(getComputedStyle(firstChild).borderRadius);
totalContentHeight = rowHeight * itemCount;
updateOnScroll();
});
function updateOnScroll() {
scrollTop = virtualBoxNode.scrollTop;
startNodeIdx =
Math.max(0, Math.floor(scrollTop / rowHeight) - renderAhead) || 0;
visibleNodeCount = Math.min(
itemCount - startNodeIdx,
Math.ceil(viewportHeight / rowHeight) + 2 * renderAhead
);
offsetY = startNodeIdx * rowHeight;
slice = data.slice(startNodeIdx, startNodeIdx + visibleNodeCount);
}
</script>
<div
class="virtual-scroll-box"
bind:this={virtualBoxNode}
on:scroll={handleScroll}
style:--height={containerHeight}
style:--scrollbar-width={scrollbarWidth}
>
<span style:border-radius={itemHeight} />
<div class="viewport" style:--viewport-height={totalContentHeight + "px"}>
<svelte:element
this={containerTag}
class="virtual-scroll-container"
style:--translate-y={offsetY + "px"}
>
{#each slice as item, i}
<svelte:element
this={itemTag}
class="virtual-scroll-item"
style:--item-height={rowHeight + "px"}
>
<slot {item} index={i} />
</svelte:element>
{/each}
</svelte:element>
</div>
</div>
<style lang="postcss">
.virtual-scroll-box {
--height: 100%;
--scrollbar-width: unset;
@apply overflow-y-auto w-full h-[var(--height)];
}
.virtual-scroll-box::-webkit-scrollbar {
@apply w-[var(--scrollbar-width)];
}
.viewport {
--viewport-height: 100%;
@apply overflow-hidden will-change-transform h-[var(--viewport-height)] relative;
}
.virtual-scroll-container {
--translate-y: 0px;
@apply translate-y-[var(--translate-y)] will-change-transform;
}
.virtual-scroll-item {
--item-height: auto;
@apply h-[var(--item-height)];
}
</style>
I was wondering if there's a way to make VirtualList support tags like <ul> to be the list a container having <li>s as list items or how can a <table> be rendered using it?