Vue

by Dominic Myers

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.7.14/vue.min.js"></script>
<div id="app">
    <ol v-if="totalPages > 0" 
        aria-label="Pagination navigation">
        <li v-on:click="paginationOptions.page = 1" 
            aria-label="Go to the first page" 
            title="Go to the first page"
            v-bind:class="{
                'disabled': paginationOptions.page === 1
            }">
            <div class="content">←</div>
        </li>
        <li v-if="totalPages > 3 && paginationOptions.page - 2 >= 1" 
            v-on:click="paginationOptions.page = (paginationOptions.page - 3 < 1) ? 1 : paginationOptions.page - 3" 
            aria-label="Jump three pages backward" 
            title="Jump three pages backward"><div class="content">…</div></li>
        <li v-for="page in pages" 
            v-bind:key="page" 
            v-on:click="paginationOptions.page = page" 
            v-bind:aria-label="(page === paginationOptions.page) ? 'Current page, page ' + page : 'Go to page ' + page" 
            v-bind:aria-current="page === paginationOptions.page" 
            v-bind:title="(page === paginationOptions.page) ? 'Current page, page ' + page : 'Go to page ' + page"
            v-bind:class="{
                'active': page === paginationOptions.page
            }">
            <div class="content">{{page}}</div>
            <span v-if="page === paginationOptions.page" 
                  style="display: none">(current)</span>
        </li>
        <li v-if="totalPages > 3 && paginationOptions.page + 2 <= totalPages" 
            v-on:click="paginationOptions.page = (paginationOptions.page + 3 <= totalPages) ? paginationOptions.page + 3 : totalPages" 
            aria-label="Jump three pages forward" 
            title="Jump three pages forward"><div class="content">…</div></li>
        <li v-on:click="paginationOptions.page = totalPages" 
            aria-label="Go to the last page" 
            title="Go to the last page"
         ...

SCSS

#app{
    margin: 1em;
}
ol {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    margin: 0;
    padding: 0;
    list-style-type: none;
    width: 100%;
    text-align: right;
    li{
        display: inline-block;
        &.disabled {
            cursor: not-allowed;
        }
        .content {
            text-decoration: none;
            font-weight: bold;
            background-color: #fff;
            color: #7f8c8d;
            display: flex;
            align-items: center;
            justify-content: center;
            width: 40px;
            height: 40px;
            margin: 0 3px;
            border: 1px solid #e5e5e5;
        }
        &.active {
            .content {
                border-color: #2c3e50;
                background-color: #2c3e50;
                color: #fff;
                text-decoration: none;
            }
        }
    }
}

JavaScript

new Vue({
	el: "#app",
	data: {
		paginationOptions: {
			page: 2,
			total: 45,
			pageSize: 10
		}
	},
	computed: {
		totalPages() {
			return Math.ceil(this.paginationOptions.total / this.paginationOptions.pageSize);
		},
    pages: () => {
      const returnArray = [];
      if(this.paginationOptions.page === 1){
        for(i = 1, count = 0; i <= this.totalPages && count < 3; i++, count++){
          returnArray.push(i)
        }
      } else {
        if(this.paginationOptions.page === this.totalPages){
          for(let i = this.totalPages, count = 0; i >= 1 && count < 3; i--, count++){
            returnArray.push(i)
          }
          returnArray.reverse();
        } else {
          returnArray.push(this.paginationOptions.page);
          if(this.paginationOptions.page < this.totalPages){
            returnArray.push(this.paginationOptions.page + 1)
          }
          if(this.paginationOptions.page >= 1){
            returnArray.unshift(this.paginationOptions.page - 1)
          }
        }
      }
      return returnArray;
    },
	}
});