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:
AmirReza Jamali
2026-05-18 15:28:07 +03:30
parent 7b63b2a6f3
commit fc53d6b556
3 changed files with 57 additions and 4 deletions
+4
View File
@@ -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,
};
};
+14 -2
View File
@@ -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;