1098 lines
49 KiB
Vue
1098 lines
49 KiB
Vue
<template>
|
|
<AppDoc name="VirtualScrollerDemo" :sources="sources" github="virtualscroller/VirtualScrollerDemo.vue">
|
|
<h5>Imports</h5>
|
|
<pre v-code.script><code>
|
|
import VirtualScroller from 'primevue/virtualscroller';
|
|
|
|
</code></pre>
|
|
|
|
<h5>Import via CDN</h5>
|
|
<pre v-code><code>
|
|
<script src="https://unpkg.com/primevue@^3/core/core.min.js"></script>
|
|
|
|
</code></pre>
|
|
|
|
<h5>Getting Started</h5>
|
|
<p>
|
|
VirtualScroller is used to display huge data. It periodically adds special elements defined according to the scroll's position to the DOM. The <i>item</i> and <i>itemSize</i> properties and <i>item</i> template are required on component.
|
|
In addition, an initial array is required based on the total number of items to display.<br />
|
|
VirtualScroller automatically calculates how many items will be displayed in the view according to <i>itemSize</i> using a specified scroll height. Its scroll height can be adjusted with <i>scrollHeight</i> property or height property of
|
|
CSS.
|
|
</p>
|
|
|
|
<pre v-code><code><template v-pre>
|
|
<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>
|
|
</template>
|
|
</code></pre>
|
|
|
|
<pre v-code.script><code>
|
|
export default {
|
|
data() {
|
|
return {
|
|
basicItems: null
|
|
}
|
|
},
|
|
mounted() {
|
|
this.basicItems = Array.from({ length: 100000 }).map((_, i) => `Item #${i}`);
|
|
}
|
|
}
|
|
|
|
</code></pre>
|
|
|
|
<h5>Loader</h5>
|
|
<p>VirtualScroller has a special loader. It can be activated with the <i>showLoader</i> property. In addition, <i>loader</i> template can be used to add custom loaders to item elements.</p>
|
|
|
|
<pre v-code><code><template v-pre>
|
|
<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>
|
|
<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>
|
|
</template>
|
|
</code></pre>
|
|
|
|
<h5>Lazy</h5>
|
|
<p>Lazy mode is handy to deal with large datasets, instead of loading the entire data, small chunks of data is loaded by invoking <i>lazy-load</i> event.</p>
|
|
|
|
<pre v-code><code><template v-pre>
|
|
<VirtualScroller :items="lazyItems" :itemSize="50" showLoader :delay="250" :loading="lazyLoading" :lazy=true @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>
|
|
</template>
|
|
</code></pre>
|
|
|
|
<pre v-code.script><code>
|
|
export default {
|
|
data() {
|
|
return {
|
|
lazyItems: null,
|
|
lazyLoading: false,
|
|
loadLazyTimeout: null,
|
|
}
|
|
},
|
|
mounted() {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
</code></pre>
|
|
|
|
<h5>Properties</h5>
|
|
<div class="doc-tablewrapper">
|
|
<table class="doc-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Type</th>
|
|
<th>Default</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>id</td>
|
|
<td>string</td>
|
|
<td>null</td>
|
|
<td>Unique identifier of the element.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>style</td>
|
|
<td>any</td>
|
|
<td>null</td>
|
|
<td>Inline style of the component.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>class</td>
|
|
<td>string</td>
|
|
<td>null</td>
|
|
<td>Style class of the component.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>items</td>
|
|
<td>array</td>
|
|
<td>null</td>
|
|
<td>An array of objects to display.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>itemSize</td>
|
|
<td>number / [number, number]</td>
|
|
<td>null</td>
|
|
<td>The height/width of item according to orientation.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>scrollHeight</td>
|
|
<td>string</td>
|
|
<td>null</td>
|
|
<td>Height of the scroll viewport.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>scrollWidth</td>
|
|
<td>string</td>
|
|
<td>null</td>
|
|
<td>Width of the scroll viewport.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>orientation</td>
|
|
<td>string</td>
|
|
<td>'vertical'</td>
|
|
<td>The orientation of scrollbar, valid values are 'vertical', 'horizontal' and 'both'.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>numToleratedItems</td>
|
|
<td>number</td>
|
|
<td>null</td>
|
|
<td>
|
|
Determines how many additional elements to add to the DOM outside of the view. <br />
|
|
According to the scrolls made up and down, extra items are added in a certain algorithm in the form of multiples of this number. <br />
|
|
Default value is half the number of items shown in the view.
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>delay</td>
|
|
<td>number</td>
|
|
<td>0</td>
|
|
<td>Delay in scroll before new data is loaded.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>lazy</td>
|
|
<td>boolean</td>
|
|
<td>false</td>
|
|
<td>Defines if data is loaded and interacted with in lazy manner.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>disabled</td>
|
|
<td>boolean</td>
|
|
<td>false</td>
|
|
<td>If disabled, the VirtualScroller feature is eliminated and the content is displayed directly.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>loaderDisabled</td>
|
|
<td>boolean</td>
|
|
<td>false</td>
|
|
<td>Used to implement a custom loader instead of using the loader feature in the VirtualScroller.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>loading</td>
|
|
<td>boolean</td>
|
|
<td>false</td>
|
|
<td>Whether the data is loaded.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>showSpacer</td>
|
|
<td>boolean</td>
|
|
<td>true</td>
|
|
<td>Used to implement a custom spacer instead of using the spacer feature in the VirtualScroller.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>showLoader</td>
|
|
<td>boolean</td>
|
|
<td>false</td>
|
|
<td>Whether to show loader.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>tabindex</td>
|
|
<td>number</td>
|
|
<td>0</td>
|
|
<td>Index of the element in tabbing order.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<h5>Events</h5>
|
|
<div class="doc-tablewrapper">
|
|
<table class="doc-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Parameters</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>scroll</td>
|
|
<td>event: Browser event</td>
|
|
<td>Callback to invoke when scroll position changes.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>scroll-index-change</td>
|
|
<td>
|
|
event.first: First index of the new data range to be loaded.<br />
|
|
event.last: Last index of the new data range to be loaded.
|
|
</td>
|
|
<td>Callback to invoke when scroll position and item's range in view changes.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>lazy-load</td>
|
|
<td>
|
|
event.first: First index of the new data range to be loaded.<br />
|
|
event.last: Last index of the new data range to be loaded.
|
|
</td>
|
|
<td>Callback to invoke in lazy mode to load new data.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<h5>Methods</h5>
|
|
<div class="doc-tablewrapper">
|
|
<table class="doc-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Parameters</th>
|
|
<th>Description</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>scrollTo</td>
|
|
<td>
|
|
left: Left position of scroll. <br />
|
|
top: Top position of scroll <br />
|
|
behavior: Behavior of scroll, valid values are 'auto' and 'smooth'
|
|
</td>
|
|
<td>Scroll to move to a specific position.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>scrollToIndex</td>
|
|
<td>
|
|
index: Index of item according to orientation mode. <br />
|
|
behavior: Behavior of scroll, valid values are 'auto' and 'smooth'
|
|
</td>
|
|
<td>Scroll to move to a specific item.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>scrollInView</td>
|
|
<td>
|
|
index: Index of item according to orientation mode. <br />
|
|
to: Defines the location of the item in view, valid values are 'to-start' and 'to-end'. <br />
|
|
behavior: Behavior of scroll, valid values are 'auto' and 'smooth'
|
|
</td>
|
|
<td>It is used to move the specified index into the view. It is a method that will usually be needed when keyboard support is added to the virtualScroller component.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>getRenderedRange</td>
|
|
<td>-</td>
|
|
<td>Returns the range of items added to the DOM.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<h5>Slots</h5>
|
|
<div class="doc-tablewrapper">
|
|
<table class="doc-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Parameters</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>content</td>
|
|
<td>
|
|
items: An array of objects to display.<br />
|
|
styleClass: Style class of the component<br />
|
|
contentRef: Referance of the content<br />
|
|
getItemOptions: Options of the items<br />
|
|
loading: Whether the data is loaded<br />
|
|
getLoaderOptions: Loader options of the items while the data is loading.<br />
|
|
itemSize: The height/width of item according to orientation.<br />
|
|
rows: The number of the rendered rows.<br />
|
|
columns: The number of the rendered columns.<br />
|
|
spacerStyle: The style of spacer element.<br />
|
|
contentStyle: The style of content element.<br />
|
|
vertical: Whether the orientation is vertical.<br />
|
|
horizontal: Whether the orientation is horizontal.<br />
|
|
both: Whether the orientation is both.<br />
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>item</td>
|
|
<td>
|
|
item: Item instance<br />
|
|
options: Options of the item instance
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>loader</td>
|
|
<td>options: Options of the loader items</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<h5>Styling</h5>
|
|
<p>Following is the list of structural style classes, for theming classes visit <router-link to="/theming">theming</router-link> page.</p>
|
|
<div class="doc-tablewrapper">
|
|
<table class="doc-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Element</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>p-virtualscroller</td>
|
|
<td>Container element.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>p-virtualscroller-content</td>
|
|
<td>Content element.</td>
|
|
</tr>
|
|
<tr>
|
|
<td>p-virtualscroller-loader</td>
|
|
<td>Loader element.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<h5>Dependencies</h5>
|
|
<p>None.</p>
|
|
</AppDoc>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
data() {
|
|
return {
|
|
sources: {
|
|
'options-api': {
|
|
tabName: 'Options API Source',
|
|
content: `
|
|
<template>
|
|
<div class="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=true @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>
|
|
</template>
|
|
|
|
<script>
|
|
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: 10000 });
|
|
},
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
<\\/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>
|
|
|
|
`
|
|
},
|
|
'composition-api': {
|
|
tabName: 'Composition API Source',
|
|
content: `
|
|
<template>
|
|
<div class="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=true @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>
|
|
</template>
|
|
|
|
<script>
|
|
import { ref, onMounted } from 'vue';
|
|
|
|
export default {
|
|
setup() {
|
|
const basicItems = ref(Array.from({ length: 100000 }).map((_, i) => \`Item #\${i}\`));
|
|
const multiItems = ref(Array.from({ length: 1000 }).map((_, i) => Array.from({ length: 1000 }).map((_j, j) => \`Item #\${i}_\${j}\`)));
|
|
const lazyItems = ref(Array.from({ length: 10000 }));
|
|
const lazyLoading = ref(false);
|
|
const loadLazyTimeout = ref(null);
|
|
|
|
const onLazyLoad = (event) => {
|
|
lazyLoading.value = true;
|
|
|
|
if (loadLazyTimeout.value) {
|
|
clearTimeout(loadLazyTimeout.value);
|
|
}
|
|
|
|
//imitate delay of a backend call
|
|
loadLazyTimeout.value = setTimeout(() => {
|
|
const { first, last } = event;
|
|
const _lazyItems = [...lazyItems.value];
|
|
|
|
for (let i = first; i < last; i++) {
|
|
_lazyItems[i] = \`Item #\${i}\`;
|
|
}
|
|
|
|
lazyItems.value = _lazyItems;
|
|
lazyLoading.value = false;
|
|
|
|
}, Math.random() * 1000 + 250);
|
|
}
|
|
|
|
return { basicItems, multiItems, lazyItems, lazyLoading, onLazyLoad }
|
|
},
|
|
methods: {
|
|
|
|
}
|
|
}
|
|
<\\/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>
|
|
`
|
|
},
|
|
'browser-source': {
|
|
tabName: 'Browser Source',
|
|
imports: `<script src="https://unpkg.com/primevue@^3/skeleton/skeleton.min.js"><\\/script>`,
|
|
content: `<div id="app">
|
|
<div class="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>
|
|
<p-virtualscroller :items="basicItems" :item-size="50">
|
|
<template v-slot:item="{ item, options }">
|
|
<div :class="['scroll-item p-2', {'odd': options.odd}]" style="height: 50px">{{ item }}</div>
|
|
</template>
|
|
</p-virtualscroller>
|
|
</div>
|
|
|
|
<div class="flex flex-column mr-3 mt-3">
|
|
<h6>Horizontal</h6>
|
|
<p-virtualscroller :items="basicItems" :item-size="50" orientation="horizontal">
|
|
<template v-slot:item="{ item, options }">
|
|
<div :class="['scroll-item p-2', {'odd': options.odd}]" style="width: 50px">{{ item }}</div>
|
|
</template>
|
|
</p-virtualscroller>
|
|
</div>
|
|
|
|
<div class="flex flex-column mr-3 mt-3">
|
|
<h6>Both</h6>
|
|
<p-virtualscroller :items="multiItems" :item-size="[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>
|
|
</p-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>
|
|
<p-virtualscroller :items="basicItems" :item-size="50">
|
|
<template v-slot:item="{ item, options }">
|
|
<div :class="['scroll-item p-2', {'odd': options.odd}]" style="height: 50px">{{ item }}</div>
|
|
</template>
|
|
</p-virtualscroller>
|
|
</div>
|
|
|
|
<div class="flex flex-column mr-3 mt-3">
|
|
<h6>150ms Delay</h6>
|
|
<p-virtualscroller :items="basicItems" :item-size="50" :delay="150">
|
|
<template v-slot:item="{ item, options }">
|
|
<div :class="['scroll-item p-2', {'odd': options.odd}]" style="height: 50px">{{ item }}</div>
|
|
</template>
|
|
</p-virtualscroller>
|
|
</div>
|
|
|
|
<div class="flex flex-column mr-3 mt-3">
|
|
<h6>250ms Delay</h6>
|
|
<p-virtualscroller :items="basicItems" :item-size="50" :delay="250">
|
|
<template v-slot:item="{ item, options }">
|
|
<div :class="['scroll-item p-2', {'odd': options.odd}]" style="height: 50px">{{ item }}</div>
|
|
</template>
|
|
</p-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>
|
|
<p-virtualscroller :items="basicItems" :item-size="50" show-loader :delay="250">
|
|
<template v-slot:item="{ item, options }">
|
|
<div :class="['scroll-item p-2', {'odd': options.odd}]" style="height: 50px">{{ item }}</div>
|
|
</template>
|
|
</p-virtualscroller>
|
|
</div>
|
|
|
|
<div class="flex flex-column mr-3 mt-3">
|
|
<h6>Templating</h6>
|
|
<p-virtualscroller class="custom-loading" :items="basicItems" :item-size="50" show-loader :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" >
|
|
<p-skeleton :width="options.even ? '60%' : '50%'" height="1.3rem"></p-skeleton>
|
|
</div>
|
|
</template>
|
|
</p-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">
|
|
<p-virtualscroller :items="lazyItems" :item-size="50" show-loader :delay="250" :loading="lazyLoading" :lazy=true @lazy-load="onLazyLoad">
|
|
<template v-slot:item="{ item, options }">
|
|
<div :class="['scroll-item p-2', {'odd': options.odd}]" style="height: 50px">{{ item }}</div>
|
|
</template>
|
|
</p-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">
|
|
<p-virtualscroller class="custom-loading" :items="basicItems" :item-size="25 * 7" show-loader :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"><p-skeleton width="60%" height="1.2rem"></p-skeleton></div>
|
|
<div class="flex align-items-center px-2" style="height: 25px"><p-skeleton width="50%" height="1.2rem"></p-skeleton></div>
|
|
<div class="flex align-items-center px-2" style="height: 25px"><p-skeleton width="60%" height="1.2rem"></p-skeleton></div>
|
|
<div class="flex align-items-center px-2" style="height: 25px"><p-skeleton width="50%" height="1.2rem"></p-skeleton></div>
|
|
<div class="flex align-items-center px-2" style="height: 25px"><p-skeleton width="60%" height="1.2rem"></p-skeleton></div>
|
|
<div class="flex align-items-center px-2" style="height: 25px"><p-skeleton width="50%" height="1.2rem"></p-skeleton></div>
|
|
<div class="flex align-items-center px-2" style="height: 25px"><p-skeleton width="60%" height="1.2rem"></p-skeleton></div>
|
|
</div>
|
|
</template>
|
|
</p-virtualscroller>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script type="module">
|
|
const { createApp, ref, onMounted } = Vue;
|
|
|
|
const App = {
|
|
setup() {
|
|
const basicItems = ref(Array.from({ length: 100000 }).map((_, i) => \`Item #\${i}\`));
|
|
const multiItems = ref(Array.from({ length: 1000 }).map((_, i) => Array.from({ length: 1000 }).map((_j, j) => \`Item #\${i}_\${j}\`)));
|
|
const lazyItems = ref(Array.from({ length: 10000 }));
|
|
const lazyLoading = ref(false);
|
|
const loadLazyTimeout = ref(null);
|
|
|
|
const onLazyLoad = (event) => {
|
|
lazyLoading.value = true;
|
|
|
|
if (loadLazyTimeout.value) {
|
|
clearTimeout(loadLazyTimeout.value);
|
|
}
|
|
|
|
//imitate delay of a backend call
|
|
loadLazyTimeout.value = setTimeout(() => {
|
|
const { first, last } = event;
|
|
const _lazyItems = [...lazyItems.value];
|
|
|
|
for (let i = first; i < last; i++) {
|
|
_lazyItems[i] = \`Item #\${i}\`;
|
|
}
|
|
|
|
lazyItems.value = _lazyItems;
|
|
lazyLoading.value = false;
|
|
|
|
}, Math.random() * 1000 + 250);
|
|
}
|
|
|
|
return { basicItems, multiItems, lazyItems, lazyLoading, onLazyLoad }
|
|
},
|
|
components: {
|
|
"p-virtualscroller": primevue.virtualscroller,
|
|
"p-skeleton": primevue.skeleton
|
|
}
|
|
};
|
|
|
|
createApp(App)
|
|
.use(primevue.config.default)
|
|
.mount("#app");
|
|
<\\/script>
|
|
|
|
<style>
|
|
.virtualscroller-demo .p-virtualscroller {
|
|
height: 200px;
|
|
width: 200px;
|
|
border: 1px solid var(--surface-border);
|
|
}
|
|
|
|
.virtualscroller-demo .p-virtualscroller .scroll-item {
|
|
background-color: var(--surface-card);
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.virtualscroller-demo .p-virtualscroller .custom-scroll-item {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.virtualscroller-demo .p-virtualscroller .odd {
|
|
background-color: var(--surface-ground);
|
|
}
|
|
|
|
.virtualscroller-demo .p-horizontal-scroll .p-virtualscroller-content {
|
|
display: flex;
|
|
flex-direction: row;
|
|
}
|
|
|
|
.virtualscroller-demo .p-horizontal-scroll .scroll-item {
|
|
writing-mode: vertical-lr;
|
|
}
|
|
|
|
.virtualscroller-demo .custom-loading > .p-virtualscroller-loader {
|
|
display: block;
|
|
}
|
|
|
|
</style>
|
|
`
|
|
}
|
|
}
|
|
};
|
|
}
|
|
};
|
|
</script>
|