v3 composition api paginate

by dapo

HTML

<script src="https://gistcdn.githack.com/dasDaniel/f3cebc1274339055729c6e887ca8d2ad/raw/32f92b584f2f1df6aff813d75684dc38a666f32e/vue.global.js"></script>
<div id="app"></div>

<template id="appTemplate">
  <div>
    <div>
      <div class="paginate-item" v-for="i in pagin8.range" :style="{width:(100/pagin8.pageSize)+'%'}">{{i}}</div>
    </div>

    <div style="text-align:center;">
      <button @click="pagin8.gotoFirst">◄◄</button>
      <button @click="pagin8.gotoPrev">◄</button>
      {{pagin8.currentPageNum}} / {{pagin8.numPages}}
      <button @click="pagin8.gotoNext">►</button>
      <button @click="pagin8.gotoLast">►►</button>
    </div>

    Page Size: <input v-model="pagin8.pageSize" type="number">

    <pre>{{ {pageSize: pageSize, currentIndex: pagin8.currentIndex, currentPageNum: pagin8.currentPageNum, total: pagin8.total, numPages: pagin8.numPages, range: pagin8.range} }}</pre>
  </div>
</template>

CSS

body {
  font-family: Verdana, Arial, Helvetica, sans-serif;
}

.paginate-item{
  display: inline-block;
  width: 25%;
  box-sizing: border-box;
  border: 1px solid #CCC;
  padding: 10px;
  margin: 0 0 10px 0;
}

button {
  width: 42px;
  height: 42px;
  border-radius: 4px;
  border: 1px solid #eee;
  margin: 2px;
  box-shadow: 2px 2px 2px #ccc;
  background-color: #f9f9f9;
  color: #555;
  transition: all 0.125s;
}

button:hover {
  box-shadow: 1px 1px 1px #bbb;
  background-color: #eee;
  color: #000;
  transition: all 0.125s;
}

JavaScript

const {
  reactive,
  computed,
  ref,
  onMounted,
  watch,
  isRef
} = Vue;


/**
 *
 * @param {Object} options
 * @param {ref|number} options.pageSize
 * @param {ref|number} options.currentIndex 0 based index
 * @param {ref|array} options.array
 */
const pagin8r = options => {
  // setup props
  const pageSize = isRef(options.pageSize)
    ? options.pageSize
    : ref(options.pageSize);
  const currentIndex = isRef(options.currentIndex)
    ? options.currentIndex
    : ref(options.currentIndex);
  const array = isRef(options.array) ? options.array : ref(options.array);

  // create computed
  const total = computed(() => array.value.length);
  const currentPageNum = computed(() => currentIndex.value + 1);
  const numPages = computed(() => Math.ceil(total.value / pageSize.value));
  const range = computed(() =>
    array.value.slice(
      currentIndex.value * pageSize.value,
      (currentIndex.value + 1) * pageSize.value
    )
  );

  // create methods
  const gotoNext = () => {
    currentIndex.value = Math.min(currentIndex.value + 1, numPages.value - 1);
  };
  const gotoPrev = () => {
    currentIndex.value = Math.max(0, currentIndex.value - 1);
  };
  const gotoFirst = () => {
    currentIndex.value = 0;
  };
  const gotoLast = () => {
    currentIndex.value = numPages.value - 1;
  };

  // return props, computed, and method variables
  return {
    array,
    pageSize,
    currentIndex,
    currentPageNum,
    total,
    numPages,
    range,
    gotoNext,
    gotoPrev,
    gotoFirst,
    gotoLast
  };
};

const App = {
  template: document.getElementById("appTemplate").innerHTML,
  setup() {
    // create an array
    let array = [...Array(50)].map((x, n) => n);
    let pageSize = ref(4);
    let currentIndex = 0;
    // setup/config the pagination functionality
    const pagin8 = pagin8r({
      pageSize,
      currentIndex,
      array
    });
    // pass entire pagination to template
    return {
      pagin8,
      pageSize
    };
  }
};

const app =...