{
  "$schema": "https://abackui.com/schema/registry-item.json",
  "name": "pagination",
  "type": "registry:ui",
  "title": "Pagination",
  "description": "Page controls with a current-page marker, collapsing ranges, and link mode.",
  "registryDependencies": [
    "https://abackui.com/r/utils.json"
  ],
  "categories": [
    "Navigation"
  ],
  "docs": "https://abackui.com/docs/components/pagination",
  "meta": {
    "status": "beta"
  },
  "files": [
    {
      "path": "components/ui/pagination.tsx",
      "content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface PaginationLabels {\n  previous?: string;\n  next?: string;\n  navigation?: string;\n}\n\nexport interface PaginationProps extends Omit<React.ComponentProps<\"nav\">, \"onChange\"> {\n  page: number;\n  pageCount: number;\n  onPageChange?: (page: number) => void;\n  /** Render links instead of buttons. Use this for server-rendered pages. */\n  hrefFor?: (page: number) => string;\n  siblingCount?: number;\n  labels?: PaginationLabels;\n}\n\ntype PageItem = number | \"ellipsis\";\n\nfunction range(start: number, end: number): number[] {\n  return Array.from({ length: Math.max(0, end - start + 1) }, (_, index) => start + index);\n}\n\nexport function getPageItems(\n  page: number,\n  pageCount: number,\n  siblingCount = 1,\n): PageItem[] {\n  const total = Math.max(1, pageCount);\n  const current = Math.min(Math.max(1, page), total);\n  const visible = siblingCount * 2 + 5;\n\n  if (total <= visible) return range(1, total);\n\n  const left = Math.max(2, current - siblingCount);\n  const right = Math.min(total - 1, current + siblingCount);\n  const showLeftEllipsis = left > 2;\n  const showRightEllipsis = right < total - 1;\n\n  if (!showLeftEllipsis && showRightEllipsis) {\n    const count = 3 + siblingCount * 2;\n    return [...range(1, count), \"ellipsis\", total];\n  }\n\n  if (showLeftEllipsis && !showRightEllipsis) {\n    const count = 3 + siblingCount * 2;\n    return [1, \"ellipsis\", ...range(total - count + 1, total)];\n  }\n\n  return [1, \"ellipsis\", ...range(left, right), \"ellipsis\", total];\n}\n\nconst base =\n  \"inline-flex h-8 min-w-8 items-center justify-center border px-2 text-sm font-medium tabular-nums transition-colors outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\";\n\nfunction Pagination({\n  page,\n  pageCount,\n  onPageChange,\n  hrefFor,\n  siblingCount = 1,\n  labels,\n  className,\n  ...props\n}: PaginationProps) {\n  const total = Math.max(1, pageCount);\n  const current = Math.min(Math.max(1, page), total);\n  const items = getPageItems(current, total, siblingCount);\n\n  function renderItem(target: number, label: React.ReactNode, key: string, disabled = false) {\n    const isCurrent = target === current;\n\n    if (isCurrent) {\n      return (\n        <span\n          key={key}\n          aria-current=\"page\"\n          className={cn(base, \"border-primary bg-primary text-primary-foreground\")}\n        >\n          {label}\n        </span>\n      );\n    }\n\n    if (disabled) {\n      return (\n        <span\n          key={key}\n          aria-disabled=\"true\"\n          className={cn(base, \"border-border text-muted-foreground opacity-50\")}\n        >\n          {label}\n        </span>\n      );\n    }\n\n    const common = {\n      \"aria-label\": typeof label === \"string\" ? label : `Go to page ${label}`,\n      className: cn(base, \"border-border bg-background text-foreground hover:bg-muted\"),\n    };\n\n    if (hrefFor) {\n      return (\n        <a key={key} href={hrefFor(target)} {...common}>\n          {label}\n        </a>\n      );\n    }\n\n    return (\n      <button key={key} type=\"button\" onClick={() => onPageChange?.(target)} {...common}>\n        {label}\n      </button>\n    );\n  }\n\n  return (\n    <nav\n      aria-label={labels?.navigation ?? \"Pagination\"}\n      className={cn(\"flex flex-wrap items-center gap-1\", className)}\n      {...props}\n    >\n      {renderItem(current - 1, labels?.previous ?? \"Previous\", \"previous\", current <= 1)}\n      {items.map((item, index) =>\n        item === \"ellipsis\" ? (\n          <span\n            key={`ellipsis-${index}`}\n            aria-hidden=\"true\"\n            className=\"inline-flex size-8 items-center justify-center text-sm text-muted-foreground\"\n          >\n            …\n          </span>\n        ) : (\n          renderItem(item, item, `page-${item}`)\n        ),\n      )}\n      {renderItem(current + 1, labels?.next ?? \"Next\", \"next\", current >= total)}\n    </nav>\n  );\n}\n\nexport { Pagination };\n",
      "type": "registry:ui",
      "target": "components/ui/pagination.tsx"
    }
  ]
}
