{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pagination-outline",
  "title": "Outline pagination",
  "description": "Styled pagination: the Outline set. 5 decorative pagination variations (RingPagination, DashedPagination, DoublePagination, ThickPagination, CornerPagination) 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-outline.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/* Outline pagination family: 5 edge-led paginators. The frame speaks instead of the fill: ring, dashed, double, thick and corner-marked. Colour comes only from semantic tokens; alpha through color-mix. The active page slides with a framer layoutId; the layoutId derives from React.useId(), so instances on the same page do not get mixed up. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\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\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/* Shared shell: the frame technique arrives through the idle and active classes. The active emphasis stays unfilled, only the edge is painted. */\nfunction OutlineShell({\n  props,\n  radius,\n  idleClassName,\n  activeClassName,\n  activeTextClassName,\n  arrowClassName,\n}: {\n  props: PaginationProps\n  radius: string\n  idleClassName: string\n  activeClassName: string\n  activeTextClassName: string\n  arrowClassName: string\n}) {\n  const { className, size = \"md\", total = 8, defaultPage = 1 } = props\n  const reduce = useReducedMotion()\n  const { current, total: n, goto } = usePageState(total, defaultPage)\n  const items = pageRange(current, n)\n  const uid = React.useId()\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        aria-label=\"Go to previous page\"\n        disabled={current <= 1}\n        onClick={() => goto(current - 1)}\n        className={cn(btnBase, focusRing, iconSize[size], radius, arrowClassName)}\n      >\n        <ChevronLeft />\n      </button>\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              radius,\n              it === current ? activeTextClassName : idleClassName\n            )}\n          >\n            {it === current && (\n              <motion.span\n                layoutId={reduce ? undefined : `${uid}-outline-active`}\n                aria-hidden=\"true\"\n                transition={reduce ? { duration: 0 } : { type: \"spring\" as const, stiffness: 500, damping: 34 }}\n                className={cn(\"absolute inset-0\", radius, activeClassName)}\n              />\n            )}\n            <span className=\"relative\">{it}</span>\n          </button>\n        )\n      )}\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], radius, arrowClassName)}\n      >\n        <ChevronRight />\n      </button>\n    </nav>\n  )\n}\n\n/* Ring: the active page is ringed with a primary ring, no fill. */\nexport function RingPagination(props: PaginationProps) {\n  return (\n    <OutlineShell\n      props={props}\n      radius=\"rounded-full\"\n      idleClassName=\"text-muted-foreground hover:text-foreground hover:bg-[color-mix(in_oklab,var(--color-foreground)_6%,transparent)]\"\n      activeClassName=\"ring-2 ring-primary ring-inset bg-[color-mix(in_oklab,var(--color-primary)_10%,transparent)]\"\n      activeTextClassName=\"text-foreground\"\n      arrowClassName=\"border border-border text-foreground hover:bg-accent\"\n    />\n  )\n}\n\n/* Dashed: a dashed frame; on the active page the dashes turn primary. */\nexport function DashedPagination(props: PaginationProps) {\n  return (\n    <OutlineShell\n      props={props}\n      radius=\"rounded-lg\"\n      idleClassName=\"border border-dashed border-border text-muted-foreground hover:border-[color-mix(in_oklab,var(--color-foreground)_35%,transparent)] hover:text-foreground\"\n      activeClassName=\"border-2 border-dashed border-primary\"\n      activeTextClassName=\"text-foreground\"\n      arrowClassName=\"border border-dashed border-border text-foreground hover:bg-accent\"\n    />\n  )\n}\n\n/* Double: ic ve dis olmak uzere iki katmanli cerceve. */\nexport function DoublePagination(props: PaginationProps) {\n  return (\n    <OutlineShell\n      props={props}\n      radius=\"rounded-md\"\n      idleClassName=\"border border-border text-muted-foreground hover:text-foreground hover:border-[color-mix(in_oklab,var(--color-foreground)_30%,transparent)]\"\n      activeClassName=\"border border-primary ring-1 ring-primary ring-offset-2 ring-offset-background\"\n      activeTextClassName=\"text-foreground\"\n      arrowClassName=\"border border-border text-foreground hover:bg-accent\"\n    />\n  )\n}\n\n/* Thick: kalin, iddiali kenar. */\nexport function ThickPagination(props: PaginationProps) {\n  return (\n    <OutlineShell\n      props={props}\n      radius=\"rounded-lg\"\n      idleClassName=\"border-2 border-[color-mix(in_oklab,var(--color-foreground)_18%,transparent)] text-muted-foreground hover:text-foreground hover:border-[color-mix(in_oklab,var(--color-foreground)_40%,transparent)]\"\n      activeClassName=\"border-[3px] border-primary\"\n      activeTextClassName=\"text-foreground font-semibold\"\n      arrowClassName=\"border-2 border-[color-mix(in_oklab,var(--color-foreground)_18%,transparent)] text-foreground hover:bg-accent\"\n    />\n  )\n}\n\n/* Corner: no border; the active page is marked with an underline plus primary. */\nexport function CornerPagination(props: PaginationProps) {\n  return (\n    <OutlineShell\n      props={props}\n      radius=\"rounded-none\"\n      idleClassName=\"border-b-2 border-transparent text-muted-foreground hover:border-[color-mix(in_oklab,var(--color-foreground)_25%,transparent)] hover:text-foreground\"\n      activeClassName=\"border-b-2 border-primary bg-[color-mix(in_oklab,var(--color-primary)_8%,transparent)]\"\n      activeTextClassName=\"text-foreground font-semibold\"\n      arrowClassName=\"border-b-2 border-transparent text-foreground hover:border-[color-mix(in_oklab,var(--color-foreground)_25%,transparent)]\"\n    />\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/pagination-outline.tsx"
    }
  ],
  "categories": [
    "styled",
    "pagination"
  ],
  "type": "registry:component"
}