9router / src /shared /components /Pagination.js
2api
feat: configurable stream stall timeout + per-provider UI
88c4c60
Raw
History Blame Contribute Delete
4.66 kB
"use client";
import { cn } from "@/shared/utils/cn";
import Button from "./Button";
export default function Pagination({
currentPage,
pageSize,
totalItems,
onPageChange,
onPageSizeChange,
className,
}) {
const totalPages = Math.ceil(totalItems / pageSize);
const startItem = totalItems > 0 ? (currentPage - 1) * pageSize + 1 : 0;
const endItem = Math.min(currentPage * pageSize, totalItems);
const getPageNumbers = () => {
const pages = [];
const showMax = 5;
let start = Math.max(1, currentPage - 2);
let end = Math.min(totalPages, start + showMax - 1);
if (end - start + 1 < showMax) {
start = Math.max(1, end - showMax + 1);
}
for (let i = start; i <= end; i++) {
pages.push(i);
}
return pages;
};
const pageNumbers = getPageNumbers();
return (
<div
className={cn(
"flex flex-col sm:flex-row items-center justify-between gap-4 py-4 px-2",
className
)}
>
{/* Info text */}
{totalItems > 0 && (
<div className="text-sm text-text-muted">
Showing <span className="font-medium text-text-main">{startItem}</span> to{" "}
<span className="font-medium text-text-main">{endItem}</span> of{" "}
<span className="font-medium text-text-main">{totalItems}</span> results
</div>
)}
<div className="flex flex-wrap items-center justify-center gap-2 sm:gap-4">
{/* Page size selector */}
{onPageSizeChange && (
<div className="flex items-center gap-2">
<span className="text-sm text-text-muted">Rows:</span>
<select
value={pageSize}
onChange={(e) => onPageSizeChange(Number(e.target.value))}
className={cn(
"h-9 rounded-lg border border-black/10 dark:border-white/10 bg-surface",
"text-sm text-text-main focus:outline-none focus:ring-2 focus:ring-primary/20",
"cursor-pointer"
)}
style={{ colorScheme: 'auto' }}
>
{[10, 20, 50].map((size) => (
<option key={size} value={size}>
{size}
</option>
))}
</select>
</div>
)}
{totalPages > 1 && (
<div className="flex items-center gap-1">
<Button
variant="outline"
size="sm"
onClick={() => onPageChange(currentPage - 1)}
disabled={currentPage === 1}
className="w-9 px-0"
>
<span className="material-symbols-outlined text-[18px]">chevron_left</span>
</Button>
{pageNumbers[0] > 1 && (
<>
<Button
variant="ghost"
size="sm"
onClick={() => onPageChange(1)}
className="w-9 px-0 hidden sm:inline-flex"
>
1
</Button>
{pageNumbers[0] > 2 && (
<span className="text-text-muted px-1 hidden sm:inline">...</span>
)}
</>
)}
{pageNumbers.map((page) => (
<Button
key={page}
variant={currentPage === page ? "primary" : "ghost"}
size="sm"
onClick={() => onPageChange(page)}
className={cn(
"w-9 px-0",
currentPage === page ? "inline-flex" : "hidden sm:inline-flex"
)}
>
{page}
</Button>
))}
{pageNumbers[pageNumbers.length - 1] < totalPages && (
<>
{pageNumbers[pageNumbers.length - 1] < totalPages - 1 && (
<span className="text-text-muted px-1 hidden sm:inline">...</span>
)}
<Button
variant="ghost"
size="sm"
onClick={() => onPageChange(totalPages)}
className="w-9 px-0 hidden sm:inline-flex"
>
{totalPages}
</Button>
</>
)}
<Button
variant="outline"
size="sm"
onClick={() => onPageChange(currentPage + 1)}
disabled={currentPage === totalPages}
className="w-9 px-0"
>
<span className="material-symbols-outlined text-[18px]">chevron_right</span>
</Button>
</div>
)}
</div>
</div>
);
}