JSPM

  • Created
  • Published
  • Downloads 3648
  • Score
    100M100P100Q124662F
  • License MIT

Package Exports

  • vue-virtual-scroll-grid
  • vue-virtual-scroll-grid/dist/style.css

Readme

Virtual Scroll Grid for Vue 3

Demo

This is a reusable component for Vue 3 that renders a list with a huge number of items (e.g. 1000+ items) as a grid in a performant way.

Features

  • Use virtual-scrolling / windowing to render the items, so the number of DOM nodes is low.
  • Support using a paginated API to load the items in the background.
  • Support placeholders for unloaded items and loaded items are cached for better performance.
  • Just use CSS grid to style your grid. Minimum styling opinions form the library.

Exmaple

<template>
  <!-- length: The number of items in the list. -->
  <!-- pageSize: The number of items in a page from the item provider (e.g. a backend API). -->
  <!-- pageProvider: The callback that returns a page of items as a promise. -->
  <Grid :length="1000" :pageSize="2" :pageProvider="pageProvider" class="grid">
    <!-- When the item is not loaded, a placeholder is rendered -->
    <template v-slot:placeholder="{ index, style, className }">
      <div :class="[className, 'item']" :style="style">
        Placeholder {{ index }}
      </div>
    </template>

    <!-- Render a loaded item -->
    <template v-slot:default="{ item, style, index, className }">
      <div :class="[className, 'item']" :style="style">
        {{ item }} {{ index }}
      </div>
    </template>
  </Grid>
</template>

<script>
import Grid from "vue-virtual-scroll-grid";

export default {
  name      : "App",
  components: {Grid},
  setup     : () => ({
    // Return items for the given page after a 0-3 second randomly
    pageProvider: (pageNumber, pageSize) =>
      new Promise((resolve) =>
        setTimeout(
          () => resolve(new Array(pageSize).fill("Loaded Item")),
          Math.round(3000 * Math.random())
        )
      ),
  }),
};
</script>

<style>
@import "vue-virtual-scroll-grid/dist/style.css";

body {
  margin: 0;
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

.grid {
  display: grid;
  grid-gap: 20px;
  grid-template-rows: 200px;
  grid-template-columns: repeat(2, 1fr);
  place-items: strech;
}

@media (min-width: 768px) {
  .grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (min-width: 992px) {
  .grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

@media (min-width: 1280px) {
  .grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

@media (min-width: 1440px) {
  .grid {
    grid-template-columns: repeat(5, 1fr);
  }
}

@media (min-width: 1650px) {
  .grid {
    grid-template-columns: repeat(6, 1fr);
  }
}

@media (min-width: 1890px) {
  .grid {
    grid-template-columns: repeat(7, 1fr);
  }
}

@media (min-width: 2530px) {
  .grid {
    grid-template-columns: repeat(8, 1fr);
  }
}

.item {
  box-sizing: border-box;
  border: 1px solid black;
  display: flex;
  flex-flow: row nowrap;
  justify-content: center;
  align-items: center;
}
</style>

Available Props

interface Props {
  // The number of items in the list.
  length: number;
  // The callback that returns a page of items as a promise.
  pageProvider: (pageNumber: number, pageSize: number) => Promise<unknown[]>
  // The number of items in a page from the item provider (e.g. a backend API).
  pageSize: number;
}

Development

  • Setup npm install
  • Run dev server shell npm run dev
  • Lint (type check) shell npm run lint
  • Build the library shell npm run build
  • Build the demo shell npm run build -- --mode=demo
  • Preview the locally built demo shell npm run serve