/** * LANE-UI — build bar pagination (pure; see pages.test.ts). * * Round 2 orders: kind tabs, `[`/`]` to page, 1-9 within the active page, "every machine * hotkey-reachable at any catalog size". * * Kind tabs alone don't deliver that last clause: LANE-DATA's 21 machines include 14 * crafters, and 1-9 cannot reach 14. So a page is at most PER_PAGE machines, and a kind * group larger than that spills into numbered pages (REFINE 1/2, REFINE 2/2). That keeps * the invariant true no matter how big the catalog gets. */ import type { MachineDef, MachineKind } from '../contracts'; /** Hotkeys are 1-9, so a page can never hold more than nine. */ export const PER_PAGE = 9; /** Kinds collapse into these tabs, in house order: dig, move, refine, power, ship. */ const GROUP_OF: Record = { extractor: 'EXTRACT', belt: 'LOGISTICS', splitter: 'LOGISTICS', buffer: 'LOGISTICS', crafter: 'REFINE', power: 'POWER', shipper: 'SHIP', lab: 'RESEARCH', // v4 kind }; const GROUP_ORDER = ['EXTRACT', 'LOGISTICS', 'REFINE', 'POWER', 'SHIP']; export interface BuildPage { /** Tab label, e.g. "REFINE" or "REFINE 2/2". */ label: string; machines: MachineDef[]; } export function paginate(machines: MachineDef[], perPage: number = PER_PAGE): BuildPage[] { const groups = new Map(); for (const m of machines) { // An unknown kind still gets a home rather than vanishing from the bar. const g = GROUP_OF[m.kind] ?? 'OTHER'; const list = groups.get(g); if (list) list.push(m); else groups.set(g, [m]); } const names = [...groups.keys()].sort((a, b) => { const ia = GROUP_ORDER.indexOf(a); const ib = GROUP_ORDER.indexOf(b); // Unknown groups sort last, alphabetically, rather than jumping the queue. if (ia === -1 && ib === -1) return a < b ? -1 : a > b ? 1 : 0; if (ia === -1) return 1; if (ib === -1) return -1; return ia - ib; }); const pages: BuildPage[] = []; for (const name of names) { const list = groups.get(name)!; const chunks = Math.max(1, Math.ceil(list.length / perPage)); for (let i = 0; i < chunks; i++) { pages.push({ label: chunks > 1 ? `${name} ${i + 1}/${chunks}` : name, machines: list.slice(i * perPage, (i + 1) * perPage), }); } } return pages; } /** Page index holding `defId`, or -1. Used to follow a click-selection to its tab. */ export function pageOf(pages: BuildPage[], defId: string): number { return pages.findIndex((p) => p.machines.some((m) => m.id === defId)); }