6975e38f79
- 更新 components.json,修改 CSS 文件路径为 src/styles/shell.css。 - 移除 tailwind.config.js 中的冗余配置,改为导入 tailwind.shell.config.js。 - 新增 tailwind.pages.config.js 和 tailwind.shared.js,集中管理主题和样式配置。 - 更新多个组件以使用新的样式类和配置,提升代码一致性和可维护性。
66 lines
1.7 KiB
TypeScript
66 lines
1.7 KiB
TypeScript
import React, { useEffect, useRef, useState } from 'react';
|
|
import { Check, Copy } from 'lucide-react';
|
|
import { copyTextToClipboard } from '@/utils/clipboard';
|
|
import { Button, type ButtonProps } from '@/components/ui/button';
|
|
import { cn } from '@/lib/utils';
|
|
import { toast } from 'sonner';
|
|
|
|
interface CopyButtonProps extends Omit<ButtonProps, 'children' | 'onClick'> {
|
|
text: string;
|
|
tooltip?: string;
|
|
}
|
|
|
|
export const CopyButton: React.FC<CopyButtonProps> = ({
|
|
text,
|
|
tooltip,
|
|
variant = 'ghost',
|
|
size = 'icon',
|
|
className,
|
|
...props
|
|
}) => {
|
|
const [copied, setCopied] = useState(false);
|
|
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
};
|
|
}, []);
|
|
|
|
const handleCopy = async (e: React.MouseEvent<HTMLButtonElement>) => {
|
|
e.stopPropagation();
|
|
|
|
if (!text) {
|
|
toast.error('无内容可复制');
|
|
return;
|
|
}
|
|
|
|
const success = await copyTextToClipboard(text);
|
|
if (success) {
|
|
toast.success('已复制到剪贴板');
|
|
setCopied(true);
|
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
timerRef.current = setTimeout(() => setCopied(false), 1500);
|
|
} else {
|
|
toast.error('复制失败');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Button
|
|
type="button"
|
|
onClick={handleCopy}
|
|
title={tooltip ?? '复制'}
|
|
aria-label={tooltip ?? '复制'}
|
|
variant={variant}
|
|
size={size}
|
|
className={cn(className, copied && 'text-emerald-500')}
|
|
{...props}
|
|
>
|
|
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
|
</Button>
|
|
);
|
|
};
|
|
|
|
export default CopyButton;
|