62 lines
2.1 KiB
TypeScript
62 lines
2.1 KiB
TypeScript
import Link from "next/link";
|
|
import { toFaDigits } from "@/lib/format";
|
|
import { ChevronLeft, ChevronRight } from "../icons";
|
|
|
|
// Simple numbered pager. `buildHref` turns a page number into the URL for
|
|
// that page — callers own how query params are preserved (search, filters).
|
|
export default function Pagination({
|
|
page,
|
|
pageSize,
|
|
total,
|
|
buildHref,
|
|
}: {
|
|
page: number;
|
|
pageSize: number;
|
|
total: number;
|
|
buildHref: (page: number) => string;
|
|
}) {
|
|
const pageCount = Math.max(1, Math.ceil(total / pageSize));
|
|
if (pageCount <= 1) return null;
|
|
|
|
// A short window around the current page, plus first/last, keeps the
|
|
// control usable even with a few hundred pages.
|
|
const pages = new Set<number>([1, pageCount, page - 1, page, page + 1].filter((p) => p >= 1 && p <= pageCount));
|
|
const sorted = [...pages].sort((a, b) => a - b);
|
|
|
|
const linkClass = (active: boolean) =>
|
|
`flex h-9 min-w-9 items-center justify-center rounded-lg px-2 text-[13px] transition ${
|
|
active ? "bg-brand text-white" : "border border-line text-ink-soft hover:border-brand hover:text-brand"
|
|
}`;
|
|
|
|
return (
|
|
<nav className="mt-8 flex items-center justify-center gap-1.5" aria-label="صفحهبندی">
|
|
<Link
|
|
href={buildHref(Math.max(1, page - 1))}
|
|
aria-disabled={page <= 1}
|
|
className={`${linkClass(false)} ${page <= 1 ? "pointer-events-none opacity-40" : ""}`}
|
|
aria-label="صفحه قبل"
|
|
>
|
|
<ChevronRight className="size-4" />
|
|
</Link>
|
|
|
|
{sorted.map((p, i) => (
|
|
<span key={p} className="flex items-center gap-1.5">
|
|
{i > 0 && p - sorted[i - 1] > 1 && <span className="px-1 text-body">…</span>}
|
|
<Link href={buildHref(p)} className={linkClass(p === page)}>
|
|
{toFaDigits(p)}
|
|
</Link>
|
|
</span>
|
|
))}
|
|
|
|
<Link
|
|
href={buildHref(Math.min(pageCount, page + 1))}
|
|
aria-disabled={page >= pageCount}
|
|
className={`${linkClass(false)} ${page >= pageCount ? "pointer-events-none opacity-40" : ""}`}
|
|
aria-label="صفحه بعد"
|
|
>
|
|
<ChevronLeft className="size-4" />
|
|
</Link>
|
|
</nav>
|
|
);
|
|
}
|