SPB Git forge
28commits 1branches 0releases
7.7 MBsize
maindefault branch
10 days agolast push
Python 66.3% TypeScript 22.7% JavaScript 8.6% HTML 1.4% CSS 0.7%
2.2 KB · 51 lines tsx
Raw Blame History
1import Link from 'next/link';2import { cn } from '@/lib/cn';3import { fmtInt } from '@/lib/format';45/** URL-driven pagination (server component). `makeHref(page)` builds the link preserving current filters. */6export function Pagination({ total, page, pages, perPage, makeHref, className }: { total: number; page: number; pages: number; perPage: number; makeHref: (page: number) => string; className?: string }) {7  if (pages <= 1) return <p className={cn('tnum text-xs text-ink-3', className)}>{fmtInt(total)} results</p>;8  const from = (page - 1) * perPage + 1;9  const to = Math.min(total, page * perPage);10  const btn = 'inline-flex h-10 min-w-10 items-center justify-center border border-rule px-3 text-sm text-ink-2 hover:bg-surface-2 hover:text-ink';11  return (12    <nav className={cn('flex flex-wrap items-center justify-between gap-3', className)} aria-label="Pagination">13      <p className="tnum text-xs text-ink-3">14        {fmtInt(from)}–{fmtInt(to)} of {fmtInt(total)}15      </p>16      <div className="flex items-center gap-1.5">17        {page > 1 ? (18          <Link href={makeHref(page - 1)} className={btn} rel="prev">19            ‹ Prev20          </Link>21        ) : (22          <span className={cn(btn, 'opacity-40')}>‹ Prev</span>23        )}24        <span className="mono px-2 text-xs text-ink-3">25          {page} / {fmtInt(pages)}26        </span>27        {page < pages ? (28          <Link href={makeHref(page + 1)} className={btn} rel="next">29            Next ›30          </Link>31        ) : (32          <span className={cn(btn, 'opacity-40')}>Next ›</span>33        )}34      </div>35    </nav>36  );37}3839/** Build a query-string href preserving existing params (page reset unless patched). */40export function withParams(base: string, current: Record<string, string | undefined>, patch: Record<string, string | number | undefined | null>): string {41  const p = new URLSearchParams();42  for (const [k, v] of Object.entries(current)) if (v !== undefined && v !== '') p.set(k, v);43  for (const [k, v] of Object.entries(patch)) {44    if (v === undefined || v === null || v === '') p.delete(k);45    else p.set(k, String(v));46  }47  if (!('page' in patch)) p.delete('page');48  const s = p.toString();49  return s ? `${base}?${s}` : base;50}51