feat(pagination): implement windowed pagination for improved navigation
- Introduced windowed pagination logic in the TendersTable component to enhance user experience by allowing for a more manageable view of pages. - Updated the pagination component to accept new props for page range and margin pages, enabling customizable pagination display. - Refactored the useTenderListPresenter to utilize the new windowed pagination functionality, improving clarity and maintainability of pagination handling.
This commit is contained in:
@@ -42,6 +42,7 @@ const TendersTable = () => {
|
||||
navigateToTenderDetails,
|
||||
navigateToTenderFeedback,
|
||||
handlePaginationChange,
|
||||
buildWindowedPageLabel,
|
||||
getRowNumber,
|
||||
formatDateTimeCell,
|
||||
pagination,
|
||||
@@ -157,6 +158,9 @@ const TendersTable = () => {
|
||||
currentPage={pagination.currentPage}
|
||||
totalPages={pagination.totalPages}
|
||||
onPageChange={handlePaginationChange}
|
||||
pageRangeDisplayed={3}
|
||||
marginPagesDisplayed={1}
|
||||
pageLabelBuilder={buildWindowedPageLabel}
|
||||
/>
|
||||
</IsVisible>
|
||||
</ListWrapper>
|
||||
|
||||
@@ -346,6 +346,42 @@ const useTenderListPresenter = () => {
|
||||
error,
|
||||
});
|
||||
|
||||
const MAX_PAGE_JUMP = 10;
|
||||
const paginationWindow = useMemo(() => {
|
||||
const windowStart = Math.max(0, pagination.currentPage - MAX_PAGE_JUMP);
|
||||
const windowEnd = Math.min(
|
||||
Math.max(pagination.totalPages - 1, 0),
|
||||
pagination.currentPage + MAX_PAGE_JUMP,
|
||||
);
|
||||
return {
|
||||
windowStart,
|
||||
virtualCurrent: pagination.currentPage - windowStart,
|
||||
virtualTotal: Math.max(windowEnd - windowStart + 1, 1),
|
||||
};
|
||||
}, [pagination.currentPage, pagination.totalPages]);
|
||||
|
||||
const handleWindowedPaginationChange = useCallback(
|
||||
(e: { selected: number }) => {
|
||||
handlePaginationChange({
|
||||
selected: e.selected + paginationWindow.windowStart,
|
||||
});
|
||||
},
|
||||
[handlePaginationChange, paginationWindow.windowStart],
|
||||
);
|
||||
|
||||
const buildWindowedPageLabel = useCallback(
|
||||
(virtualPage: number) => String(virtualPage + paginationWindow.windowStart),
|
||||
[paginationWindow.windowStart],
|
||||
);
|
||||
|
||||
const windowedPagination = useMemo(
|
||||
() => ({
|
||||
currentPage: paginationWindow.virtualCurrent,
|
||||
totalPages: paginationWindow.virtualTotal,
|
||||
}),
|
||||
[paginationWindow.virtualCurrent, paginationWindow.virtualTotal],
|
||||
);
|
||||
|
||||
const shouldShowPagination =
|
||||
!isPending && (data?.meta?.pages ?? 1) * (data?.meta?.limit ?? 10) > 10;
|
||||
|
||||
@@ -498,10 +534,11 @@ const useTenderListPresenter = () => {
|
||||
tendersList,
|
||||
navigateToTenderDetails,
|
||||
navigateToTenderFeedback,
|
||||
handlePaginationChange,
|
||||
handlePaginationChange: handleWindowedPaginationChange,
|
||||
buildWindowedPageLabel,
|
||||
getRowNumber,
|
||||
formatDateTimeCell,
|
||||
pagination,
|
||||
pagination: windowedPagination,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -4,13 +4,23 @@ interface IProps {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
onPageChange: ReactPaginateProps["onPageChange"];
|
||||
pageRangeDisplayed?: number;
|
||||
marginPagesDisplayed?: number;
|
||||
pageLabelBuilder?: ReactPaginateProps["pageLabelBuilder"];
|
||||
}
|
||||
|
||||
/** Full-cell hit target: styles live on <li>, but react-paginate puts clicks on the inner <a>. */
|
||||
const pageLinkClassName =
|
||||
"absolute inset-0 z-[1] flex cursor-pointer items-center justify-center select-none";
|
||||
|
||||
const Pagination: FC<IProps> = ({ totalPages, currentPage, onPageChange }) => {
|
||||
const Pagination: FC<IProps> = ({
|
||||
totalPages,
|
||||
currentPage,
|
||||
onPageChange,
|
||||
pageRangeDisplayed = 5,
|
||||
marginPagesDisplayed = 3,
|
||||
pageLabelBuilder,
|
||||
}) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<ReactPaginate
|
||||
@@ -19,7 +29,9 @@ const Pagination: FC<IProps> = ({ totalPages, currentPage, onPageChange }) => {
|
||||
forcePage={currentPage}
|
||||
nextLabel="›"
|
||||
previousLabel="‹"
|
||||
pageRangeDisplayed={5}
|
||||
pageRangeDisplayed={pageRangeDisplayed}
|
||||
marginPagesDisplayed={marginPagesDisplayed}
|
||||
pageLabelBuilder={pageLabelBuilder}
|
||||
activeClassName="flex h-9 min-w-9 items-center justify-center rounded-xl border border-white/35 bg-gradient-to-br from-primary/85 via-primary/75 to-primary/65 px-3 text-sm font-semibold !text-white shadow-theme-xs shadow-primary/20 backdrop-blur-md transition-all duration-300 before:pointer-events-none before:absolute before:inset-x-0 before:top-0 before:h-1/2 before:rounded-t-xl before:bg-gradient-to-b before:from-white/30 before:to-transparent dark:border-white/20 dark:before:from-white/20"
|
||||
onPageChange={(e) => {
|
||||
if (!onPageChange) return;
|
||||
|
||||
Reference in New Issue
Block a user