{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "copy-button",
  "title": "Copy Button",
  "author": "ratneshc <ratneshchipre@gmail.com>",
  "description": "A copy button with direction-aware icon transitions.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [
    "https://ratneshc.com/r/use-copy-to-clipboard.json"
  ],
  "files": [
    {
      "path": "src/registry/components/copy-button/copy-button.tsx",
      "content": "// Thanks @ncdai\r\n\r\n\"use client\";\r\n\r\nimport type { ComponentProps } from \"react\";\r\nimport {\r\n  CancelCircleIcon,\r\n  Copy01Icon,\r\n  Tick02Icon,\r\n} from \"@hugeicons/core-free-icons\";\r\nimport { HugeiconsIcon } from \"@hugeicons/react\";\r\nimport { AnimatePresence, motion } from \"motion/react\";\r\nimport type { HTMLMotionProps, Variants } from \"motion/react\";\r\n\r\nimport { Button } from \"@/components/ui/button\";\r\nimport type { CopyState } from \"@/registry/hooks/use-copy-to-clipboard\";\r\nimport { useCopyToClipboard } from \"@/registry/hooks/use-copy-to-clipboard\";\r\n\r\nexport const motionIconVariants: Variants = {\r\n  initial: (direction: number) => ({\r\n    opacity: 0,\r\n    scale: 0.8,\r\n    y: direction > 0 ? 10 : -10,\r\n    filter: \"blur(2px)\",\r\n  }),\r\n  animate: { opacity: 1, scale: 1, y: 0, filter: \"blur(0px)\" },\r\n  exit: (direction: number) => ({\r\n    opacity: 0,\r\n    scale: 0.8,\r\n    y: direction > 0 ? -10 : 10,\r\n    filter: \"blur(2px)\",\r\n  }),\r\n};\r\n\r\nexport const motionIconProps: HTMLMotionProps<\"span\"> = {\r\n  variants: motionIconVariants,\r\n  initial: \"initial\",\r\n  animate: \"animate\",\r\n  exit: \"exit\",\r\n  transition: { duration: 0.15, ease: \"easeOut\" },\r\n};\r\n\r\nexport type CopyStateIconProps = {\r\n  /** The current state of the copy operation. */\r\n  state: CopyState;\r\n  /** Custom icon for idle state. */\r\n  idleIcon?: React.ReactNode;\r\n  /** Custom icon for done state. */\r\n  doneIcon?: React.ReactNode;\r\n  /** Custom icon for error state. */\r\n  errorIcon?: React.ReactNode;\r\n};\r\n\r\nexport function CopyStateIcon({\r\n  state,\r\n  idleIcon,\r\n  doneIcon,\r\n  errorIcon,\r\n}: CopyStateIconProps) {\r\n  const direction = state === \"idle\" ? -1 : 1;\r\n\r\n  return (\r\n    <span className=\"relative flex items-center justify-center\">\r\n      <AnimatePresence mode=\"popLayout\" initial={false} custom={direction}>\r\n        {state === \"idle\" ? (\r\n          <motion.span key=\"idle\" {...motionIconProps} custom={direction}>\r\n            {idleIcon ?? <HugeiconsIcon icon={Copy01Icon} strokeWidth={2} />}\r\n          </motion.span>\r\n        ) : state === \"done\" ? (\r\n          <motion.span key=\"done\" {...motionIconProps} custom={direction}>\r\n            {doneIcon ?? <HugeiconsIcon icon={Tick02Icon} strokeWidth={2} />}\r\n          </motion.span>\r\n        ) : state === \"error\" ? (\r\n          <motion.span key=\"error\" {...motionIconProps} custom={direction}>\r\n            {errorIcon ?? (\r\n              <HugeiconsIcon icon={CancelCircleIcon} strokeWidth={2} />\r\n            )}\r\n          </motion.span>\r\n        ) : null}\r\n      </AnimatePresence>\r\n    </span>\r\n  );\r\n}\r\n\r\nexport type CopyButtonProps = ComponentProps<typeof Button> & {\r\n  /** The text to copy, or a function that returns the text. */\r\n  text: string | (() => string);\r\n  /** Called when the text is successfully copied. */\r\n  onCopySuccess?: (text: string) => void;\r\n  /** Called when the copy operation fails. */\r\n  onCopyError?: (error: Error) => void;\r\n} & Pick<CopyStateIconProps, \"idleIcon\" | \"doneIcon\" | \"errorIcon\">;\r\n\r\nexport function CopyButton({\r\n  size = \"icon\",\r\n  children,\r\n  text,\r\n  idleIcon,\r\n  doneIcon,\r\n  errorIcon,\r\n  onClick,\r\n  onCopySuccess,\r\n  onCopyError,\r\n  ...props\r\n}: CopyButtonProps) {\r\n  const { state, copy } = useCopyToClipboard({\r\n    onCopySuccess,\r\n    onCopyError,\r\n  });\r\n\r\n  return (\r\n    <Button\r\n      size={size}\r\n      onClick={(e) => {\r\n        copy(text);\r\n        onClick?.(e);\r\n      }}\r\n      aria-label=\"Copy\"\r\n      {...props}\r\n    >\r\n      <CopyStateIcon\r\n        state={state}\r\n        idleIcon={idleIcon}\r\n        doneIcon={doneIcon}\r\n        errorIcon={errorIcon}\r\n      />\r\n      {children}\r\n    </Button>\r\n  );\r\n}\r\n",
      "type": "registry:component"
    }
  ],
  "docs": "https://ratneshc.com/components/copy-button",
  "type": "registry:component"
}