{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pagination-rich",
  "title": "Rich pagination",
  "description": "Styled pagination: the Rich set. 5 decorative pagination variations (SizesPagination, TotalPagination, PerPagePagination, InfoPagination, FullPagination) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2",
    "lucide-react@^1.23.0"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/pagination-rich.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronLeft, ChevronRight, MoreHorizontal } from \"lucide-react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Rich pagination family: 5 paginators that carry extra controls alongside page navigation: a per-page record selector, a total record counter, range information and a full bar combining all of them. Colour comes only from semantic tokens; alpha through color-mix. Motion is gated with useReducedMotion(); the framer layoutId derives from React.useId(). */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst controlSize: Record<StyledSize, string> = {\n  sm: \"h-8 text-sm\",\n  md: \"h-9 text-sm\",\n  lg: \"h-10 text-sm\",\n  xl: \"h-12 text-base\",\n}\n\nconst iconSize: Record<StyledSize, string> = {\n  sm: \"size-8\",\n  md: \"size-9\",\n  lg: \"size-10\",\n  xl: \"size-12\",\n}\n\nconst focusRing =\n  \"outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50\"\n\nconst btnBase =\n  \"relative inline-flex shrink-0 select-none items-center justify-center font-medium whitespace-nowrap transition-colors [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\n\nconst arrowBtn = \"rounded-lg border border-border text-foreground hover:bg-accent\"\n\nconst selectBase =\n  \"rounded-lg border border-border bg-background px-2 text-foreground\"\n\ninterface PaginationProps {\n  className?: string\n  size?: StyledSize\n  total?: number\n  defaultPage?: number\n}\n\n/* Dahili sayfa durumu. */\nfunction usePageState(total: number, defaultPage: number) {\n  const n = Math.max(total, 1)\n  const clamp = React.useCallback((p: number) => Math.min(Math.max(p, 1), n), [n])\n  const [current, setCurrent] = React.useState(() => clamp(defaultPage))\n  const goto = (p: number) => setCurrent(clamp(p))\n  return { current: clamp(current), total: n, goto }\n}\n\nfunction pageRange(current: number, total: number): (number | \"ellipsis\")[] {\n  if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1)\n  const out: (number | \"ellipsis\")[] = [1]\n  const left = Math.max(2, current - 1)\n  const right = Math.min(total - 1, current + 1)\n  if (left > 2) out.push(\"ellipsis\")\n  for (let p = left; p <= right; p++) out.push(p)\n  if (right < total - 1) out.push(\"ellipsis\")\n  out.push(total)\n  return out\n}\n\n/* Sayi butonlari serisi: aktif sayfa kayan primary vurgu ile. */\nfunction PageNumbers({\n  current,\n  total,\n  goto,\n  size,\n  uid,\n}: {\n  current: number\n  total: number\n  goto: (p: number) => void\n  size: StyledSize\n  uid: string\n}) {\n  const reduce = useReducedMotion()\n  const items = pageRange(current, total)\n  return (\n    <>\n      {items.map((it, i) =>\n        it === \"ellipsis\" ? (\n          <span\n            key={`e${i}`}\n            aria-hidden=\"true\"\n            className={cn(iconSize[size], \"inline-flex items-center justify-center text-muted-foreground\")}\n          >\n            <MoreHorizontal />\n          </span>\n        ) : (\n          <button\n            key={it}\n            type=\"button\"\n            aria-label={`Go to page ${it}`}\n            aria-current={it === current ? \"page\" : undefined}\n            onClick={() => goto(it)}\n            className={cn(\n              btnBase,\n              focusRing,\n              iconSize[size],\n              \"rounded-lg\",\n              it === current\n                ? \"text-primary-foreground\"\n                : \"text-foreground hover:bg-[color-mix(in_oklab,var(--color-foreground)_8%,transparent)]\"\n            )}\n          >\n            {it === current && (\n              <motion.span\n                layoutId={reduce ? undefined : `${uid}-rich-active`}\n                aria-hidden=\"true\"\n                transition={reduce ? { duration: 0 } : { type: \"spring\" as const, stiffness: 500, damping: 34 }}\n                className=\"absolute inset-0 rounded-lg bg-primary\"\n              />\n            )}\n            <span className=\"relative\">{it}</span>\n          </button>\n        )\n      )}\n    </>\n  )\n}\n\nconst perPageOptions = [10, 20, 50, 100]\n\n/* SizesPagination: sayfa basi kayit secici + sayi butonlari. */\nexport function SizesPagination({ className, size = \"md\", total = 12, defaultPage = 1 }: PaginationProps) {\n  const { current, total: n, goto } = usePageState(total, defaultPage)\n  const [perPage, setPerPage] = React.useState(20)\n  const uid = React.useId()\n  const selectId = React.useId()\n  return (\n    <nav\n      data-slot=\"styled-pagination\"\n      aria-label=\"Pagination\"\n      className={cn(\"flex flex-wrap items-center gap-3\", className)}\n    >\n      <span className=\"inline-flex items-center gap-2\">\n        <label htmlFor={selectId} className=\"text-sm text-muted-foreground\">\n          Rows per page\n        </label>\n        <select\n          id={selectId}\n          value={perPage}\n          onChange={(e) => setPerPage(Number(e.target.value))}\n          className={cn(controlSize[size], selectBase, focusRing)}\n        >\n          {perPageOptions.map((o) => (\n            <option key={o} value={o}>\n              {o}\n            </option>\n          ))}\n        </select>\n      </span>\n      <span className=\"inline-flex items-center gap-1\">\n        <button\n          type=\"button\"\n          aria-label=\"Go to previous page\"\n          disabled={current <= 1}\n          onClick={() => goto(current - 1)}\n          className={cn(btnBase, focusRing, iconSize[size], arrowBtn)}\n        >\n          <ChevronLeft />\n        </button>\n        <PageNumbers current={current} total={n} goto={goto} size={size} uid={uid} />\n        <button\n          type=\"button\"\n          aria-label=\"Go to next page\"\n          disabled={current >= n}\n          onClick={() => goto(current + 1)}\n          className={cn(btnBase, focusRing, iconSize[size], arrowBtn)}\n        >\n          <ChevronRight />\n        </button>\n      </span>\n    </nav>\n  )\n}\n\n/* TotalPagination: toplam kayit sayisi rozeti + sayi butonlari. */\nexport function TotalPagination({ className, size = \"md\", total = 12, defaultPage = 1 }: PaginationProps) {\n  const { current, total: n, goto } = usePageState(total, defaultPage)\n  const uid = React.useId()\n  const rows = n * 20\n  return (\n    <nav\n      data-slot=\"styled-pagination\"\n      aria-label=\"Pagination\"\n      className={cn(\"flex flex-wrap items-center gap-3\", className)}\n    >\n      <span\n        className={cn(\n          controlSize[size],\n          \"inline-flex items-center rounded-full bg-[color-mix(in_oklab,var(--color-primary)_12%,transparent)] px-3 tabular-nums text-foreground\"\n        )}\n      >\n        <span className=\"font-semibold\">{rows}</span>\n        <span className=\"ml-1 text-muted-foreground\">results</span>\n      </span>\n      <span className=\"inline-flex items-center gap-1\">\n        <button\n          type=\"button\"\n          aria-label=\"Go to previous page\"\n          disabled={current <= 1}\n          onClick={() => goto(current - 1)}\n          className={cn(btnBase, focusRing, iconSize[size], arrowBtn)}\n        >\n          <ChevronLeft />\n        </button>\n        <PageNumbers current={current} total={n} goto={goto} size={size} uid={uid} />\n        <button\n          type=\"button\"\n          aria-label=\"Go to next page\"\n          disabled={current >= n}\n          onClick={() => goto(current + 1)}\n          className={cn(btnBase, focusRing, iconSize[size], arrowBtn)}\n        >\n          <ChevronRight />\n        </button>\n      </span>\n    </nav>\n  )\n}\n\n/* PerPagePagination: sayfa basi secici, secim degisince ilk sayfaya doner. */\nexport function PerPagePagination({ className, size = \"md\", total = 12, defaultPage = 1 }: PaginationProps) {\n  const { current, total: n, goto } = usePageState(total, defaultPage)\n  const [perPage, setPerPage] = React.useState(10)\n  const selectId = React.useId()\n  return (\n    <nav\n      data-slot=\"styled-pagination\"\n      aria-label=\"Pagination\"\n      className={cn(\n        \"flex flex-wrap items-center gap-3 rounded-xl border border-border bg-card p-2\",\n        className\n      )}\n    >\n      <span className=\"inline-flex items-center gap-2\">\n        <label htmlFor={selectId} className=\"text-sm text-muted-foreground\">\n          Show\n        </label>\n        <select\n          id={selectId}\n          value={perPage}\n          onChange={(e) => {\n            setPerPage(Number(e.target.value))\n            goto(1)\n          }}\n          className={cn(controlSize[size], selectBase, focusRing)}\n        >\n          {perPageOptions.map((o) => (\n            <option key={o} value={o}>\n              {o} per page\n            </option>\n          ))}\n        </select>\n      </span>\n      <span className={cn(controlSize[size], \"inline-flex items-center tabular-nums text-muted-foreground\")}>\n        Page <span className=\"mx-1 font-medium text-foreground\">{current}</span> of{\" \"}\n        <span className=\"ml-1 font-medium text-foreground\">{n}</span>\n      </span>\n      <span className=\"inline-flex items-center gap-1\">\n        <button\n          type=\"button\"\n          aria-label=\"Go to previous page\"\n          disabled={current <= 1}\n          onClick={() => goto(current - 1)}\n          className={cn(btnBase, focusRing, iconSize[size], arrowBtn)}\n        >\n          <ChevronLeft />\n        </button>\n        <button\n          type=\"button\"\n          aria-label=\"Go to next page\"\n          disabled={current >= n}\n          onClick={() => goto(current + 1)}\n          className={cn(btnBase, focusRing, iconSize[size], arrowBtn)}\n        >\n          <ChevronRight />\n        </button>\n      </span>\n    </nav>\n  )\n}\n\n/* InfoPagination: gosterilen kayit araligi bilgisi + sayi butonlari. */\nexport function InfoPagination({ className, size = \"md\", total = 12, defaultPage = 2 }: PaginationProps) {\n  const { current, total: n, goto } = usePageState(total, defaultPage)\n  const uid = React.useId()\n  const perPage = 10\n  const rows = n * perPage\n  const from = (current - 1) * perPage + 1\n  const to = Math.min(current * perPage, rows)\n  return (\n    <nav\n      data-slot=\"styled-pagination\"\n      aria-label=\"Pagination\"\n      className={cn(\"flex flex-wrap items-center gap-3\", className)}\n    >\n      <span\n        aria-live=\"polite\"\n        className={cn(controlSize[size], \"inline-flex items-center tabular-nums text-muted-foreground\")}\n      >\n        Showing\n        <span className=\"mx-1 font-medium text-foreground\">\n          {from}-{to}\n        </span>\n        of <span className=\"ml-1 font-medium text-foreground\">{rows}</span>\n      </span>\n      <span className=\"inline-flex items-center gap-1\">\n        <button\n          type=\"button\"\n          aria-label=\"Go to previous page\"\n          disabled={current <= 1}\n          onClick={() => goto(current - 1)}\n          className={cn(btnBase, focusRing, iconSize[size], arrowBtn)}\n        >\n          <ChevronLeft />\n        </button>\n        <PageNumbers current={current} total={n} goto={goto} size={size} uid={uid} />\n        <button\n          type=\"button\"\n          aria-label=\"Go to next page\"\n          disabled={current >= n}\n          onClick={() => goto(current + 1)}\n          className={cn(btnBase, focusRing, iconSize[size], arrowBtn)}\n        >\n          <ChevronRight />\n        </button>\n      </span>\n    </nav>\n  )\n}\n\n/* FullPagination: range information plus a per-page selector plus number buttons plus first/last jumps. A complete bar for a table footer. */\nexport function FullPagination({ className, size = \"md\", total = 12, defaultPage = 3 }: PaginationProps) {\n  const { current, total: n, goto } = usePageState(total, defaultPage)\n  const [perPage, setPerPage] = React.useState(10)\n  const uid = React.useId()\n  const selectId = React.useId()\n  const rows = n * perPage\n  const from = (current - 1) * perPage + 1\n  const to = Math.min(current * perPage, rows)\n  const textBtn = cn(controlSize[size], \"rounded-lg border border-border px-3 text-foreground hover:bg-accent\")\n  return (\n    <nav\n      data-slot=\"styled-pagination\"\n      aria-label=\"Pagination\"\n      className={cn(\n        \"flex flex-wrap items-center gap-3 rounded-xl border border-border bg-card p-2\",\n        className\n      )}\n    >\n      <span\n        aria-live=\"polite\"\n        className={cn(controlSize[size], \"inline-flex items-center tabular-nums text-muted-foreground\")}\n      >\n        <span className=\"font-medium text-foreground\">\n          {from}-{to}\n        </span>\n        <span className=\"ml-1\">of {rows}</span>\n      </span>\n      <span className=\"inline-flex items-center gap-2\">\n        <label htmlFor={selectId} className=\"text-sm text-muted-foreground\">\n          Rows\n        </label>\n        <select\n          id={selectId}\n          value={perPage}\n          onChange={(e) => {\n            setPerPage(Number(e.target.value))\n            goto(1)\n          }}\n          className={cn(controlSize[size], selectBase, focusRing)}\n        >\n          {perPageOptions.map((o) => (\n            <option key={o} value={o}>\n              {o}\n            </option>\n          ))}\n        </select>\n      </span>\n      <span className=\"inline-flex items-center gap-1\">\n        <button\n          type=\"button\"\n          disabled={current <= 1}\n          onClick={() => goto(1)}\n          className={cn(btnBase, focusRing, textBtn)}\n        >\n          First\n        </button>\n        <button\n          type=\"button\"\n          aria-label=\"Go to previous page\"\n          disabled={current <= 1}\n          onClick={() => goto(current - 1)}\n          className={cn(btnBase, focusRing, iconSize[size], arrowBtn)}\n        >\n          <ChevronLeft />\n        </button>\n        <PageNumbers current={current} total={n} goto={goto} size={size} uid={uid} />\n        <button\n          type=\"button\"\n          aria-label=\"Go to next page\"\n          disabled={current >= n}\n          onClick={() => goto(current + 1)}\n          className={cn(btnBase, focusRing, iconSize[size], arrowBtn)}\n        >\n          <ChevronRight />\n        </button>\n        <button\n          type=\"button\"\n          disabled={current >= n}\n          onClick={() => goto(n)}\n          className={cn(btnBase, focusRing, textBtn)}\n        >\n          Last\n        </button>\n      </span>\n    </nav>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/pagination-rich.tsx"
    }
  ],
  "categories": [
    "styled",
    "pagination"
  ],
  "type": "registry:component"
}