Files
veridian/app/components/RowVirtualizerFixed.vue

52 lines
1.6 KiB
Vue

<script setup lang="ts">
import { useVirtualizer } from '@tanstack/vue-virtual';
const props = defineProps<{
items: readonly any[] | any[];
keyField?: string;
scrollElement: HTMLElement | null;
itemSize: number;
overscan: number;
prerender?: number;
}>();
const rowVirtualizer = useVirtualizer(computed(() => ({
count: props.items.length,
getScrollElement: () => props.scrollElement,
estimateSize: () => props.itemSize,
overscan: props.overscan,
getItemKey: (index: number) => props.keyField ? props.items[index]?.[props.keyField] || index : index,
initialRect: {
width: 0,
height: props.prerender ? props.itemSize * props.prerender : 0
},
})));
const virtualRows = computed(() => rowVirtualizer.value.getVirtualItems());
const totalSize = computed(() => rowVirtualizer.value.getTotalSize());
defineExpose({
scrollToIndex: (index: number, options?: { align?: 'start' | 'center' | 'end' }) => {
rowVirtualizer.value.scrollToIndex(index, options);
}
})
watch(() => props.items, () => {
rowVirtualizer.value.measure();
}, { deep: false });
</script>
<template>
<div :style="{ height: `${totalSize}px`, width: '100%', position: 'relative' }" v-bind="$attrs">
<div v-for="virtualRow in virtualRows" :key="(virtualRow.key as any | number)" :style="{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${virtualRow.size}`,
transform: `translateY(${virtualRow.start}px)`,
}">
<slot :item="props.items[virtualRow.index]" :index="virtualRow.index" />
</div>
</div>
</template>