75 lines
1.8 KiB
TypeScript
75 lines
1.8 KiB
TypeScript
import { useState } from 'react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Copy, Check } from 'lucide-react';
|
|
import { copyToClipboard } from '@/lib/social-utils';
|
|
|
|
interface CopyLinkButtonProps {
|
|
url: string;
|
|
size?: 'sm' | 'default' | 'lg';
|
|
variant?: 'default' | 'outline' | 'ghost';
|
|
className?: string;
|
|
showLabel?: boolean;
|
|
onCopy?: (success: boolean) => void;
|
|
}
|
|
|
|
export function CopyLinkButton({
|
|
url,
|
|
size = 'default',
|
|
variant = 'outline',
|
|
className = '',
|
|
showLabel = false,
|
|
onCopy,
|
|
}: CopyLinkButtonProps) {
|
|
const [isCopied, setIsCopied] = useState(false);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
const handleCopy = async () => {
|
|
if (isLoading) return;
|
|
|
|
setIsLoading(true);
|
|
try {
|
|
const success = await copyToClipboard(url);
|
|
setIsCopied(success);
|
|
|
|
if (onCopy) {
|
|
onCopy(success);
|
|
}
|
|
|
|
// Reset copied state after 2 seconds
|
|
if (success) {
|
|
setTimeout(() => setIsCopied(false), 2000);
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to copy:', error);
|
|
if (onCopy) {
|
|
onCopy(false);
|
|
}
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
const label = isCopied ? 'Copied!' : 'Copy Link';
|
|
|
|
return (
|
|
<Button
|
|
type="button"
|
|
variant={variant}
|
|
size={size}
|
|
onClick={handleCopy}
|
|
disabled={isLoading}
|
|
className={`relative ${className}`}
|
|
aria-label={label}
|
|
title={label}
|
|
>
|
|
{isLoading ? (
|
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-current" />
|
|
) : isCopied ? (
|
|
<Check className="h-4 w-4 text-green-600" />
|
|
) : (
|
|
<Copy className="h-4 w-4" />
|
|
)}
|
|
{showLabel && <span className="ml-2">{label}</span>}
|
|
</Button>
|
|
);
|
|
} |