SPB Git forge

spb/ai-atlas

Public
41commits 1branches 0releases
4.6 MBsize
maindefault branch
12 days agolast push
HTML 77.2% TypeScript 10.5% Python 9.6% JavaScript 2.5%
2.3 KB · 52 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(offset)` builds the link preserving current filters. */6export function Pagination({ total, limit, offset, makeHref, className }: { total: number; limit: number; offset: number; makeHref: (offset: number) => string; className?: string }) {7  const pages = Math.max(1, Math.ceil(total / limit));8  const page = Math.floor(offset / limit) + 1;9  if (pages <= 1) return <p className={cn('tnum text-xs text-ink-3', className)}>{fmtInt(total)} results</p>;10  const from = offset + 1;11  const to = Math.min(total, offset + limit);12  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';13  return (14    <nav className={cn('flex flex-wrap items-center justify-between gap-3', className)} aria-label="Pagination">15      <p className="tnum text-xs text-ink-3">16        {fmtInt(from)}–{fmtInt(to)} of {fmtInt(total)}17      </p>18      <div className="flex items-center gap-1.5">19        {page > 1 ? (20          <Link href={makeHref(Math.max(0, offset - limit))} className={btn} rel="prev">21            ‹ Prev22          </Link>23        ) : (24          <span className={cn(btn, 'opacity-40')}>‹ Prev</span>25        )}26        <span className="mono px-2 text-xs text-ink-3">27          {page} / {fmtInt(pages)}28        </span>29        {page < pages ? (30          <Link href={makeHref(offset + limit)} className={btn} rel="next">31            Next ›32          </Link>33        ) : (34          <span className={cn(btn, 'opacity-40')}>Next ›</span>35        )}36      </div>37    </nav>38  );39}4041/** Build a query-string href preserving existing params. */42export function withParams(base: string, current: Record<string, string | undefined>, patch: Record<string, string | number | undefined | null>): string {43  const p = new URLSearchParams();44  for (const [k, v] of Object.entries(current)) if (v !== undefined && v !== '') p.set(k, v);45  for (const [k, v] of Object.entries(patch)) {46    if (v === undefined || v === null || v === '' || v === 0 || v === '0') p.delete(k);47    else p.set(k, String(v));48  }49  const s = p.toString();50  return s ? `${base}?${s}` : base;51}52