/** * DRF Static Pagination Component * * Universal pagination component that works with Django REST Framework pagination format. * Manages page state internally and provides callbacks for page changes. */ "use client" import { ChevronLeft, ChevronRight } from 'lucide-react'; import React from 'react'; import { cn } from '../../../lib'; import { Button } from '../../forms/button'; import { useIsMobile } from '../../../hooks'; import { Pagination, PaginationContent, PaginationEllipsis, PaginationItem } from './pagination'; export interface DRFPaginatedResponse { count: number; page: number; pages: number; page_size: number; has_next: boolean; has_previous: boolean; next_page?: number | null; previous_page?: number | null; results: T[]; } interface StaticPaginationProps { data?: DRFPaginatedResponse | null; onPageChange: (page: number) => void; className?: string; showInfo?: boolean; maxVisiblePages?: number; } export const StaticPagination: React.FC = ({ data, onPageChange, className, showInfo = true, maxVisiblePages = 7, }) => { const isMobile = useIsMobile(); if (!data || !('count' in data) || !('page' in data) || !('pages' in data)) { return null; } const { count: totalItems, page: currentPage, pages: totalPages, page_size: itemsPerPage, has_next: hasNextPage, has_previous: hasPreviousPage, } = data; if (totalPages <= 1) { return null; } const getVisiblePages = (): (number | 'ellipsis')[] => { const mobileMaxVisible = 3; const effectiveMaxVisible = isMobile ? mobileMaxVisible : maxVisiblePages; if (totalPages <= effectiveMaxVisible) { return Array.from({ length: totalPages }, (_, i) => i + 1); } const pages: (number | 'ellipsis')[] = []; const halfVisible = Math.floor(effectiveMaxVisible / 2); if (isMobile) { if (currentPage > 1) { pages.push(currentPage - 1); } pages.push(currentPage); if (currentPage < totalPages) { pages.push(currentPage + 1); } } else { pages.push(1); let start = Math.max(2, currentPage - halfVisible); let end = Math.min(totalPages - 1, currentPage + halfVisible); if (currentPage <= halfVisible + 1) { end = Math.min(totalPages - 1, effectiveMaxVisible - 1); } else if (currentPage >= totalPages - halfVisible) { start = Math.max(2, totalPages - effectiveMaxVisible + 2); } if (start > 2) { pages.push('ellipsis'); } for (let i = start; i <= end; i++) { pages.push(i); } if (end < totalPages - 1) { pages.push('ellipsis'); } if (totalPages > 1) { pages.push(totalPages); } } return pages; }; const visiblePages = getVisiblePages(); const startItem = (currentPage - 1) * itemsPerPage + 1; const endItem = Math.min(currentPage * itemsPerPage, totalItems); const handlePageClick = (page: number) => { if (page !== currentPage && page >= 1 && page <= totalPages) { onPageChange(page); } }; return (
{showInfo && (
{isMobile ? ( `Page ${currentPage} of ${totalPages}` ) : ( `Showing ${startItem.toLocaleString()} to ${endItem.toLocaleString()} of ${totalItems.toLocaleString()} results` )}
)} {visiblePages.map((page, index) => ( {page === 'ellipsis' ? ( ) : ( )} ))}
); }; StaticPagination.displayName = 'StaticPagination'; export function useDRFPagination(initialPage = 1, initialPageSize = 10) { const [page, setPage] = React.useState(initialPage); const [pageSize, setPageSize] = React.useState(initialPageSize); const handlePageSizeChange = React.useCallback((newPageSize: number) => { setPageSize(newPageSize); setPage(1); }, []); const params = React.useMemo(() => ({ page, page_size: pageSize, }), [page, pageSize]); return { page, pageSize, setPage, setPageSize: handlePageSizeChange, params, reset: () => { setPage(initialPage); setPageSize(initialPageSize); }, }; } export function useDRFPaginationInfo(data?: DRFPaginatedResponse | null) { return React.useMemo(() => { if (!data || !('count' in data) || !('page' in data)) { return null; } return { totalItems: data.count, currentPage: data.page, totalPages: data.pages, itemsPerPage: data.page_size, hasNext: data.has_next, hasPrevious: data.has_previous, nextPage: data.next_page, previousPage: data.previous_page, results: data.results || [], resultsCount: data.results?.length || 0, }; }, [data]); }