{"version":3,"file":"Pagination.cjs","names":[],"sources":["../../../src/components/Pagination/Pagination.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — page/totalPages/onPageChange is the pager,\n * pageSize/onPageSizeChange/ pageSizeOptions is the size control that sits in the\n * same bar, and totalItems plus siblingCount decide the copy and how many page\n * buttons are rendered. They ship together because a footer that pages but cannot\n * resize is half a footer.\n */\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport styles from \"./Pagination.module.css\";\n\nexport interface PaginationProps {\n    page: number;\n    totalPages: number;\n    onPageChange: (page: number) => void;\n    pageSize?: number;\n    onPageSizeChange?: (size: number) => void;\n    pageSizeOptions?: number[];\n    /** Total items count; if provided, renders the summary text. */\n    totalItems?: number;\n    /** Pages kept around the current one; ~`siblingCount + 4` buttons render. Default 3. */\n    siblingCount?: number;\n    /**\n     * Below 640px, drop to `‹` / `›` plus the summary and hide the numbered\n     * buttons and the size select. Default `true`.\n     *\n     * The default suits a desktop-first app, and costs the thing pagination is\n     * for on a phone: reaching page 7 becomes six taps on \"next\" instead of one\n     * tap on `7`. Pass `false` in a mobile-first app and the numbers stay at\n     * every width — the row scrolls horizontally by itself rather than pushing\n     * the page wider, and the current page is scrolled into view when it\n     * changes.\n     */\n    compactOnMobile?: boolean;\n    className?: string;\n}\n\nfunction buildRange(page: number, totalPages: number, siblings: number): (number | \"...\")[] {\n    const totalSlots = siblings + 4;\n    if (totalPages <= totalSlots) {\n        return Array.from({ length: totalPages }, (_, i) => i + 1);\n    }\n    const start = Math.max(2, page - Math.floor(siblings / 2));\n    const end = Math.min(totalPages - 1, start + siblings - 1);\n    const range: (number | \"...\")[] = [1];\n    if (start > 2) range.push(\"...\");\n    for (let i = start; i <= end; i++) range.push(i);\n    if (end < totalPages - 1) range.push(\"...\");\n    range.push(totalPages);\n    return range;\n}\n\n/**\n * Numeric pagination controls. Pair with {@link usePagination} for state.\n *\n * Narrow screens collapse to `‹` / `›` plus the summary by default, which turns\n * random access into sequential access — fine for a desktop-first app, wrong for\n * one whose traffic is phones. `compactOnMobile={false}` keeps the numbers at\n * every width: the row scrolls on its own instead of widening the page, and the\n * effect below keeps the current page inside the visible part of that row.\n *\n * The scroll is why this is one boolean rather than a `variant`. Simply\n * un-hiding the buttons would push a nine-button row past a 360px viewport and\n * give the whole document a horizontal scrollbar — the opt-out has to carry its\n * own layout, so it does.\n */\nexport function Pagination({\n    page,\n    totalPages,\n    onPageChange,\n    pageSize,\n    onPageSizeChange,\n    pageSizeOptions = [10, 25, 50, 100],\n    totalItems,\n    siblingCount = 3,\n    compactOnMobile = true,\n    className,\n}: PaginationProps) {\n    const pages = useMemo(\n        () => buildRange(page, totalPages, siblingCount),\n        [page, totalPages, siblingCount],\n    );\n    const activePage = useRef<HTMLButtonElement | null>(null);\n\n    useEffect(() => {\n        if (compactOnMobile) return;\n        activePage.current?.scrollIntoView?.({ block: \"nearest\", inline: \"nearest\" });\n    }, [page, compactOnMobile]);\n\n    if (totalPages <= 1 && !onPageSizeChange) return null;\n\n    return (\n        <div className={cn(styles.wrapper, className)} data-compact={String(compactOnMobile)}>\n            <div className={styles.summary}>\n                {typeof totalItems === \"number\"\n                    ? `${totalItems} resultado${totalItems === 1 ? \"\" : \"s\"}`\n                    : `Página ${page} de ${totalPages}`}\n            </div>\n            <div className={styles.controls}>\n                <button\n                    type=\"button\"\n                    className={styles.page}\n                    onClick={() => onPageChange(Math.max(1, page - 1))}\n                    disabled={page <= 1}\n                    aria-label=\"Página anterior\"\n                >\n                    ‹\n                </button>\n                {pages.map((entry, index) =>\n                    entry === \"...\" ? (\n                        <span\n                            key={`ellipsis-${index}`}\n                            className={cn(styles.ellipsis, styles.numeric)}\n                        >\n                            …\n                        </span>\n                    ) : (\n                        <button\n                            type=\"button\"\n                            key={entry}\n                            ref={entry === page ? activePage : undefined}\n                            className={cn(\n                                styles.page,\n                                styles.numeric,\n                                entry === page && styles.active,\n                            )}\n                            onClick={() => onPageChange(entry)}\n                            aria-current={entry === page ? \"page\" : undefined}\n                        >\n                            {entry}\n                        </button>\n                    ),\n                )}\n                <button\n                    type=\"button\"\n                    className={styles.page}\n                    onClick={() => onPageChange(Math.min(totalPages, page + 1))}\n                    disabled={page >= totalPages}\n                    aria-label=\"Próxima página\"\n                >\n                    ›\n                </button>\n                {onPageSizeChange && (\n                    <select\n                        className={styles.sizeSelect}\n                        value={pageSize}\n                        onChange={(event) => onPageSizeChange(Number(event.target.value))}\n                        aria-label=\"Itens por página\"\n                    >\n                        {pageSizeOptions.map((opt) => (\n                            <option key={opt} value={opt}>\n                                {opt} / página\n                            </option>\n                        ))}\n                    </select>\n                )}\n            </div>\n        </div>\n    );\n}\n"],"mappings":"iIAqCA,SAAS,EAAW,EAAc,EAAoB,EAAsC,CAExF,GAAI,GADe,EAAW,EAE1B,OAAO,MAAM,KAAK,CAAE,OAAQ,CAAW,GAAI,EAAG,IAAM,EAAI,CAAC,EAE7D,IAAM,EAAQ,KAAK,IAAI,EAAG,EAAO,KAAK,MAAM,EAAW,CAAC,CAAC,EACnD,EAAM,KAAK,IAAI,EAAa,EAAG,EAAQ,EAAW,CAAC,EACnD,EAA4B,CAAC,CAAC,EAChC,EAAQ,GAAG,EAAM,KAAK,KAAK,EAC/B,IAAK,IAAI,EAAI,EAAO,GAAK,EAAK,IAAK,EAAM,KAAK,CAAC,EAG/C,OAFI,EAAM,EAAa,GAAG,EAAM,KAAK,KAAK,EAC1C,EAAM,KAAK,CAAU,EACd,CACX,CAgBA,SAAgB,EAAW,CACvB,OACA,aACA,eACA,WACA,mBACA,kBAAkB,CAAC,GAAI,GAAI,GAAI,GAAG,EAClC,aACA,eAAe,EACf,kBAAkB,GAClB,aACgB,CAChB,IAAM,GAAA,EAAQ,EAAA,QAAA,KACJ,EAAW,EAAM,EAAY,CAAY,EAC/C,CAAC,EAAM,EAAY,CAAY,CACnC,EACM,GAAA,EAAa,EAAA,OAAA,CAAiC,IAAI,EASxD,OAPA,EAAA,EAAA,UAAA,KAAgB,CACR,GACJ,EAAW,SAAS,iBAAiB,CAAE,MAAO,UAAW,OAAQ,SAAU,CAAC,CAChF,EAAG,CAAC,EAAM,CAAe,CAAC,EAEtB,GAAc,GAAK,CAAC,EAAyB,MAG7C,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,CAAS,EAAG,eAAc,OAAO,CAAe,EAAnF,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,QAClB,SAAA,OAAO,GAAe,SACjB,GAAG,EAAW,YAAY,IAAe,EAAI,GAAK,MAClD,UAAU,EAAK,MAAM,GAC1B,CAAA,GACL,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,SAAvB,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,KAClB,YAAe,EAAa,KAAK,IAAI,EAAG,EAAO,CAAC,CAAC,EACjD,SAAU,GAAQ,EAClB,aAAW,kBACd,SAAA,GAEO,CAAA,EACP,EAAM,KAAK,EAAO,IACf,IAAU,OACN,EAAA,EAAA,IAAA,CAAC,OAAD,CAEI,UAAW,EAAA,GAAG,EAAA,QAAO,SAAU,EAAA,QAAO,OAAO,EAChD,SAAA,GAEK,EAJG,YAAY,GAIf,GAEN,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SAEL,IAAK,IAAU,EAAO,EAAa,IAAA,GACnC,UAAW,EAAA,GACP,EAAA,QAAO,KACP,EAAA,QAAO,QACP,IAAU,GAAQ,EAAA,QAAO,MAC7B,EACA,YAAe,EAAa,CAAK,EACjC,eAAc,IAAU,EAAO,OAAS,IAAA,GAEvC,SAAA,CACG,EAXC,CAWD,CAEhB,GACA,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,KAClB,YAAe,EAAa,KAAK,IAAI,EAAY,EAAO,CAAC,CAAC,EAC1D,SAAU,GAAQ,EAClB,aAAW,iBACd,SAAA,GAEO,CAAA,EACP,IACG,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,UAAW,EAAA,QAAO,WAClB,MAAO,EACP,SAAW,GAAU,EAAiB,OAAO,EAAM,OAAO,KAAK,CAAC,EAChE,aAAW,mBAEV,SAAA,EAAgB,IAAK,IAClB,EAAA,EAAA,KAAA,CAAC,SAAD,CAAkB,MAAO,EAAzB,SAAA,CACK,EAAI,WACD,CAFK,EAAA,CAEL,CACX,CACG,CAAA,CAEX,CACJ,CAAA,CAAA,GAEb"}