@inkline/inkline
Version:
Inkline is the Vue.js UI/UX Library built for creating your next design system
230 lines (212 loc) • 6.82 kB
text/typescript
import { defineComponent } from 'vue';
import {
defaultPropValue,
colorVariantClass,
sizePropValidator
} from '@inkline/inkline/mixins';
import { breakpointKeys, breakpoints } from '@inkline/inkline/constants';
import { debounce } from '@inkline/inkline/helpers';
import { Classes } from '@inkline/inkline/types';
/**
* Slot for previous button content
* @name previous
* @kind slot
*/
/**
* Slot for next button content
* @name next
* @kind slot
*/
const componentName = 'IPagination';
export default defineComponent({
name: componentName,
props: {
/**
* The aria-label of the pagination
* @type String
* @default Pagination
* @name ariaLabel
*/
ariaLabel: {
type: String,
default: 'Pagination'
},
/**
* The color variant of the pagination
* @type light | dark
* @default light
* @name color
*/
color: {
type: String,
default: defaultPropValue<string>(componentName, 'color')
},
/**
* The number of items per page to be displayed
* @type Number
* @default 20
* @name itemsPerPage
*/
itemsPerPage: {
type: Number,
default: 20
},
/**
* The total number of items
* @type Number
* @default 0
* @name itemsTotal
*/
itemsTotal: {
type: Number,
default: 0
},
/**
* The maximum number of pagination buttons to show on each breakpoint
* @type Number | Object
* @name limit
* @default { xs: 3, sm: 5 }
*/
limit: {
type: [Number, Object],
default (): { [key: string]: number } {
return {
xs: 3,
sm: 5
};
}
},
/**
* Display the quick link buttons
* @type Boolean
* @default false
* @name quickLink
*/
quickLink: {
type: Boolean,
default: false
},
/**
* Used to determine the current page
* @type Number
* @default 1
* @name modelValue
*/
modelValue: {
type: Number,
default: 1
},
/**
* The size variant of the pagination
* @type sm | md | lg
* @default md
* @name size
*/
size: {
type: String,
default: defaultPropValue<string>(componentName, 'size'),
validator: sizePropValidator
}
},
emits: [
/**
* Event emitted for setting the modelValue
* @event update:modelValue
*/
'update:modelValue'
],
data (): { pageLimit: number } {
return {
pageLimit: 5
};
},
computed: {
classes (): Classes {
return {
...colorVariantClass(this),
[`-${(this as any).size}`]: Boolean((this as any).size)
};
},
pageCount (): number {
return Math.ceil((this as any).itemsTotal / (this as any).itemsPerPage);
},
showQuickPrevious (): boolean {
return this.pageCount > this.pageLimit && // Has more pages than limit
(this as any).modelValue > this.pageLimit - (this.pageLimit - 1) / 2; // Active page is after limit - (limit - 1) / 2
},
showQuickNext (): boolean {
return this.pageCount > this.pageLimit && // Has more pages than limit
(this as any).modelValue < this.pageCount - (this.pageLimit - 1) / 2; // Active page is before pageCount - (limit - 1) / 2
},
pages (): number[] {
const pages = [];
if (this.showQuickPrevious && !this.showQuickNext) {
const startPage = this.pageCount - (this.pageLimit - 2);
for (let i = startPage; i < this.pageCount; i++) {
pages.push(i);
}
} else if (!this.showQuickPrevious && this.showQuickNext) {
for (let i = 2; i < this.pageLimit; i++) {
pages.push(i);
}
} else if (this.showQuickPrevious && this.showQuickNext) {
const offset = Math.floor(this.pageLimit / 2) - 1;
for (let i = (this as any).modelValue - offset; i <= (this as any).modelValue + offset; i++) {
pages.push(i);
}
} else {
for (let i = 2; i < this.pageCount; i++) {
pages.push(i);
}
}
return pages;
}
},
created () {
(this as any).debouncedOnWindowResize = debounce(this.onWindowResize, 250);
if (typeof window !== 'undefined') {
window.addEventListener('resize', (this as any).debouncedOnWindowResize);
this.onWindowResize();
}
},
unmounted () {
if (typeof window !== 'undefined') {
window.removeEventListener('resize', (this as any).debouncedOnWindowResize);
}
},
methods: {
next () {
if ((this as any).modelValue === this.pageCount) { return; }
this.onClick((this as any).modelValue + 1);
},
quickNext () {
if (!(this as any).quickLink) { return; }
const jumpTo = (this as any).modelValue + (this.pageLimit - 2);
this.onClick(jumpTo > this.pageCount ? this.pageCount : jumpTo);
},
previous () {
if ((this as any).modelValue === 1) { return; }
this.onClick((this as any).modelValue - 1);
},
quickPrevious () {
if (!(this as any).quickLink) { return; }
const jumpTo = (this as any).modelValue - (this.pageLimit - 2);
this.onClick(jumpTo < 1 ? 1 : jumpTo);
},
onClick (item: number) {
this.$emit('update:modelValue', item);
},
onWindowResize () {
if (typeof (this as any).limit === 'number') {
this.pageLimit = (this as any).limit;
return this.pageLimit;
}
for (const breakpointKey of breakpointKeys.slice().reverse()) {
if ((this as any).limit.hasOwnProperty(breakpointKey) && (typeof window !== 'undefined' && window.innerWidth >= breakpoints[breakpointKey][0])) {
this.pageLimit = (this as any).limit[breakpointKey];
return this.pageLimit;
}
}
}
}
});