{
  "name": "pagination",
  "dependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "pagination.tsx",
      "content": "\"use client\";\r\n\r\nimport { css, themeVars as theme } from \"@yugnex/core\";\r\nimport { useMemo, type HTMLAttributes } from \"react\";\r\n\r\nconst navClass = css({ fontFamily: theme.fontFamily.sans });\r\n\r\nconst listClass = css({\r\n  display: \"flex\",\r\n  alignItems: \"center\",\r\n  gap: theme.space[1],\r\n  margin: 0,\r\n  padding: 0,\r\n  listStyle: \"none\",\r\n});\r\n\r\nconst buttonClass = css({\r\n  display: \"inline-flex\",\r\n  alignItems: \"center\",\r\n  justifyContent: \"center\",\r\n  minWidth: \"2rem\",\r\n  height: \"2rem\",\r\n  padding: `0 ${theme.space[2]}`,\r\n  borderRadius: theme.radius.sm,\r\n  border: `1px solid transparent`,\r\n  backgroundColor: \"transparent\",\r\n  color: theme.color.mutedForeground,\r\n  fontFamily: theme.fontFamily.sans,\r\n  fontSize: theme.fontSize.sm,\r\n  fontVariantNumeric: \"tabular-nums\",\r\n  cursor: \"pointer\",\r\n  transitionProperty: \"background-color, color, border-color\",\r\n  transitionDuration: theme.duration.fast,\r\n  \"&:hover:not(:disabled)\": { backgroundColor: theme.color.muted, color: theme.color.foreground },\r\n  \"&:focus-visible\": { outline: `2px solid ${theme.color.ring}`, outlineOffset: \"1px\" },\r\n  \"&:disabled\": { opacity: 0.4, cursor: \"not-allowed\" },\r\n  '&[aria-current=\"page\"]': {\r\n    backgroundColor: theme.color.primary,\r\n    borderColor: theme.color.primary,\r\n    color: theme.color.primaryForeground,\r\n    fontWeight: theme.fontWeight.medium,\r\n  },\r\n});\r\n\r\nconst ellipsisClass = css({\r\n  display: \"inline-flex\",\r\n  alignItems: \"center\",\r\n  justifyContent: \"center\",\r\n  minWidth: \"2rem\",\r\n  height: \"2rem\",\r\n  color: theme.color.mutedForeground,\r\n  userSelect: \"none\",\r\n});\r\n\r\nexport interface PaginationProps extends Omit<HTMLAttributes<HTMLElement>, \"onChange\"> {\r\n  page: number;\r\n  totalPages: number;\r\n  onPageChange: (page: number) => void;\r\n  /** How many pages to show either side of the current one before collapsing. */\r\n  siblingCount?: number;\r\n  label?: string;\r\n}\r\n\r\n/** Builds the visible page list, inserting \"…\" where the range is collapsed. */\r\nfunction buildRange(page: number, totalPages: number, siblingCount: number): Array<number | \"gap\"> {\r\n  // First, last, current, and `siblingCount` on each side, plus two gap slots.\r\n  const maxVisible = siblingCount * 2 + 5;\r\n  if (totalPages <= maxVisible) return Array.from({ length: totalPages }, (_, i) => i + 1);\r\n\r\n  const left = Math.max(page - siblingCount, 1);\r\n  const right = Math.min(page + siblingCount, totalPages);\r\n  const showLeftGap = left > 2;\r\n  const showRightGap = right < totalPages - 1;\r\n\r\n  const range: Array<number | \"gap\"> = [1];\r\n  if (showLeftGap) range.push(\"gap\");\r\n  for (let i = Math.max(left, 2); i <= Math.min(right, totalPages - 1); i++) range.push(i);\r\n  if (showRightGap) range.push(\"gap\");\r\n  range.push(totalPages);\r\n  return range;\r\n}\r\n\r\n/** Page navigation with collapsing ranges for long sets. */\r\nexport function Pagination({\r\n  page,\r\n  totalPages,\r\n  onPageChange,\r\n  siblingCount = 1,\r\n  label = \"Pagination\",\r\n  className,\r\n  ...props\r\n}: PaginationProps) {\r\n  const range = useMemo(() => buildRange(page, totalPages, siblingCount), [page, totalPages, siblingCount]);\r\n\r\n  if (totalPages <= 1) return null;\r\n\r\n  return (\r\n    <nav aria-label={label} className={className ? `${navClass} ${className}` : navClass} {...props}>\r\n      <ul className={listClass}>\r\n        <li>\r\n          <button\r\n            type=\"button\"\r\n            className={buttonClass}\r\n            aria-label=\"Previous page\"\r\n            disabled={page <= 1}\r\n            onClick={() => onPageChange(page - 1)}\r\n          >\r\n            <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" aria-hidden=\"true\">\r\n              <path d=\"M8.5 3L4.5 7L8.5 11\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\r\n            </svg>\r\n          </button>\r\n        </li>\r\n\r\n        {range.map((entry, index) =>\r\n          entry === \"gap\" ? (\r\n            <li key={`gap-${index}`} className={ellipsisClass} aria-hidden=\"true\">\r\n              …\r\n            </li>\r\n          ) : (\r\n            <li key={entry}>\r\n              <button\r\n                type=\"button\"\r\n                className={buttonClass}\r\n                aria-label={`Page ${entry}`}\r\n                aria-current={entry === page ? \"page\" : undefined}\r\n                onClick={() => onPageChange(entry)}\r\n              >\r\n                {entry}\r\n              </button>\r\n            </li>\r\n          ),\r\n        )}\r\n\r\n        <li>\r\n          <button\r\n            type=\"button\"\r\n            className={buttonClass}\r\n            aria-label=\"Next page\"\r\n            disabled={page >= totalPages}\r\n            onClick={() => onPageChange(page + 1)}\r\n          >\r\n            <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" aria-hidden=\"true\">\r\n              <path d=\"M5.5 3L9.5 7L5.5 11\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\r\n            </svg>\r\n          </button>\r\n        </li>\r\n      </ul>\r\n    </nav>\r\n  );\r\n}\r\n",
      "type": "registry:component"
    }
  ]
}