| 16 | } |
| 17 | |
| 18 | export function CopyButton({ |
| 19 | value, |
| 20 | className, |
| 21 | showLabel = true, |
| 22 | successDuration = 1500, |
| 23 | onMouseEnter, |
| 24 | onMouseLeave, |
| 25 | }: CopyButtonProps) { |
| 26 | const [isCopied, setIsCopied] = useState(false); |
| 27 | |
| 28 | useEffect(() => { |
| 29 | if (!isCopied) return; |
| 30 | const timer = setTimeout(() => setIsCopied(false), successDuration); |
| 31 | return () => clearTimeout(timer); |
| 32 | }, [isCopied, successDuration]); |
| 33 | |
| 34 | const handleCopy = useCallback(() => { |
| 35 | try { |
| 36 | copy(value); |
| 37 | setIsCopied(true); |
| 38 | } catch (error) { |
| 39 | console.error("Failed to copy text: ", error); |
| 40 | } |
| 41 | }, [value]); |
| 42 | |
| 43 | return ( |
| 44 | <Button |
| 45 | variant="ghost" |
| 46 | size={showLabel ? "default" : "icon"} |
| 47 | onClick={handleCopy} |
| 48 | onMouseEnter={onMouseEnter} |
| 49 | onMouseLeave={onMouseLeave} |
| 50 | className={cn( |
| 51 | "transition-colors duration-300 ease-[cubic-bezier(0.165,0.85,0.45,1)]", |
| 52 | "bg-neutral-100 text-neutral-800 shadow-sm ring-1 ring-neutral-200 hover:bg-neutral-200 hover:text-neutral-950 dark:bg-neutral-800/90 dark:text-neutral-200 dark:ring-white/10 dark:hover:bg-neutral-600 dark:hover:text-white dark:hover:ring-white/20", |
| 53 | className, |
| 54 | )} |
| 55 | aria-label={isCopied ? "Copied!" : "Copy to clipboard"} |
| 56 | > |
| 57 | <span className={cn("relative h-5 w-5 shrink-0")} aria-hidden="true"> |
| 58 | <span |
| 59 | className={cn( |
| 60 | "absolute inset-0 flex items-center justify-center", |
| 61 | "transition-all duration-300 ease-[cubic-bezier(0.165,0.85,0.45,1)]", |
| 62 | "motion-reduce:transition-none", |
| 63 | isCopied ? "scale-50 opacity-0" : "scale-100 opacity-100", |
| 64 | )} |
| 65 | > |
| 66 | <Copy className="h-4 w-4" /> |
| 67 | </span> |
| 68 | <span |
| 69 | className={cn( |
| 70 | "absolute inset-0 flex items-center justify-center", |
| 71 | "transition-all duration-300 ease-[cubic-bezier(0.165,0.85,0.45,1)]", |
| 72 | "motion-reduce:transition-none", |
| 73 | isCopied ? "scale-100 opacity-100" : "scale-50 opacity-0", |
| 74 | )} |
| 75 | > |