{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "copy-button",
  "title": "Copy Button",
  "description": "Copy-to-clipboard button whose icon twists into a check.",
  "dependencies": [
    "class-variance-authority",
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.cuelark.com/r/theme.json",
    "https://ui.cuelark.com/r/button.json",
    "https://ui.cuelark.com/r/motion.json"
  ],
  "files": [
    {
      "path": "src/registry/cuelark/ui/copy-button.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { CheckIcon, CopyIcon } from \"lucide-react\";\nimport type { VariantProps } from \"class-variance-authority\";\n\nimport { gsap, motionScale } from \"@/lib/motion\";\nimport { buttonVariants } from \"@/registry/cuelark/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\ntype CopyButtonProps = Omit<React.ComponentProps<\"button\">, \"value\"> &\n  VariantProps<typeof buttonVariants> & {\n    /** The text to copy. */\n    value: string;\n    /** A visible label next to the icon; it reads \"Copied\" for a moment after copying. */\n    label?: string;\n    /** How long the confirmation stays, in ms. */\n    timeout?: number;\n    onCopied?: (value: string) => void;\n  };\n\n/** Copies to the clipboard; the copy icon twists into a check (GSAP) and the result is announced politely. */\nfunction CopyButton({ value, label, timeout = 1600, onCopied, variant = \"ghost\", size, className, onClick, ...props }: CopyButtonProps) {\n  const [copied, setCopied] = React.useState(false);\n  const iconRef = React.useRef<HTMLSpanElement>(null);\n\n  React.useEffect(() => {\n    const icon = iconRef.current;\n    if (!icon || !motionScale(icon)) return;\n    gsap.fromTo(icon, { rotate: copied ? -90 : 90, scale: 0.4, opacity: 0 }, { rotate: 0, scale: 1, opacity: 1, duration: 0.45, ease: \"cue-snap\" });\n  }, [copied]);\n\n  React.useEffect(() => {\n    if (!copied) return;\n    const id = setTimeout(() => setCopied(false), timeout);\n    return () => clearTimeout(id);\n  }, [copied, timeout]);\n\n  return (\n    <button\n      type=\"button\"\n      data-slot=\"copy-button\"\n      data-copied={copied || undefined}\n      aria-label={label ? undefined : copied ? \"Copied\" : \"Copy to clipboard\"}\n      className={cn(buttonVariants({ variant, size: size ?? (label ? \"sm\" : \"icon-sm\") }), className)}\n      onClick={async (e) => {\n        onClick?.(e);\n        await navigator.clipboard.writeText(value);\n        setCopied(true);\n        onCopied?.(value);\n      }}\n      {...props}\n    >\n      <span ref={iconRef} className=\"inline-flex\">\n        {copied ? <CheckIcon className=\"text-success\" /> : <CopyIcon />}\n      </span>\n      {label && <span>{copied ? \"Copied\" : label}</span>}\n      <span aria-live=\"polite\" className=\"sr-only\">\n        {copied ? \"Copied to clipboard\" : \"\"}\n      </span>\n    </button>\n  );\n}\n\nexport { CopyButton };\nexport type { CopyButtonProps };\n",
      "type": "registry:ui"
    }
  ],
  "docs": "Docs and examples: https://ui.cuelark.com/components/copy-button",
  "categories": [
    "actions"
  ],
  "type": "registry:ui"
}