{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pagination-compact",
  "title": "Compact pagination",
  "description": "Styled pagination: the Compact set. 5 decorative pagination variations (InputPagination, JumpPagination, RangePagination, SimplePagination, CounterPagination) 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-compact.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronLeft, ChevronRight } from \"lucide-react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Compact pagination family: 5 dense paginators. Instead of laying out number buttons they do the same job in a narrow space: a jump-to-page input, first/last jumps, a record range, a plain prev/next and a counter. Colour comes only from semantic tokens; alpha through color-mix. Motion is gated with useReducedMotion(). */\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\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\n/* InputPagination: a number input for jumping straight to a page. The input is typed into freely and is clamped and applied on blur or Enter. */\nexport function InputPagination({ className, size = \"md\", total = 12, defaultPage = 1 }: PaginationProps) {\n  const { current, total: n, goto } = usePageState(total, defaultPage)\n  const [draft, setDraft] = React.useState(String(current))\n  const inputId = React.useId()\n\n  React.useEffect(() => {\n    setDraft(String(current))\n  }, [current])\n\n  const commit = () => {\n    const parsed = Number.parseInt(draft, 10)\n    if (Number.isNaN(parsed)) {\n      setDraft(String(current))\n      return\n    }\n    goto(parsed)\n    setDraft(String(Math.min(Math.max(parsed, 1), n)))\n  }\n\n  return (\n    <nav\n      data-slot=\"styled-pagination\"\n      aria-label=\"Pagination\"\n      className={cn(\"inline-flex items-center gap-2\", className)}\n    >\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      <label htmlFor={inputId} className=\"text-sm text-muted-foreground\">\n        Page\n      </label>\n      <input\n        id={inputId}\n        type=\"number\"\n        inputMode=\"numeric\"\n        min={1}\n        max={n}\n        value={draft}\n        aria-label=\"Jump to page\"\n        onChange={(e) => setDraft(e.target.value)}\n        onBlur={commit}\n        onKeyDown={(e) => {\n          if (e.key === \"Enter\") {\n            e.preventDefault()\n            commit()\n          }\n        }}\n        className={cn(\n          controlSize[size],\n          focusRing,\n          \"w-14 rounded-lg border border-border bg-background px-2 text-center tabular-nums text-foreground\"\n        )}\n      />\n      <span className={cn(controlSize[size], \"inline-flex items-center text-sm text-muted-foreground\")}>of {n}</span>\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    </nav>\n  )\n}\n\n/* JumpPagination: prev/next yaninda ilk ve son sayfaya atlama. */\nexport function JumpPagination({ className, size = \"md\", total = 12, defaultPage = 5 }: PaginationProps) {\n  const { current, total: n, goto } = usePageState(total, defaultPage)\n  const textBtn = cn(\n    controlSize[size],\n    \"rounded-lg border border-border px-3 text-foreground hover:bg-accent\"\n  )\n  return (\n    <nav\n      data-slot=\"styled-pagination\"\n      aria-label=\"Pagination\"\n      className={cn(\"inline-flex items-center gap-1.5\", className)}\n    >\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      <span\n        aria-current=\"page\"\n        className={cn(\n          controlSize[size],\n          \"inline-flex min-w-9 items-center justify-center rounded-lg bg-[color-mix(in_oklab,var(--color-primary)_14%,transparent)] px-2 font-medium tabular-nums text-foreground\"\n        )}\n      >\n        {current}\n      </span>\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    </nav>\n  )\n}\n\n/* RangePagination: gosterilen kayit araligi (\"1-10 of 120\") + oklar. */\nexport function RangePagination({ className, size = \"md\", total = 12, defaultPage = 1 }: PaginationProps) {\n  const { current, total: n, goto } = usePageState(total, defaultPage)\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(\"inline-flex items-center gap-3\", className)}\n    >\n      <span className={cn(controlSize[size], \"inline-flex items-center tabular-nums text-muted-foreground\")}>\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-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/* SimplePagination: Previous / Next text buttons only. */\nexport function SimplePagination({ className, size = \"md\", total = 12, defaultPage = 1 }: PaginationProps) {\n  const { current, total: n, goto } = usePageState(total, defaultPage)\n  const textBtn = cn(\n    controlSize[size],\n    \"gap-1.5 rounded-lg border border-border px-3 text-foreground hover:bg-accent\"\n  )\n  return (\n    <nav\n      data-slot=\"styled-pagination\"\n      aria-label=\"Pagination\"\n      className={cn(\"inline-flex items-center gap-2\", className)}\n    >\n      <button\n        type=\"button\"\n        disabled={current <= 1}\n        onClick={() => goto(current - 1)}\n        className={cn(btnBase, focusRing, textBtn)}\n      >\n        <ChevronLeft />\n        Previous\n      </button>\n      <button\n        type=\"button\"\n        disabled={current >= n}\n        onClick={() => goto(current + 1)}\n        className={cn(btnBase, focusRing, textBtn)}\n      >\n        Next\n        <ChevronRight />\n      </button>\n    </nav>\n  )\n}\n\n/* CounterPagination: a \"Page 3 of 12\" counter; it transitions softly when the number\n   changes. */\nexport function CounterPagination({ className, size = \"md\", total = 12, defaultPage = 3 }: PaginationProps) {\n  const reduce = useReducedMotion()\n  const { current, total: n, goto } = usePageState(total, defaultPage)\n  return (\n    <nav\n      data-slot=\"styled-pagination\"\n      aria-label=\"Pagination\"\n      className={cn(\n        \"inline-flex items-center gap-1 rounded-lg border border-border bg-card p-1\",\n        className\n      )}\n    >\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], \"rounded-md text-foreground hover:bg-accent\")}\n      >\n        <ChevronLeft />\n      </button>\n      <span\n        aria-live=\"polite\"\n        className={cn(controlSize[size], \"inline-flex items-center gap-1 px-2 tabular-nums text-muted-foreground\")}\n      >\n        Page\n        <motion.span\n          key={current}\n          initial={reduce ? false : { opacity: 0, y: -4 }}\n          animate={reduce ? {} : { opacity: 1, y: 0 }}\n          transition={reduce ? { duration: 0 } : { duration: 0.18, ease: \"easeOut\" }}\n          className=\"font-semibold text-foreground\"\n        >\n          {current}\n        </motion.span>\n        of <span className=\"font-medium text-foreground\">{n}</span>\n      </span>\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], \"rounded-md text-foreground hover:bg-accent\")}\n      >\n        <ChevronRight />\n      </button>\n    </nav>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/pagination-compact.tsx"
    }
  ],
  "categories": [
    "styled",
    "pagination"
  ],
  "type": "registry:component"
}