bootstrap-vue-next
Version:
Seamless integration of Vue 3, Bootstrap 5, and TypeScript for modern, type-safe UI development
440 lines (439 loc) • 16 kB
JavaScript
import { z as useToNumber } from "./dist-B10a-gZ8.mjs";
import { n as getActiveElement } from "./dom-AhkaSoh8.mjs";
import { t as useDefaults } from "./useDefaults-BKgBaqOV.mjs";
import { n as BvEvent } from "./classes-B0E5Y78Y.mjs";
import "./constants-BSIK14yA.mjs";
import "./src/types/index.mjs";
import { t as useAlignment } from "./useAlignment-BiCREohQ.mjs";
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createTextVNode, defineComponent, mergeModels, mergeProps, nextTick, normalizeClass, openBlock, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useModel, useTemplateRef, watch, withCtx } from "vue";
//#region src/components/BPagination/BPagination.vue?vue&type=script&setup=true&lang.ts
var _hoisted_1 = ["aria-disabled", "aria-label"];
var _hoisted_2 = ["displayIndex"];
var DEFAULT_PER_PAGE = 20;
var DEFAULT_TOTAL_ROWS = 0;
//#endregion
//#region src/components/BPagination/BPagination.vue
var BPagination_default = /* @__PURE__ */ defineComponent({
__name: "BPagination",
props: /* @__PURE__ */ mergeModels({
align: { default: "start" },
ariaControls: { default: void 0 },
ariaLabel: { default: "Pagination" },
disabled: {
type: Boolean,
default: false
},
ellipsisClass: { default: void 0 },
ellipsisText: { default: "…" },
firstClass: { default: void 0 },
firstNumber: {
type: Boolean,
default: false
},
firstText: { default: "«" },
noEllipsis: {
type: Boolean,
default: false
},
noGotoEndButtons: {
type: Boolean,
default: false
},
labelFirstPage: { default: "Go to first page" },
labelLastPage: { default: "Go to last page" },
labelNextPage: { default: "Go to next page" },
labelPage: { default: "Go to page" },
labelPrevPage: { default: "Go to previous page" },
lastClass: { default: void 0 },
lastNumber: {
type: Boolean,
default: false
},
lastText: { default: "»" },
limit: { default: 5 },
nextClass: { default: void 0 },
nextText: { default: "›" },
pageClass: { default: void 0 },
perPage: { default: DEFAULT_PER_PAGE },
pills: {
type: Boolean,
default: false
},
prevClass: { default: void 0 },
prevText: { default: "‹" },
size: { default: void 0 },
totalRows: { default: DEFAULT_TOTAL_ROWS }
}, {
"modelValue": { default: 1 },
"modelModifiers": {}
}),
emits: /* @__PURE__ */ mergeModels(["page-click"], ["update:modelValue"]),
setup(__props, { emit: __emit }) {
const ELLIPSIS_THRESHOLD = 3;
const FIRST_BUTTON = -1;
const PREV_BUTTON = -2;
const NEXT_BUTTON = -3;
const LAST_BUTTON = -4;
const FIRST_ELLIPSIS = -5;
const LAST_ELLIPSIS = -6;
const props = useDefaults(__props, "BPagination");
const emit = __emit;
const modelValue = useModel(__props, "modelValue");
const pageElements = useTemplateRef("_pageElements");
const limitNumber = useToNumber(() => props.limit, {
nanToZero: true,
method: "parseInt"
});
const perPageNumber = useToNumber(() => props.perPage, {
nanToZero: true,
method: "parseInt"
});
const totalRowsNumber = useToNumber(() => props.totalRows, {
nanToZero: true,
method: "parseInt"
});
const modelValueNumber = useToNumber(modelValue, {
nanToZero: true,
method: "parseInt"
});
const perPageSanitized = computed(() => Math.max(perPageNumber.value || DEFAULT_PER_PAGE, 1));
const totalRowsSanitized = computed(() => Math.max(totalRowsNumber.value || DEFAULT_TOTAL_ROWS, 0));
const numberOfPages = computed(() => Math.ceil(totalRowsSanitized.value / perPageSanitized.value));
const computedFill = computed(() => props.align === "fill");
const alignment = useAlignment(computed(() => props.align === "fill" ? "start" : props.align));
const isActivePage = (pageNumber) => pageNumber === computedModelValue.value;
const getTabIndex = (num) => props.disabled ? null : isActivePage(num) ? "0" : "-1";
const checkDisabled = (num) => props.disabled || isActivePage(num) || computedModelValue.value < 1 || num < 1 || num > numberOfPages.value;
const firstDisabled = computed(() => checkDisabled(1));
const prevDisabled = computed(() => checkDisabled(computedModelValue.value - 1));
const lastDisabled = computed(() => checkDisabled(numberOfPages.value));
const nextDisabled = computed(() => checkDisabled(computedModelValue.value + 1));
const getBaseButtonProps = ({ page, classVal, disabled, slotName, textValue, tabIndex, label, position, isActive, hidden, isSmHidden }) => ({
li: {
"class": [
"page-item",
{
"active": isActive,
disabled,
"bv-d-sm-down-none": isSmHidden,
"flex-fill": computedFill.value,
"d-flex": computedFill.value && !disabled
},
classVal
],
"role": "presentation",
"aria-hidden": hidden
},
button: {
"is": disabled ? "span" : "button",
"class": [
"page-link",
"text-center",
{ "flex-grow-1": !disabled && computedFill.value }
],
"aria-label": label,
"aria-controls": props.ariaControls || void 0,
"aria-disabled": disabled ? true : void 0,
"aria-posinset": position,
"aria-setsize": position ? numberOfPages.value : void 0,
"role": "menuitem",
"type": disabled ? void 0 : "button",
"tabindex": disabled ? void 0 : tabIndex
},
text: {
name: slotName,
active: isActive,
value: textValue ?? page,
page,
disabled,
index: page - 1,
content: textValue ? void 0 : String(page)
},
clickHandler: (e) => pageClick(e, page)
});
const getButtonProps = ({ page, classVal, disabled, slotName, textValue, label }) => getBaseButtonProps({
page,
classVal,
disabled,
slotName,
textValue,
label,
tabIndex: "-1"
});
const getPageButtonProps = (page, isSmHidden) => getBaseButtonProps({
page,
disabled: props.disabled,
classVal: props.pageClass,
slotName: "page",
label: props.labelPage ? `${props.labelPage} ${page}` : void 0,
tabIndex: getTabIndex(page) ?? void 0,
position: page,
isActive: isActivePage(page),
isSmHidden
});
const firstButtonProps = computed(() => getButtonProps({
page: 1,
disabled: firstDisabled.value,
classVal: props.firstClass,
slotName: "first-text",
textValue: props.firstText,
label: props.labelFirstPage
}));
const prevButtonProps = computed(() => getButtonProps({
page: Math.max(computedModelValue.value - 1, 1),
disabled: prevDisabled.value,
classVal: props.prevClass,
slotName: "prev-text",
textValue: props.prevText,
label: props.labelPrevPage
}));
const nextButtonProps = computed(() => getButtonProps({
page: Math.min(computedModelValue.value + 1, numberOfPages.value),
disabled: nextDisabled.value,
classVal: props.nextClass,
slotName: "next-text",
textValue: props.nextText,
label: props.labelNextPage
}));
const lastButtonProps = computed(() => getButtonProps({
page: numberOfPages.value,
disabled: lastDisabled.value,
classVal: props.lastClass,
slotName: "last-text",
textValue: props.lastText,
label: props.labelLastPage
}));
const ellipsisProps = computed(() => ({
li: {
class: [
"page-item",
"disabled",
"text-center",
"bv-d-sm-down-none",
computedFill.value ? "flex-fill" : "",
props.ellipsisClass
],
role: "separator"
},
span: { class: ["page-link"] }
}));
const computedWrapperClasses = computed(() => [alignment.value, {
[`pagination-${props.size}`]: props.size !== void 0,
"b-pagination-pills": props.pills
}]);
const pagination = computed(() => ({
pageSize: perPageSanitized.value,
totalRows: totalRowsNumber.value,
numberOfPages: numberOfPages.value
}));
const pageClick = (event, pageNumber) => {
if (pageNumber === computedModelValue.value) return;
const clickEvent = new BvEvent("page-click", {
cancelable: true,
target: event.target
});
emit("page-click", clickEvent, pageNumber);
if (clickEvent.defaultPrevented) return;
modelValue.value = pageNumber;
nextTick(() => {
if (pageNumber === 1) focusFirst();
else if (pageNumber === pagination.value.numberOfPages) focusLast();
});
};
const isDisabled = (el) => {
const isElement = el && el.nodeType === Node.ELEMENT_NODE;
const hasAttr = isElement ? el.hasAttribute("disabled") : null;
const hasClass = isElement && el.classList ? el.classList.contains("disabled") : false;
return !isElement || el.disabled || hasAttr || hasClass;
};
const getButtons = () => [...pageElements.value ?? []].sort((a, b) => Number.parseInt(a.getAttribute("displayIndex") || "0") - Number.parseInt(b.getAttribute("displayIndex") || "0")).map((page) => page.children[0]).filter((el) => {
if (el?.getAttribute("display") === "none" || el?.tagName.toUpperCase() !== "BUTTON") return false;
const bcr = el?.getBoundingClientRect();
return !!(bcr && bcr.height > 0 && bcr.width > 0);
}).map((el) => el);
const focusFirst = () => {
nextTick(() => {
getButtons().find((el) => !isDisabled(el))?.focus();
});
};
const focusPrev = () => {
nextTick(() => {
const buttons = getButtons();
const index = buttons.indexOf(getActiveElement());
const button = buttons[index - 1];
if (index > 0 && button !== void 0 && !isDisabled(button)) buttons[index - 1]?.focus();
});
};
const focusLast = () => {
nextTick(() => {
getButtons().reverse().find((el) => !isDisabled(el))?.focus();
});
};
const focusNext = () => {
nextTick(() => {
const buttons = getButtons();
const index = buttons.indexOf(getActiveElement());
const button = buttons[index + 1];
if (index < buttons.length - 1 && button !== void 0 && !isDisabled(button)) buttons[index + 1]?.focus();
});
};
const handleKeyNav = (event) => {
const { code, shiftKey } = event;
if (code === "ArrowLeft" || code === "ArrowUp") {
event.preventDefault();
if (shiftKey) focusFirst();
else focusPrev();
} else if (code === "ArrowRight" || code === "ArrowDown") {
event.preventDefault();
if (shiftKey) focusLast();
else focusNext();
}
};
const computedModelValue = computed(() => {
const page = modelValueNumber.value || 1;
return page > numberOfPages.value ? numberOfPages.value : page < 1 ? 1 : page;
});
watch(pagination, (oldValue, newValue) => {
if (newValue.pageSize !== oldValue.pageSize && newValue.totalRows === oldValue.totalRows) modelValue.value = 1;
});
const noFirstButton = computed(() => props.noGotoEndButtons && !props.firstNumber ? 1 : 0);
const noLastButton = computed(() => props.noGotoEndButtons && !props.lastNumber ? 1 : 0);
const showFirstButton = computed(() => noFirstButton.value ? 0 : 1);
const showLastButton = computed(() => noLastButton.value ? 0 : 1);
const firstPage = computed(() => props.firstNumber ? 1 : 0);
const lastPage = computed(() => props.lastNumber ? 1 : 0);
const halfLimit = computed(() => Math.floor(limitNumber.value / 2));
const pages = computed(() => {
const { value } = computedModelValue;
const els = elements.value.map((p) => {
switch (p) {
case FIRST_BUTTON: return {
id: p,
...firstButtonProps.value
};
case PREV_BUTTON: return {
id: p,
...prevButtonProps.value
};
case NEXT_BUTTON: return {
id: p,
...nextButtonProps.value
};
case LAST_BUTTON: return {
id: p,
...lastButtonProps.value
};
case FIRST_ELLIPSIS:
case LAST_ELLIPSIS: return {
id: p,
...ellipsisProps.value
};
default: return {
id: p,
...getPageButtonProps(p)
};
}
});
if (numberOfPages.value > 3) {
if (value > numberOfPages.value - halfLimit.value - lastPage.value) {
const idx = 2 + showFirstButton.value;
const el = els[idx];
if (el !== void 0) els[idx] = {
id: el.id,
...getPageButtonProps(el.id, true)
};
}
if (value <= halfLimit.value + firstPage.value) {
const idx = els.length - (3 + showLastButton.value);
const el = els[idx];
if (el !== void 0) els[idx] = {
id: el.id,
...getPageButtonProps(el.id, true)
};
}
}
return els;
});
const elements = computed(() => {
const pages = numberOfPages.value;
const { value } = computedModelValue;
const limit = limitNumber.value;
const noEllipsis = props.noEllipsis || limit <= ELLIPSIS_THRESHOLD;
if (pages < limit + firstPage.value + lastPage.value) return [
!firstPage.value && !noFirstButton.value ? FIRST_BUTTON : null,
PREV_BUTTON,
...Array.from({ length: pages }, (_, index) => index + 1),
NEXT_BUTTON,
!lastPage.value && !noLastButton.value ? LAST_BUTTON : null
].filter((x) => x !== null);
const buttons = Array.from({ length: limit + 4 - (noFirstButton.value + noLastButton.value) });
if (!noFirstButton.value) if (!firstPage.value) {
buttons[0] = FIRST_BUTTON;
buttons[1] = PREV_BUTTON;
} else {
buttons[0] = PREV_BUTTON;
buttons[1] = 1;
}
else buttons[0] = PREV_BUTTON;
if (!noLastButton.value) if (!lastPage.value) {
buttons[buttons.length - 1] = LAST_BUTTON;
buttons[buttons.length - 2] = NEXT_BUTTON;
} else {
buttons[buttons.length - 1] = NEXT_BUTTON;
buttons[buttons.length - 2] = pages;
}
else buttons[buttons.length - 1] = NEXT_BUTTON;
if (value <= halfLimit.value + firstPage.value) {
for (let index = 1; index <= limit; index++) buttons[index + 1 - noFirstButton.value] = index + firstPage.value;
if (!noEllipsis) buttons[buttons.length - (2 + showLastButton.value)] = LAST_ELLIPSIS;
}
if (value > pages - halfLimit.value - lastPage.value) {
const start = pages - (limit - 1) - lastPage.value;
for (let index = 0; index < limit; index++) buttons[index + 2 - noFirstButton.value] = start + index;
if (!noEllipsis) buttons[1 + showFirstButton.value] = FIRST_ELLIPSIS;
}
if (!buttons[2]) {
const start = value - Math.floor(limit / 2);
for (let index = 0; index < limit; index++) buttons[index + 2 - noFirstButton.value] = start + index;
if (!noEllipsis) {
buttons[1 + showFirstButton.value] = FIRST_ELLIPSIS;
buttons[buttons.length - (2 + showLastButton.value)] = LAST_ELLIPSIS;
}
}
return buttons.filter((x) => x !== null);
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("ul", {
class: normalizeClass(["pagination", computedWrapperClasses.value]),
role: "menubar",
"aria-disabled": unref(props).disabled,
"aria-label": unref(props).ariaLabel || void 0,
onKeydown: handleKeyNav
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(pages.value, (page, index) => {
return openBlock(), createElementBlock("li", mergeProps({ key: `page-${page.id}` }, { ref_for: true }, page.li, {
ref_for: true,
ref: "_pageElements",
displayIndex: index
}), [page.id === FIRST_ELLIPSIS || page.id === LAST_ELLIPSIS ? (openBlock(), createElementBlock("span", mergeProps({
key: 0,
ref_for: true
}, ellipsisProps.value.span), [renderSlot(_ctx.$slots, "ellipsis-text", {}, () => [createTextVNode(toDisplayString(unref(props).ellipsisText || "..."), 1)])], 16)) : "button" in page ? (openBlock(), createBlock(resolveDynamicComponent(page.button.is), mergeProps({
key: 1,
ref_for: true
}, page.button, { onClick: page.clickHandler }), {
default: withCtx(() => [renderSlot(_ctx.$slots, page.text.name, {
disabled: page.text.disabled,
page: page.text.page,
index: page.text.index,
active: page.text.active ?? false,
content: page.text.value
}, () => [createTextVNode(toDisplayString(page.text.value), 1)])]),
_: 2
}, 1040, ["onClick"])) : createCommentVNode("", true)], 16, _hoisted_2);
}), 128))], 42, _hoisted_1);
};
}
});
//#endregion
export { BPagination_default as t };
//# sourceMappingURL=BPagination-CvoSN1He.mjs.map