From 62f8c04d6a58588bf7a0473e4c071fd384fe525b Mon Sep 17 00:00:00 2001 From: Ulises Salinas Date: Mon, 12 May 2025 00:11:56 -0700 Subject: [PATCH] new paginator but is not compatible with other pages --- frontend/src/components/Paginator.tsx | 195 +++++++++++++++++--------- 1 file changed, 129 insertions(+), 66 deletions(-) diff --git a/frontend/src/components/Paginator.tsx b/frontend/src/components/Paginator.tsx index 8054279..eb0aceb 100644 --- a/frontend/src/components/Paginator.tsx +++ b/frontend/src/components/Paginator.tsx @@ -1,3 +1,4 @@ +import React, { useMemo } from "react"; import { RxChevronLeft, RxChevronRight, @@ -10,24 +11,21 @@ interface PaginatorContent { goToPage?: boolean; } -interface PaginatorProps { - page: number; // Current page index - perPage: number; // Current page size - totalItems: number; // Total number of items - onPageChange: (page: number) => void; // Function to change the page - onPerPageChange: (size: number) => void; // Function to change the page size - perPageOptions?: number[]; +export interface UsePagination { paginatorContent?: PaginatorContent; } -/** - * When component uses paginator, props should extend this interface to allow the user to customize pagination content and style - */ -export interface UsePagination { +interface PaginatorProps { + page: number; // zero-based + perPage: number; + totalItems: number; + onPageChange: (page: number) => void; + onPerPageChange: (size: number) => void; + perPageOptions?: number[]; paginatorContent?: PaginatorContent; } -const Paginator = ({ +const Paginator: React.FC = ({ page, perPage, totalItems, @@ -35,71 +33,136 @@ const Paginator = ({ onPerPageChange, perPageOptions = [10, 20, 30, 40, 50], paginatorContent = { setPerPage: false, goToPage: false }, -}: PaginatorProps) => { - const totalPages = Math.ceil(totalItems / perPage); +}) => { + const totalPages = Math.max(1, Math.ceil(totalItems / perPage)); + + const pagesToShow = useMemo<(number | "...")[]>(() => { + const result: (number | "...")[] = []; + if (totalPages <= 7) { + for (let i = 1; i <= totalPages; i++) result.push(i); + } else { + if (page + 1 <= 4) { + for (let i = 1; i <= 5; i++) result.push(i); + result.push("...", totalPages); + } else if (page + 1 >= totalPages - 3) { + result.push(1, "..."); + for (let i = totalPages - 4; i <= totalPages; i++) result.push(i); + } else { + result.push(1, "...", page, page + 1, page + 2, "...", totalPages); + } + } + return result; + }, [page, totalPages]); return ( -
+
{paginatorContent.setPerPage && ( - +
+ +
)} - - - - Page {page + 1} of {totalPages === 0 ? 1 : totalPages} - - - + {/* first/prev ... next/last) */} +
+ + + + {pagesToShow.map((p, idx) => + p === "..." ? ( + + … + + ) : ( + + ) + )} + + + +
+ + {/* per page dropdown */} + {/* go to page */} {paginatorContent.goToPage && ( - - Go to page:{" "} +
+ Go to: { - const newPage = Number(e.target.value) - 1; - onPageChange( - newPage >= 0 && newPage < totalPages ? newPage : page, - ); + defaultValue={page + 1} + onKeyDown={(e) => { + if (e.key === "Enter") { + const v = Number((e.target as HTMLInputElement).value) - 1; + if (v >= 0 && v < totalPages) onPageChange(v); + } }} + className=" + w-16 h-8 px-2 + border border-gray-300 + bg-white text-gray-700 + rounded-md text-sm + focus:outline-none focus:ring-1 focus:ring-blue-500 + " /> - +
)}
);