'use client'; import * as Button from '@/components/ui/button'; import { cn } from '@/utils/cn'; import { Check, Copy, LoaderCircle } from 'lucide-react'; import { AnimatePresence, motion, useReducedMotion } from 'motion/react'; import { type ReactNode, useCallback, useState } from 'react'; import * as AlertToast from '@/components/ui/toast-alert'; import { toast } from '@/components/ui/toast'; export interface CopyButtonProps { className?: string; disabled?: boolean; duration?: number; idleIcon?: ReactNode; loadingDuration?: number; loadingIcon?: ReactNode; onCopy?: () => Promise | void; successIcon?: ReactNode; } const defaultIcons = { idle: , loading: , success: , }; export default function CopyButton({ onCopy, idleIcon = defaultIcons.idle, loadingIcon = defaultIcons.loading, successIcon = defaultIcons.success, className = '', duration = 2000, loadingDuration = 1000, disabled = false, }: CopyButtonProps) { const [buttonState, setButtonState] = useState< 'idle' | 'loading' | 'success' >('idle'); const shouldReduceMotion = useReducedMotion(); const handleClick = useCallback(async () => { setButtonState('loading'); if (onCopy) { await onCopy(); } setTimeout(() => { setButtonState('success'); toast.custom((t) => ( )); }, loadingDuration); setTimeout(() => { setButtonState('idle'); }, loadingDuration + duration); }, [onCopy, loadingDuration, duration]); const icons = { idle: idleIcon, loading: loadingIcon, success: successIcon, }; const ariaLabels = { idle: 'Copy', loading: 'Copying...', success: 'Copied', }; return (
{icons[buttonState]}
); }