<template> <div> <Head> <Title>Vue VirtualScroller Component</Title> <Meta name="description" content="VirtualScroller is a performant approach to handle huge data efficiently." /> </Head> <div class="content-section introduction"> <div class="feature-intro"> <h1>VirtualScroller</h1> <p>VirtualScroller is a performant approach to handle huge data efficiently.</p> </div> <AppDemoActions /> </div> <div class="content-section implementation virtualscroller-demo"> <div class="card"> <h5 class="mb-0">Basic</h5> <div class="flex align-items-center flex-wrap"> <div class="flex flex-column mr-3 mt-3"> <h6>Vertical</h6> <VirtualScroller :items="basicItems" :itemSize="50"> <template v-slot:item="{ item, options }"> <div :class="['scroll-item p-2', { odd: options.odd }]" style="height: 50px">{{ item }}</div> </template> </VirtualScroller> </div> <div class="flex flex-column mr-3 mt-3"> <h6>Horizontal</h6> <VirtualScroller :items="basicItems" :itemSize="50" orientation="horizontal"> <template v-slot:item="{ item, options }"> <div :class="['scroll-item p-2', { odd: options.odd }]" style="width: 50px">{{ item }}</div> </template> </VirtualScroller> </div> <div class="flex flex-column mr-3 mt-3"> <h6>Both</h6> <VirtualScroller :items="multiItems" :itemSize="[50, 100]" orientation="both"> <template v-slot:item="{ item, options }"> <div :class="['scroll-item p-2', { odd: options.odd }]" style="height: 50px"> <template v-for="(el, index) of item" :key="index"> <div style="width: 100px">{{ el }}</div> </template> </div> </template> </VirtualScroller> </div> </div> </div> <div class="card"> <h5 class="mb-0">Scroll Delay</h5> <div class="flex align-items-center flex-wrap"> <div class="flex flex-column mr-3 mt-3"> <h6>0ms Delay</h6> <VirtualScroller :items="basicItems" :itemSize="50"> <template v-slot:item="{ item, options }"> <div :class="['scroll-item p-2', { odd: options.odd }]" style="height: 50px">{{ item }}</div> </template> </VirtualScroller> </div> <div class="flex flex-column mr-3 mt-3"> <h6>150ms Delay</h6> <VirtualScroller :items="basicItems" :itemSize="50" :delay="150"> <template v-slot:item="{ item, options }"> <div :class="['scroll-item p-2', { odd: options.odd }]" style="height: 50px">{{ item }}</div> </template> </VirtualScroller> </div> <div class="flex flex-column mr-3 mt-3"> <h6>250ms Delay</h6> <VirtualScroller :items="basicItems" :itemSize="50" :delay="250"> <template v-slot:item="{ item, options }"> <div :class="['scroll-item p-2', { odd: options.odd }]" style="height: 50px">{{ item }}</div> </template> </VirtualScroller> </div> </div> </div> <div class="card"> <h5 class="mb-0">Loading</h5> <div class="flex align-items-center flex-wrap"> <div class="flex flex-column mr-3 mt-3"> <h6>Basic</h6> <VirtualScroller :items="basicItems" :itemSize="50" showLoader :delay="250"> <template v-slot:item="{ item, options }"> <div :class="['scroll-item p-2', { odd: options.odd }]" style="height: 50px">{{ item }}</div> </template> </VirtualScroller> </div> <div class="flex flex-column mr-3 mt-3"> <h6>Templating</h6> <VirtualScroller class="custom-loading" :items="basicItems" :itemSize="50" showLoader :delay="250"> <template v-slot:item="{ item, options }"> <div :class="['scroll-item p-2', { odd: options.odd }]" style="height: 50px">{{ item }}</div> </template> <template v-slot:loader="{ options }"> <div :class="['scroll-item p-2', { odd: options.odd }]" style="height: 50px"> <Skeleton :width="options.even ? '60%' : '50%'" height="1.3rem" /> </div> </template> </VirtualScroller> </div> </div> </div> <div class="card"> <h5 class="mb-0">Lazy</h5> <div class="flex align-items-center flex-wrap"> <div class="flex flex-column mr-3 mt-3"> <VirtualScroller :items="lazyItems" :itemSize="50" showLoader :delay="250" :loading="lazyLoading" lazy @lazy-load="onLazyLoad"> <template v-slot:item="{ item, options }"> <div :class="['scroll-item p-2', { odd: options.odd }]" style="height: 50px">{{ item }}</div> </template> </VirtualScroller> </div> </div> </div> <div class="card"> <h5 class="mb-0">Template</h5> <div class="flex align-items-center flex-wrap"> <div class="flex flex-column mr-3 mt-3"> <VirtualScroller class="custom-loading" :items="basicItems" :itemSize="25 * 7" showLoader :delay="250"> <template v-slot:item="{ item, options }"> <div :class="['custom-scroll-item scroll-item', { odd: options.odd }]"> <div class="flex align-items-center px-2" style="height: 25px">{{ `Item: ${item}` }}</div> <div class="flex align-items-center px-2" style="height: 25px">{{ `Index: ${options.index}` }}</div> <div class="flex align-items-center px-2" style="height: 25px">{{ `Count: ${options.count}` }}</div> <div class="flex align-items-center px-2" style="height: 25px">{{ `First: ${options.first}` }}</div> <div class="flex align-items-center px-2" style="height: 25px">{{ `Last: ${options.last}` }}</div> <div class="flex align-items-center px-2" style="height: 25px">{{ `Even: ${options.even}` }}</div> <div class="flex align-items-center px-2" style="height: 25px">{{ `Odd: ${options.odd}` }}</div> </div> </template> <template v-slot:loader="{ options }"> <div :class="['custom-scroll-item scroll-item', { odd: options.odd }]"> <div class="flex align-items-center px-2" style="height: 25px"><Skeleton width="60%" height="1.2rem" /></div> <div class="flex align-items-center px-2" style="height: 25px"><Skeleton width="50%" height="1.2rem" /></div> <div class="flex align-items-center px-2" style="height: 25px"><Skeleton width="60%" height="1.2rem" /></div> <div class="flex align-items-center px-2" style="height: 25px"><Skeleton width="50%" height="1.2rem" /></div> <div class="flex align-items-center px-2" style="height: 25px"><Skeleton width="60%" height="1.2rem" /></div> <div class="flex align-items-center px-2" style="height: 25px"><Skeleton width="50%" height="1.2rem" /></div> <div class="flex align-items-center px-2" style="height: 25px"><Skeleton width="60%" height="1.2rem" /></div> </div> </template> </VirtualScroller> </div> </div> </div> </div> <VirtualScrollerDoc /> </div> </template> <script> import VirtualScrollerDoc from './VirtualScrollerDoc.vue'; export default { data() { return { basicItems: null, multiItems: null, lazyItems: null, lazyLoading: false, loadLazyTimeout: null }; }, mounted() { this.basicItems = Array.from({ length: 100000 }).map((_, i) => `Item #${i}`); this.multiItems = Array.from({ length: 1000 }).map((_, i) => Array.from({ length: 1000 }).map((_j, j) => `Item #${i}_${j}`)); this.lazyItems = Array.from({ length: 100000 }); }, methods: { onLazyLoad(event) { this.lazyLoading = true; if (this.loadLazyTimeout) { clearTimeout(this.loadLazyTimeout); } //imitate delay of a backend call this.loadLazyTimeout = setTimeout(() => { const { first, last } = event; const lazyItems = [...this.lazyItems]; for (let i = first; i < last; i++) { lazyItems[i] = `Item #${i}`; } this.lazyItems = lazyItems; this.lazyLoading = false; }, Math.random() * 1000 + 250); } }, components: { VirtualScrollerDoc } }; </script> <style lang="scss" scoped> .virtualscroller-demo { ::v-deep(.p-virtualscroller) { height: 200px; width: 200px; border: 1px solid var(--surface-border); .scroll-item { background-color: var(--surface-card); display: flex; align-items: center; } .custom-scroll-item { flex-direction: column; align-items: stretch; } .odd { background-color: var(--surface-ground); } } ::v-deep(.p-horizontal-scroll) { .p-virtualscroller-content { display: flex; flex-direction: row; } .scroll-item { writing-mode: vertical-lr; } } ::v-deep(.custom-loading > .p-virtualscroller-loader) { display: block; } } </style>