@availity/pagination
Version:
component for displaying paginated data
469 lines (463 loc) • 16.1 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __objRest = (source, exclude) => {
var target = {};
for (var prop in source)
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
target[prop] = source[prop];
if (source != null && __getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(source)) {
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
target[prop] = source[prop];
}
return target;
};
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/Pagination.tsx
import { createContext, useState, useContext, useEffect, useRef } from "react";
import isFunction from "lodash/isFunction";
import { useDebounce } from "react-use";
import { jsx } from "react/jsx-runtime";
var PaginationContext = createContext(null);
function usePagination() {
const ctx = useContext(PaginationContext);
if (!ctx)
throw new Error("usePagination must be used inside the Pagination Provider");
return ctx;
}
var defaultValues = {
items: [],
watchList: [],
resetParams: [],
itemsPerPage: 10,
defaultPage: 1,
debounceTimeout: 0,
shouldReturnPrevious: false
};
function Pagination({
items: theItems = defaultValues.items,
page: propsCurrentPage,
itemsPerPage = defaultValues.itemsPerPage,
onPageChange,
children,
watchList = defaultValues.watchList,
resetParams = defaultValues.resetParams,
defaultPage = defaultValues.defaultPage,
debounceTimeout = defaultValues.debounceTimeout,
shouldReturnPrevious = defaultValues.shouldReturnPrevious,
onError
}) {
const ref = useRef(null);
const [stateCurrentPage, setPage] = useState(defaultPage);
const [doFocusRefOnPageChange, setDoFocusRefOnPageChange] = useState(false);
const [pageData, setPageData] = useState({
total: theItems != null && !isFunction(theItems) ? theItems.length : 0,
pageCount: 0,
page: [],
allPages: [],
lower: 0,
upper: 0,
hasMore: false
});
const currentPage = propsCurrentPage || stateCurrentPage;
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const toggleLoading = (isLoading) => {
setLoading((prev) => isLoading !== void 0 ? isLoading : !prev);
};
useEffect(() => {
if (onError && error)
onError(error);
}, [error, onError]);
const getPageData = () => __async(this, null, function* () {
try {
toggleLoading(true);
const { items, totalCount } = isFunction(theItems) ? yield theItems(currentPage, itemsPerPage) : { items: theItems, totalCount: theItems.length };
const lower = currentPage === 1 ? 1 : (currentPage - 1) * itemsPerPage + 1;
const upper = items.length - currentPage * itemsPerPage > 0 ? itemsPerPage * currentPage : items.length;
const page = isFunction(theItems) ? items : items.slice(lower - 1, upper);
const total = totalCount || items.length;
const pageCount = Math.ceil(total / itemsPerPage);
setPageData({
total,
pageCount,
page,
allPages: [...pageData.allPages, ...page],
lower,
upper,
hasMore: currentPage < pageCount
});
if (doFocusRefOnPageChange && ref.current && ref.current.nextSibling) {
ref.current.nextSibling.focus();
setDoFocusRefOnPageChange(false);
}
} catch (error_) {
setError(error_);
} finally {
toggleLoading(false);
}
});
useDebounce(
() => {
if (!shouldReturnPrevious) {
getPageData();
}
},
debounceTimeout,
[currentPage, itemsPerPage, isFunction(theItems) ? null : theItems, shouldReturnPrevious, ...watchList]
);
const updatePage = (page) => {
if (page !== currentPage) {
toggleLoading(true);
if (!propsCurrentPage) {
setPage(page);
}
if (onPageChange) {
onPageChange(page);
}
}
};
const firstUpdate = useRef(true);
useDebounce(
() => {
if (firstUpdate.current) {
firstUpdate.current = false;
} else {
setPageData(__spreadProps(__spreadValues({}, pageData), { allPages: [] }));
const current = currentPage;
updatePage(1);
if (current === 1 && isFunction(theItems)) {
getPageData();
}
}
},
debounceTimeout,
[...resetParams]
);
return /* @__PURE__ */ jsx(
PaginationContext.Provider,
{
value: __spreadProps(__spreadValues({}, pageData), {
setPage: updatePage,
currentPage,
loading,
error,
setError,
itemsPerPage,
ref,
setDoFocusRefOnPageChange
}),
children
}
);
}
var Pagination_default = Pagination;
// src/PaginationContent.tsx
import React2, { useMemo } from "react";
import { Button } from "reactstrap";
import InfiniteScroll from "react-infinite-scroll-component";
import BlockUI from "@availity/block-ui";
import isFunction2 from "lodash/isFunction";
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
import { createElement } from "react";
var PaginationContent = (_a) => {
var _b = _a, {
component: Component,
loadingMessage,
itemKey,
loader = false,
containerTag = "div",
containerProps = {},
infiniteScroll = false,
infiniteScrollProps,
children
} = _b, rest = __objRest(_b, [
"component",
"loadingMessage",
"itemKey",
"loader",
"containerTag",
"containerProps",
"infiniteScroll",
"infiniteScrollProps",
"children"
]);
const { page, currentPage, setPage, allPages, hasMore, loading, lower, ref, setDoFocusRefOnPageChange } = usePagination();
const _children = useMemo(() => {
let items;
if (infiniteScroll) {
const indexOfItemToReference = lower - 1;
items = allPages && allPages.map((value, index) => {
if (!value[itemKey]) {
console.warn("Warning a Pagination Item doesn't have a key:", value);
}
if (indexOfItemToReference === index) {
const ComponentWithRef = React2.forwardRef((props, innerRef) => /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx2("span", { className: "sr-only", ref: innerRef }),
/* @__PURE__ */ jsx2(Component, __spreadValues({}, props))
] }));
return /* @__PURE__ */ createElement(ComponentWithRef, __spreadValues(__spreadProps(__spreadValues({ ref }, rest), { key: value[itemKey] || index }), value));
}
return /* @__PURE__ */ createElement(Component, __spreadValues(__spreadProps(__spreadValues({}, rest), { key: value[itemKey] || index }), value));
});
} else {
items = page && page.map((value, key) => {
if (!value[itemKey]) {
console.warn("Warning a Pagination Item doesn't have a key:", value);
}
return /* @__PURE__ */ createElement(Component, __spreadValues(__spreadProps(__spreadValues({}, rest), { key: value[itemKey] || key }), value));
});
}
if (children) {
return isFunction2(children) ? children({ items }) : children;
}
return items;
}, [allPages, children, Component, infiniteScroll, itemKey, lower, page, ref, rest]);
if (infiniteScroll) {
return /* @__PURE__ */ jsxs(
InfiniteScroll,
__spreadProps(__spreadValues({
loader: loader && /* @__PURE__ */ jsx2("div", { className: "h3", children: loadingMessage })
}, infiniteScrollProps), {
next: () => {
setPage(currentPage + 1);
},
hasMore,
dataLength: allPages.length,
children: [
_children,
/* @__PURE__ */ jsx2(
Button,
{
"data-testid": "sr-only-pagination-load-more-btn",
className: "sr-only",
"aria-label": "Load More",
onClick: () => {
setDoFocusRefOnPageChange(true);
setPage(currentPage + 1);
},
children: "Load More"
}
)
]
})
);
}
return /* @__PURE__ */ jsx2(
BlockUI,
__spreadProps(__spreadValues({
"data-testid": "pagination-content-con",
role: containerProps.role || "list",
keepInView: true
}, containerProps), {
tag: containerTag,
blocking: loader && loading,
message: loadingMessage,
children: _children
})
);
};
var PaginationContent_default = PaginationContent;
// src/PaginationControls.tsx
import { Pagination as Pagination2, PaginationItem, PaginationLink } from "reactstrap";
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
var leftCaret = "\u2039";
var rightCaret = "\u203A";
var PaginationControls = (_a) => {
var _b = _a, {
directionLinks = false,
autoHide = true,
pageRange = 5,
marginPages = 2,
breakLabel = true,
showPaginationText = false,
populatePaginationText
} = _b, rest = __objRest(_b, [
"directionLinks",
"autoHide",
"pageRange",
"marginPages",
"breakLabel",
"showPaginationText",
"populatePaginationText"
]);
const { pageCount, currentPage, setPage, lower, upper, total } = usePagination();
const createItem = (pageNumber) => /* @__PURE__ */ jsx3(PaginationItem, { active: currentPage === pageNumber, "data-testid": `control-page-${pageNumber}`, children: /* @__PURE__ */ jsx3(
PaginationLink,
{
style: { zIndex: "auto" },
onClick: () => setPage(pageNumber),
type: "button",
"aria-label": `Go to page ${pageNumber}`,
"aria-current": currentPage === pageNumber,
children: pageNumber
}
) }, pageNumber);
const getForwardJump = () => {
const forwardJump = currentPage + pageRange;
return forwardJump >= pageCount ? pageCount : forwardJump;
};
const getBackwardJump = () => {
const backwardJump = currentPage - pageRange;
return backwardJump < 1 ? 1 : backwardJump;
};
const handleBreakClick = (index) => {
setPage(currentPage < index ? getForwardJump() : getBackwardJump());
};
const createBreak = (index) => /* @__PURE__ */ jsx3(PaginationItem, { "data-testid": `control-page-${index}`, children: /* @__PURE__ */ jsx3(
PaginationLink,
{
onClick: () => handleBreakClick(index),
type: "button",
"aria-label": currentPage < index ? `Jump forwards to page ${getForwardJump()}` : `Jump backwards to page ${getBackwardJump()}`,
children: "\u2026"
}
) }, index);
const paginate = () => {
const items = [];
const selected = currentPage - 1;
if (pageCount <= pageRange) {
for (let index = 0; index < pageCount; index++) {
items.push(createItem(index + 1));
}
} else {
let leftSide = pageRange / 2;
let rightSide = pageRange - leftSide;
if (selected > pageCount - leftSide) {
rightSide = pageCount - selected;
leftSide = pageRange - rightSide;
} else if (selected < leftSide) {
leftSide = selected;
rightSide = pageRange - leftSide;
}
let breakView;
let pageNumber;
for (let index = 0; index < pageCount; index++) {
pageNumber = index + 1;
if (pageNumber <= marginPages || pageNumber > pageCount - marginPages || index >= selected - leftSide && index <= selected + rightSide) {
items.push(createItem(pageNumber));
} else if (items[items.length - 1] !== breakView && breakLabel) {
breakView = createBreak(pageNumber);
items.push(breakView);
}
}
}
return items;
};
return pageCount > 1 || !autoHide ? /* @__PURE__ */ jsxs2(Pagination2, __spreadProps(__spreadValues({ "data-testid": "pagination-controls-con" }, rest), { children: [
directionLinks ? /* @__PURE__ */ jsx3(PaginationItem, { disabled: currentPage === 1, "data-testid": "pagination-control-previous", children: /* @__PURE__ */ jsxs2(
PaginationLink,
{
onClick: () => currentPage === 1 ? null : setPage(currentPage - 1),
type: "button",
"aria-disabled": currentPage === 1,
previous: true,
children: [
leftCaret,
" Prev"
]
}
) }) : "",
paginate(),
directionLinks ? /* @__PURE__ */ jsx3(PaginationItem, { disabled: currentPage === pageCount, "data-testid": "pagination-control-next", children: /* @__PURE__ */ jsxs2(
PaginationLink,
{
"data-testid": "pagination-control-next-link",
onClick: () => currentPage === pageCount ? null : setPage(currentPage + 1),
type: "button",
"aria-disabled": currentPage === pageCount,
next: true,
children: [
"Next ",
rightCaret
]
}
) }) : "",
showPaginationText && /* @__PURE__ */ jsx3("li", { "data-testid": "pagination-text", className: "pagination-text pt-1 pl-2 pr-2", children: populatePaginationText ? populatePaginationText(lower, upper, total) : `${lower}-${upper} of ${total}` })
] })) : null;
};
var PaginationControls_default = PaginationControls;
// src/AvResourcePagination.tsx
import { jsx as jsx4 } from "react/jsx-runtime";
var AvResourcePagination = (_a) => {
var _b = _a, {
parameters = {},
resource,
getResult,
children
} = _b, paginationProps = __objRest(_b, [
"parameters",
"resource",
"getResult",
"children"
]);
const loadPage = (page, itemsPerPage) => __async(void 0, null, function* () {
const params = __spreadValues({
limit: itemsPerPage,
offset: (page - 1) * itemsPerPage
}, parameters.params || {});
const resp = yield resource.postGet(params, parameters || {});
const useGetResult = getResult || resource.getResult;
const items = (typeof useGetResult === "function" ? useGetResult.call(resource, resp.data) : resp.data[useGetResult]) || resp.data;
if (!Array.isArray(items)) {
throw new TypeError(
`Expected data to be an array but got \`${typeof items}\`. Use the \`getResult\` prop to specify how to get the data from the API response.`
);
}
return {
items,
totalCount: resp.data.totalCount
};
});
return /* @__PURE__ */ jsx4(Pagination_default, __spreadProps(__spreadValues({}, paginationProps), { items: (page, itemsPerPage) => loadPage(page, itemsPerPage), children }));
};
var AvResourcePagination_default = AvResourcePagination;
// src/index.ts
Pagination_default.Controls = PaginationControls_default;
Pagination_default.Content = PaginationContent_default;
var src_default = Pagination_default;
export {
AvResourcePagination_default as AvResourcePagination,
Pagination_default as Pagination,
PaginationContent_default as PaginationContent,
PaginationContext,
PaginationControls_default as PaginationControls,
src_default as default,
usePagination
};