ci / go (push) Waiting to run
ci / go-db (agent) (push) Waiting to run
ci / go-db (config) (push) Waiting to run
ci / go-db (db) (push) Waiting to run
ci / go-db (evidence) (push) Waiting to run
ci / go-db (llmrec) (push) Waiting to run
ci / go-db (server) (push) Waiting to run
web / web (push) Waiting to run
docs / links (push) Canceled after 0s
detections / detections (push) Canceled after 0s
63 lines
1.7 KiB
TypeScript
63 lines
1.7 KiB
TypeScript
"use client";
|
|
|
|
import * as React from "react";
|
|
|
|
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
import { useTranslations } from "next-intl";
|
|
import { toast } from "sonner";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
import { cn, copyText } from "@/lib/utils";
|
|
|
|
type CopyButtonProps = {
|
|
// 要复制的文本;为空则按钮禁用。
|
|
text: string | null | undefined;
|
|
// 复制成功后的 toast 文案,默认「已复制」。
|
|
successMessage?: string;
|
|
label?: React.ReactNode;
|
|
size?: React.ComponentProps<typeof Button>["size"];
|
|
variant?: React.ComponentProps<typeof Button>["variant"];
|
|
className?: string;
|
|
};
|
|
|
|
// CopyButton 统一的「复制到剪贴板」按钮:内置成功/失败反馈,并在 HTTP 非安全上下文
|
|
// 下自动降级(见 copyText)。
|
|
export function CopyButton({
|
|
text,
|
|
successMessage,
|
|
label,
|
|
size = "sm",
|
|
variant = "outline",
|
|
className,
|
|
}: CopyButtonProps) {
|
|
const t = useTranslations("copyButton");
|
|
const [copied, setCopied] = React.useState(false);
|
|
const timer = React.useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
|
|
React.useEffect(() => {
|
|
return () => {
|
|
if (timer.current) clearTimeout(timer.current);
|
|
};
|
|
}, []);
|
|
|
|
async function handleCopy() {
|
|
if (!text) return;
|
|
const ok = await copyText(text);
|
|
if (ok) {
|
|
setCopied(true);
|
|
toast.success(successMessage ?? t("success"));
|
|
if (timer.current) clearTimeout(timer.current);
|
|
timer.current = setTimeout(() => setCopied(false), 1500);
|
|
} else {
|
|
toast.error(t("error"));
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Button type="button" size={size} variant={variant} className={cn(className)} disabled={!text} onClick={handleCopy}>
|
|
{copied ? <CheckIcon /> : <CopyIcon />}
|
|
{label ?? t("label")}
|
|
</Button>
|
|
);
|
|
}
|