SPB Git forge

spb/rent-ka

Public
8commits 1branches 0releases
7.4 MBsize
maindefault branch
19 days agolast push
Python 68.8% TypeScript 18.6% CSS 8.7% JavaScript 3.3% HTML 0.6%
2.5 KB · 74 lines tsx
Raw Blame History
1// -----------------------------------------------------------------------------2// Rent-Ka — Rental listings aggregator (Canada, outside Québec)3// Author: Simon-Pierre Boucher — contact@spboucher.ai4// components/Pager.tsx: editorial pagination (12 listings/page)5//   Previous / numbers with ellipses / Next + "X–Y of N" recap.6// -----------------------------------------------------------------------------7import { IcoChevronLeft, IcoChevronRight } from "./Icons";89/** 1 … 4 [5] 6 … 12 — window around the current page, bounds always visible */10function pageItems(page: number, pages: number): (number | "…")[] {11  if (pages <= 7) return Array.from({ length: pages }, (_, i) => i + 1);12  const win = new Set<number>([1, 2, page - 1, page, page + 1, pages - 1, pages]);13  const items: (number | "…")[] = [];14  let prev = 0;15  for (let n = 1; n <= pages; n++) {16    if (!win.has(n)) continue;17    if (prev && n - prev > 1) items.push("…");18    items.push(n);19    prev = n;20  }21  return items;22}2324export default function Pager({ page, pageSize, total, onPage }: {25  page: number;26  pageSize: number;27  total: number;28  onPage: (p: number) => void;29}) {30  const pages = Math.max(1, Math.ceil(total / pageSize));31  if (pages <= 1) return null;32  const from = (page - 1) * pageSize + 1;33  const to = Math.min(page * pageSize, total);34  return (35    <nav className="pager" aria-label="Results pagination">36      <span className="pager-info">37        {from}–{to} <em>of</em> {total.toLocaleString("en-CA")}38      </span>39      <div className="pager-nav">40        <button41          className="pager-btn pager-arrow"42          disabled={page <= 1}43          onClick={() => onPage(page - 1)}44          aria-label="Previous page"45        >46          <IcoChevronLeft size={14} />47        </button>48        {pageItems(page, pages).map((it, i) =>49          it === "…" ? (50            <span key={`e${i}`} className="pager-ellipsis" aria-hidden="true">…</span>51          ) : (52            <button53              key={it}54              className={`pager-btn ${it === page ? "on" : ""}`}55              aria-current={it === page ? "page" : undefined}56              onClick={() => it !== page && onPage(it)}57            >58              {it}59            </button>60          ),61        )}62        <button63          className="pager-btn pager-arrow"64          disabled={page >= pages}65          onClick={() => onPage(page + 1)}66          aria-label="Next page"67        >68          <IcoChevronRight size={14} />69        </button>70      </div>71    </nav>72  );73}74