Last active
March 6, 2018 04:15
-
-
Save gerardojbaez/eebd84f1d919afe6e016ac6b8712d19a to your computer and use it in GitHub Desktop.
Laravel's style pagination using Vue.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- Inspired by Laravel Paginator. © Gerardo J. Báez <g@gerardobaez.com> 2017. Released with MIT License. --> | |
<template> | |
<ul class="pagination"> | |
<li v-if="hasPrev()"><a href="javascript:void(0)" @click="prev()" class="prev">Previous</a></li> | |
<template v-for="page in getPageSlider()"> | |
<li v-if="isNaN(page) === false"> | |
<a href="javascript:void(0)" :class="{ 'active': currentPage == page }" @click="goToPage(page)">{{ page }}</a> | |
</li> | |
<li v-else class="blank">...</li> | |
</template> | |
<li v-if="hasNext()"><a href="javascript:void(0)" @click="next()" class="next">Next</a></li> | |
</ul> | |
</template> | |
<script> | |
export default { | |
props: { | |
currentPage: { | |
type: Number, | |
required: true | |
}, | |
total: { | |
type: Number, | |
required: true | |
}, | |
perPage: { | |
type: Number, | |
required: true | |
} | |
}, | |
methods: { | |
/** | |
* Navigate to the next page (if there's one). | |
*/ | |
next () { | |
if (this.hasNext()) { | |
this.goToPage(this.currentPage + 1) | |
} | |
}, | |
/** | |
* Navigate to the previous page (if there's one). | |
*/ | |
prev () { | |
if (this.hasPrev()) { | |
this.goToPage(this.currentPage - 1) | |
} | |
}, | |
/** | |
* Determine if there's a previous page. | |
* | |
* @return {Boolean} | |
*/ | |
hasPrev () { | |
return this.currentPage > 1 | |
}, | |
/** | |
* Determine if there's a next page. | |
* | |
* @return {Boolean} | |
*/ | |
hasNext () { | |
return this.currentPage < this.getLastPage() | |
}, | |
/** | |
* Get pagination slider. | |
* | |
* @return {Array} | |
*/ | |
getPageSlider () { | |
if (this.getLastPage() < 10) { | |
// No need to split or build an advance pagination window, let's just use a simple one. | |
return this.getSmallSlider() | |
} | |
// Since the pagination can get big (20+ pages), we need to properly split and create small | |
// "windows" of page links. For example: 1, 2, ..., 3, 4, 5, 6, ..., 22, 23. | |
return _.filter(this.getFullSlider(), page => { | |
// Keep only numbers higher than 1 and the separator "...". | |
return page > 0 || page === '...' | |
}) | |
}, | |
/** | |
* Get small slider. | |
* | |
* @return {Array} | |
*/ | |
getSmallSlider () { | |
return this.getPageRange(1, this.getLastPage()) | |
}, | |
/** | |
* Get full pagination slider. | |
* | |
* @return {Array} | |
*/ | |
getFullSlider () { | |
if (this.currentPage < 6) { | |
// Don't split the beginning of the page range since we are close to it... This will return | |
// a range similar to 1, 2, 3, 4, 5, 6, ... 22 | |
return this.getPageRange(1, 6).concat(['...'], this.getFinish()) | |
} else if (this.currentPage > (this.getLastPage() - 4)) { | |
// Don't split the end of the page range since we are close to it... This will return | |
// a range similar to 1, 2, ..., 17, 18, 19, 20, 21, 22, 23 | |
return this.getStart().concat(['...'], this.getPageRange(this.getLastPage() - 6, this.getLastPage())) | |
} | |
// Split the beginning and the end of the page range... This will return | |
// a range similar to 1, 2, ..., 3, 4, 5, 6, ..., 22, 23 | |
return this.getStart().concat(['...'], this.getAdjacent(), ['...'], this.getFinish()) | |
}, | |
/** | |
* Get the start of the pagination window. | |
* | |
* @return {Array} | |
*/ | |
getStart () { | |
return this.getPageRange(1, 2) | |
}, | |
/** | |
* Get the end of the pagination window. | |
* | |
* @return {Array} | |
*/ | |
getFinish () { | |
return this.getPageRange(this.getLastPage() - 1, this.getLastPage()) | |
}, | |
/** | |
* Get the adjacent pages. | |
* | |
* @return {Array} | |
*/ | |
getAdjacent () { | |
return this.getPageRange(this.currentPage - 2, this.currentPage + 2) | |
}, | |
/** | |
* Get the last page (or page count, it's the same). | |
* | |
* @return {Number} | |
*/ | |
getLastPage () { | |
return Math.ceil(this.total / this.perPage) | |
}, | |
/** | |
* Build page range. | |
* | |
* @param {Number} start | |
* @param {Number} end | |
* @return {Array} | |
*/ | |
getPageRange (start, end) { | |
return _.range(start, end + 1) | |
}, | |
/** | |
* Navigate to a specific page number. | |
* | |
* @param {Number} page | |
*/ | |
goToPage (page) { | |
// Make sure to keep query parameters... needed to maintain the search between pages. | |
let query = _.clone(this.$route.query) | |
// Preserve selected page in search query. Useful to share a specific result page. | |
query.page = page | |
// Update the route | |
this.$router.push({ | |
query | |
}) | |
} | |
} | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment