Files
opencrm/frontend/src/components/ui/CopyButton.tsx
T
Stefan Hacker e209e9bbca first commit
2026-01-29 01:16:54 +01:00

100 lines
2.7 KiB
TypeScript

import { useState } from 'react';
import { Copy, Check } from 'lucide-react';
interface CopyButtonProps {
value: string;
className?: string;
size?: 'sm' | 'md';
title?: string;
}
export default function CopyButton({ value, className = '', size = 'sm', title = 'In Zwischenablage kopieren' }: CopyButtonProps) {
const [copied, setCopied] = useState(false);
const handleCopy = async (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
try {
await navigator.clipboard.writeText(value);
setCopied(true);
setTimeout(() => setCopied(false), 1500);
} catch (err) {
console.error('Failed to copy:', err);
}
};
const iconSize = size === 'sm' ? 'w-3.5 h-3.5' : 'w-4 h-4';
return (
<button
type="button"
onClick={handleCopy}
className={`inline-flex items-center justify-center p-1 rounded transition-colors ${
copied
? 'text-green-600 bg-green-50'
: 'text-gray-400 hover:text-blue-600 hover:bg-blue-50'
} ${className}`}
title={copied ? 'Kopiert!' : title}
>
{copied ? (
<Check className={iconSize} />
) : (
<Copy className={iconSize} />
)}
</button>
);
}
// Wrapper component for displaying a value with copy button
interface CopyableValueProps {
value: string | number | null | undefined;
label?: string;
className?: string;
multiline?: boolean;
}
export function CopyableValue({ value, label, className = '', multiline = false }: CopyableValueProps) {
if (value === null || value === undefined || value === '') return null;
const stringValue = String(value);
return (
<div className={className}>
{label && <dt className="text-sm text-gray-500">{label}</dt>}
<dd className={`flex items-start gap-1 ${multiline ? '' : 'items-center'}`}>
<span className={multiline ? 'whitespace-pre-line' : ''}>{stringValue}</span>
<CopyButton value={stringValue} className="flex-shrink-0 ml-1" />
</dd>
</div>
);
}
// For copying multiple values at once (e.g., full address)
interface CopyableBlockProps {
values: (string | number | null | undefined)[];
separator?: string;
children: React.ReactNode;
className?: string;
}
export function CopyableBlock({ values, separator = '\n', children, className = '' }: CopyableBlockProps) {
const combinedValue = values
.filter(v => v !== null && v !== undefined && v !== '')
.map(String)
.join(separator);
if (!combinedValue) return <>{children}</>;
return (
<div className={`relative group ${className}`}>
{children}
<CopyButton
value={combinedValue}
className="absolute top-0 right-0 opacity-60 group-hover:opacity-100"
title="Alles kopieren"
/>
</div>
);
}