Python 68.8%
TypeScript 18.6%
CSS 8.7%
JavaScript 3.3%
HTML 0.6%
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