vui-design
Version:
A high quality UI Toolkit based on Vue.js
447 lines (404 loc) • 13.7 kB
JavaScript
import VuiSelect from "../select";
import VuiOption from "../option";
import VuiInput from "../input";
import Locale from "../../mixins/locale";
import PropTypes from "../../utils/prop-types";
import is from "../../utils/is";
import range from "../../utils/range";
import getClassNamePrefix from "../../utils/getClassNamePrefix";
export const createProps = () => {
return {
classNamePrefix: PropTypes.string,
small: PropTypes.bool.def(false),
simple: PropTypes.bool.def(false),
align: PropTypes.oneOf(["left", "center", "right"]).def("left"),
total: PropTypes.number.def(0),
showTotal: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
page: PropTypes.number.def(1),
pageSize: PropTypes.number.def(10),
pageSizeOptions: PropTypes.array.def([10, 20, 30, 40]),
showPageSizer: PropTypes.bool.def(false),
showPageElevator: PropTypes.bool.def(false),
prevPageText: PropTypes.string,
nextPageText: PropTypes.string,
hideOnSinglePage: PropTypes.bool.def(false)
};
};
export default {
name: "vui-pagination",
components: {
VuiSelect,
VuiOption,
VuiInput
},
mixins: [
Locale
],
model: {
prop: "page",
event: "input"
},
props: createProps(),
data() {
const { $props: props } = this;
const totalPages = this.getTotalPages(props.total, props.pageSize);
let page = props.page;
if (page < 1) {
page = 1;
}
else if (page > totalPages) {
page = totalPages;
}
return {
state: {
page: page,
pageSize: props.pageSize
}
};
},
computed: {
totalPages() {
const { $props: props, state } = this;
return this.getTotalPages(props.total, state.pageSize);
}
},
watch: {
page(value) {
const { $props: props, state } = this;
const totalPages = this.getTotalPages(props.total, state.pageSize);
let page = value;
if (page < 1) {
page = 1;
}
else if (page > totalPages) {
page = totalPages;
}
this.state.page = page;
},
pageSize(value) {
const { $props: props, state } = this;
const totalPages = this.getTotalPages(props.total, value);
let page = state.page;
if (page > totalPages) {
page = totalPages;
}
this.state.page = page;
this.state.pageSize = value;
}
},
methods: {
getTotalPages(total, pageSize) {
let totalPages = Math.ceil(total / pageSize);
if (totalPages < 1) {
totalPages = 1;
}
return totalPages;
},
handlePrevPage() {
const { state } = this;
const newPage = state.page - 1;
if (newPage < 1) {
return;
}
this.handleChangePage(newPage);
},
handleNextPage() {
const { state } = this;
const newPage = state.page + 1;
if (newPage > this.totalPages) {
return;
}
this.handleChangePage(newPage);
},
handlePrevFivePage() {
const { state } = this;
const newPage = state.page - 5;
if (newPage > 1) {
this.handleChangePage(newPage);
}
else {
this.handleChangePage(1);
}
},
handleNextFivePage() {
const { state } = this;
const newPage = state.page + 5;
if (newPage < this.totalPages) {
this.handleChangePage(newPage);
}
else {
this.handleChangePage(this.totalPages);
}
},
handleInputPage(e) {
const keyCode = e.keyCode;
if (keyCode === 38 || keyCode === 40) {
e.preventDefault();
}
},
handleConfirmPage(e) {
const { state } = this;
const keyCode = e.keyCode;
if (keyCode === 38) {
this.handlePrevPage();
}
else if (keyCode === 40) {
this.handleNextPage();
}
else if (keyCode === 13) {
let value = e.target.value.trim();
if ((/^-?[0-9]\d*$/).test(value)) {
value = Number(value);
if (value === state.page) {
return;
}
if (value < 1) {
value = 1;
}
else if (value > this.totalPages) {
value = this.totalPages;
}
e.target.value = value;
this.handleChangePage(value);
}
else {
e.target.value = state.page;
}
}
},
handleChangePage(page) {
const { state } = this;
if (state.page === page) {
return;
}
this.state.page = page;
this.$emit("input", page);
this.$emit("change", page);
},
handleChangePageSize(pageSize) {
const { $props: props, state } = this;
const totalPages = this.getTotalPages(props.total, pageSize);
let page = state.page;
if (page > totalPages) {
page = totalPages;
}
this.state.pageSize = pageSize;
this.handleChangePage(page);
this.$emit("changePageSize", pageSize);
}
},
render(h) {
const { t: translate, $props: props, state } = this;
const { handlePrevPage, handleNextPage, handlePrevFivePage, handleNextFivePage, handleInputPage, handleConfirmPage, handleChangePage, handleChangePageSize } = this;
// show
const show = props.hideOnSinglePage ? this.totalPages > 1 : true;
// class
const classNamePrefix = getClassNamePrefix(props.classNamePrefix, "pagination");
let classes = {};
classes.el = {
[`${classNamePrefix}`]: true,
[`${classNamePrefix}-small`]: props.small,
[`${classNamePrefix}-simple`]: props.simple,
[`${classNamePrefix}-align-${props.align}`]: props.align
};
// render
const btnPrevPage = {
key:"prevPage",
title: translate("vui.pagination.prevPage"),
className: {
[`${classNamePrefix}-button`]: true,
[`${classNamePrefix}-button-prev`]: true,
[`${classNamePrefix}-button-disabled`]: state.page === 1
},
children: props.prevPageText || (
<svg viewBox="0 0 10 10" class={`${classNamePrefix}-button-arrow`}>
<path d="M3.6,5l4.1-4.1c0.2-0.2,0.2-0.6,0-0.8c-0.2-0.2-0.6-0.2-0.8,0L2.4,4.6c-0.2,0.2-0.2,0.6,0,0.8l4.5,4.4 c0.2,0.2,0.6,0.2,0.8,0c0.2-0.2,0.2-0.6,0-0.8L3.6,5z" />
</svg>
),
handler: handlePrevPage
};
const btnNextPage = {
key:"nextPage",
title: translate("vui.pagination.nextPage"),
className: {
[`${classNamePrefix}-button`]: true,
[`${classNamePrefix}-button-next`]: true,
[`${classNamePrefix}-button-disabled`]: state.page === this.totalPages
},
children: props.nextPageText || (
<svg viewBox="0 0 10 10" class={`${classNamePrefix}-button-arrow`}>
<path d="M6.4,5L2.4,0.9c-0.2-0.2-0.2-0.6,0-0.8c0.2-0.2,0.6-0.2,0.8,0l4.5,4.4c0.2,0.2,0.2,0.6,0,0.8L3.2,9.8c-0.2,0.2-0.6,0.2-0.8,0c-0.2-0.2-0.2-0.6,0-0.8L6.4,5z" />
</svg>
),
handler: handleNextPage
};
const btnPrevFivePage = {
key:"prevFivePage",
title: translate("vui.pagination.prevFivePage"),
className: {
[`${classNamePrefix}-ellipsis`]: true
},
icon: (
<i class={`${classNamePrefix}-ellipsis-icon`}>•••</i>
),
arrow: (
<svg class={`${classNamePrefix}-ellipsis-arrow`} viewBox="0 0 10 10">
<path d="M1.4,5l4.1-4.1c0.2-0.2,0.2-0.6,0-0.8c-0.2-0.2-0.6-0.2-0.8,0L0.2,4.6c-0.2,0.2-0.2,0.6,0,0.8l4.5,4.4c0.2,0.2,0.6,0.2,0.8,0c0.2-0.2,0.2-0.6,0-0.8L1.4,5z M5.8,5l4.1-4.1c0.2-0.2,0.2-0.6,0-0.8C9.6-0.1,9.3-0.1,9,0.2L4.6,4.6c-0.2,0.2-0.2,0.6,0,0.8L9,9.8c0.2,0.2,0.6,0.2,0.8,0s0.2-0.6,0-0.8L5.8,5z" />
</svg>
),
handler: handlePrevFivePage
};
const btnNextFivePage = {
key:"nextFivePage",
title: translate("vui.pagination.nextFivePage"),
className: {
[`${classNamePrefix}-ellipsis`]: true
},
icon: (
<i class={`${classNamePrefix}-ellipsis-icon`}>•••</i>
),
arrow: (
<svg class={`${classNamePrefix}-ellipsis-arrow`} viewBox="0 0 10 10">
<path d="M8.6,5L4.6,0.9c-0.2-0.2-0.2-0.6,0-0.8c0.2-0.2,0.6-0.2,0.8,0l4.5,4.4c0.2,0.2,0.2,0.6,0,0.8L5.4,9.8c-0.2,0.2-0.6,0.2-0.8,0c-0.2-0.2-0.2-0.6,0-0.8L8.6,5z M4.2,5L0.2,0.9c-0.2-0.2-0.2-0.6,0-0.8c0.2-0.2,0.6-0.2,0.8,0l4.5,4.4c0.2,0.2,0.2,0.6,0,0.8L1,9.8c-0.2,0.2-0.6,0.2-0.8,0c-0.2-0.2-0.2-0.6,0-0.8L4.2,5z" />
</svg>
),
handler: handleNextFivePage
};
if (props.simple) {
return (
<ul v-show={show} class={classes.el}>
<li key={btnPrevPage.key} title={btnPrevPage.title} class={btnPrevPage.className} onClick={btnPrevPage.handler}>
{btnPrevPage.children}
</li>
<li class={`${classNamePrefix}-elevator`}>
<VuiInput size="small" value={state.page} onKeydown={handleInputPage} onKeyup={handleConfirmPage} />
{
props.showTotal && (
<span>/ {this.totalPages}</span>
)
}
</li>
<li key={btnNextPage.key} title={btnNextPage.title} class={btnNextPage.className} onClick={btnNextPage.handler}>
{btnNextPage.children}
</li>
</ul>
);
}
else {
let totalText;
if (props.showTotal) {
if (is.function(props.showTotal)) {
let rangeFrom = (state.page - 1) * state.pageSize + 1;
let rangeTo = state.page * state.pageSize;
if (props.total < 1) {
rangeFrom = 0;
}
if (rangeTo > props.total) {
rangeTo = props.total;
}
totalText = props.showTotal(props.total, [rangeFrom, rangeTo]);
}
else {
totalText = `${translate("vui.pagination.total")} ${props.total} ${translate("vui.pagination." + (props.total > 1 ? "items" : "item"))}`;
}
}
let items = [];
if (this.totalPages < 9) {
items = range(1, this.totalPages + 1);
}
else {
if (state.page < 6) {
items = [1, 2, 3, 4, 5, 6, "nextFivePage", this.totalPages];
}
else if (state.page > this.totalPages - 5) {
items = [1, "prevFivePage", this.totalPages - 5, this.totalPages - 4, this.totalPages - 3, this.totalPages - 2, this.totalPages - 1, this.totalPages];
}
else {
items = [1, "prevFivePage", state.page - 2, state.page - 1, state.page, state.page + 1, state.page + 2, "nextFivePage", this.totalPages];
}
}
return (
<ul v-show={show} class={classes.el}>
{
props.showTotal && (
<li key={"total"} class={`${classNamePrefix}-total`}>
{totalText}
</li>
)
}
<li key={btnPrevPage.key} title={btnPrevPage.title} class={btnPrevPage.className} onClick={btnPrevPage.handler}>
{btnPrevPage.children}
</li>
{
items.map(item => {
if (item === "prevFivePage") {
let attrs = btnPrevFivePage;
return (
<li key={attrs.key} title={attrs.title} class={attrs.className} onClick={attrs.handler}>
{attrs.icon}
{attrs.arrow}
</li>
);
}
else if (item === "nextFivePage") {
let attrs = btnNextFivePage;
return (
<li key={attrs.key} title={attrs.title} class={attrs.className} onClick={attrs.handler}>
{attrs.icon}
{attrs.arrow}
</li>
);
}
else {
let attrs = {
key: item,
title: item,
className: {
[`${classNamePrefix}-item`]: true,
[`${classNamePrefix}-item-active`]: state.page === item
},
children: item,
handler: e => handleChangePage(item)
};
return (
<li key={attrs.key} title={attrs.title} class={attrs.className} onClick={attrs.handler}>
{attrs.children}
</li>
);
}
})
}
<li key={btnNextPage.key} title={btnNextPage.title} class={btnNextPage.className} onClick={btnNextPage.handler}>
{btnNextPage.children}
</li>
{
props.showPageSizer && (
<li key={"sizer"} class={`${classNamePrefix}-sizer`}>
<VuiSelect size={props.small ? "small" : undefined} value={state.pageSize} onInput={handleChangePageSize}>
{
props.pageSizeOptions.map(option => {
return (
<VuiOption key={option} value={option}>
{option} {translate("vui.pagination.pageSize")}
</VuiOption>
);
})
}
</VuiSelect>
</li>
)
}
{
props.showPageElevator && (
<li key={"elevator"} class={`${classNamePrefix}-elevator`}>
<span>{translate("vui.pagination.goto")}</span>
<VuiInput size={props.small ? "small" : undefined} value={state.page} onKeydown={handleInputPage} onKeyup={handleConfirmPage} />
<span>{translate("vui.pagination.page")}</span>
</li>
)
}
</ul>
);
}
}
};