Permalink
Browse files

perf(pagination): Move common code to pagination mixin (#1069)

* Greate pagination mixin

* Make mixin available

* [pagination-nav] Use pagination mixin

* [pagination] Use pagination mixin

* pagination-mixin ESLint

* [pagination] import isVisible dom util
  • Loading branch information...
tmorehouse committed Sep 16, 2017
1 parent ac7c506 commit 0d41e830974867fac21969bebb059174ab70c0c5
Showing with 303 additions and 542 deletions.
  1. +2 −256 lib/components/pagination-nav.vue
  2. +31 −286 lib/components/pagination.vue
  3. +2 −0 lib/mixins/index.js
  4. +268 −0 lib/mixins/pagination.js
@@ -121,20 +121,8 @@
<script>
import bLink, { pickLinkProps } from './link';
import { from as arrayFrom } from '../utils/array'
import range from '../utils/range'
import { assign } from '../utils/object';
import { isVisible, isDisabled, selectAll, getAttr } from '../utils/dom';
// Make an array of N to N+X
function makePageArray(startNum, numPages) {
return range(numPages).map(function(value, index){
return { number: index + startNum, className: null };
});
}
// Threshold of limit size when we start/stop showing ellipsis
const ELLIPSIS_THRESHOLD = 3;
import { paginationMixin } from '../mixins';
// Props needed for router links
const routerProps = pickLinkProps('activeClass','exactActiveClass','append','exact','replace','target','rel');
@@ -164,193 +152,14 @@ const props = assign(
default: null
}
},
// pagination common props
{
disabled: {
type: Boolean,
default: false
},
value: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 5
},
size: {
type: String,
default: 'md'
},
align: {
type: String,
default: 'left'
},
hideGotoEndButtons: {
type: Boolean,
default: false
},
ariaLabel: {
type: String,
default: 'Pagination'
},
labelFirstPage: {
type: String,
default: 'Goto first page'
},
firstText: {
type: String,
default: '&laquo;'
},
labelPrevPage: {
type: String,
default: 'Goto previous page'
},
prevText: {
type: String,
default: '&lsaquo;'
},
labelNextPage: {
type: String,
default: 'Goto next page'
},
nextText: {
type: String,
default: '&rsaquo;'
},
labelLastPage: {
type: String,
default: 'Goto last page'
},
lastText: {
type: String,
default: '&raquo;'
},
labelPage: {
type: String,
default: 'Goto page'
},
hideEllipsis: {
type: Boolean,
default: false
},
ellipsisText: {
type: String,
default: '&hellip;'
}
},
// Router specific props
routerProps
);
export default {
components: { bLink },
data() {
return {
showFirstDots: false,
showLastDots: false,
currentPage: this.value
};
},
mixins: [ paginationMixin ],
props,
watch: {
currentPage(newPage, oldPage) {
if (newPage !== oldPage) {
this.$emit('input', newPage);
}
},
value(newValue, oldValue) {
if (newValue !== oldValue) {
this.currentPage = newValue;
}
}
},
computed: {
btnSize() {
return this.size ? `pagination-${this.size}` : '';
},
alignment() {
if (this.align === 'center') {
return 'justify-content-center';
} else if (this.align === 'end' || this.align === 'right') {
return 'justify-content-end';
}
return '';
},
pageList() {
// Sanity checks
if (this.currentPage > this.numberOfPages) {
this.currentPage = this.numberOfPages;
} else if (this.currentPage < 1) {
this.currentPage = 1;
}
// - Hide first ellipsis marker
this.showFirstDots = false;
// - Hide last ellipsis marker
this.showLastDots = false;
let numLinks = this.limit;
let startNum = 1;
if (this.numberOfPages <= this.limit) {
// Special Case: Less pages available than the limit of displayed pages
numLinks = this.numberOfPages;
} else if (this.currentPage < (this.limit - 1) && this.limit > ELLIPSIS_THRESHOLD) {
// We are near the beginning of the page list
if (!this.hideEllipsis) {
numLinks = this.limit - 1;
this.showLastDots = true;
}
} else if ((this.numberOfPages - this.currentPage + 2) < this.limit && this.limit > ELLIPSIS_THRESHOLD) {
// We are near the end of the list
if (!this.hideEllipsis) {
this.showFirstDots = true;
numLinks = this.limit - 1;
}
startNum = this.numberOfPages - numLinks + 1;
} else {
// We are somewhere in the middle of the page list
if (this.limit > ELLIPSIS_THRESHOLD && !this.hideEllipsis) {
this.showFirstDots = true;
this.showLastDots = true;
numLinks = this.limit - 2;
}
startNum = this.currentPage - Math.floor(numLinks / 2);
}
// Sanity checks
if (startNum < 1) {
startNum = 1;
} else if (startNum > (this.numberOfPages - numLinks)) {
startNum = this.numberOfPages - numLinks + 1;
}
// Generate list of page numbers
const pages = makePageArray(startNum, numLinks);
// We limit to a total of 3 page buttons on small screens
// Ellipsis will also be hidden on small screens
if (pages.length > 3) {
const idx = this.currentPage - startNum;
if (idx === 0) {
// Keep leftmost 3 buttons visible
for (let i = 3; i < pages.length; i++) {
pages[i].className = 'd-none d-sm-flex';
}
} else if (idx === pages.length - 1) {
// Keep rightmost 3 buttons visible
for (let i = 0; i < pages.length - 3; i++) {
pages[i].className = 'd-none d-sm-flex';
}
} else {
// hide left button(s)
for (let i = 0; i < idx - 1; i++) {
pages[i].className = 'd-none d-sm-flex';
}
// hide right button(s)
for (let i = pages.length - 1; i > idx + 1; i--) {
pages[i].className = 'd-none d-sm-flex';
}
}
}
return pages;
}
},
methods: {
onClick(pageNum) {
this.currentPage = pageNum;
@@ -387,69 +196,6 @@ export default {
});
}
return props;
},
isActive(pagenum) {
return pagenum === this.currentPage;
},
pageItemClasses(page) {
return [
'page-item',
this.disabled ? 'disabled' : '',
this.isActive(page.number) ? 'active' : '',
page.className
];
},
pageLinkClasses(page) {
return [
'page-link',
this.disabled ? 'disabled' : '',
this.isActive(page.number) ? 'active' : ''
];
},
getButtons() {
// Return only buttons that are visible
return selectAll('a.page-link', this.$el).filter(btn => isVisible(btn));
},
setBtnFocus(btn) {
this.$nextTick(() => {
btn.focus();
});
},
focusFirst() {
const btn = this.getButtons().find(el => !isDisabled(el));
if (btn && btn.focus && btn !== document.activeElement) {
this.setBtnFocus(btn);
}
},
focusLast() {
const btn = this.getButtons().reverse().find(el => !isDisabled(el));
if (btn && btn.focus && btn !== document.activeElement) {
this.setBtnFocus(btn);
}
},
focusCurrent() {
const btn = this.getButtons().find(el => parseInt(getAttr(el, 'aria-posinset'), 10) === this.currentPage);
if (btn && btn.focus) {
this.setBtnFocus(btn);
} else {
// Fallback if current page is not in button list
this.focusFirst();
}
},
focusPrev() {
const buttons = this.getButtons();
const idx = buttons.indexOf(document.activeElement);
if (idx > 0 && !isDisabled(buttons[idx - 1]) && buttons[idx - 1].focus) {
this.setBtnFocus(buttons[idx - 1]);
}
},
focusNext() {
const buttons = this.getButtons();
const idx = buttons.indexOf(document.activeElement);
const cnt = buttons.length - 1;
if (idx < cnt && !isDisabled(buttons[idx + 1]) && buttons[idx + 1].focus) {
this.setBtnFocus(buttons[idx + 1]);
}
}
}
};
Oops, something went wrong.

0 comments on commit 0d41e83

Please sign in to comment.