mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
67 lines
1.9 KiB
TypeScript
67 lines
1.9 KiB
TypeScript
import { Check, Copy } from "lucide-react";
|
||
import { type ComponentPropsWithoutRef, useCallback, useEffect, useState } from "react";
|
||
import CopyToClipboard from "react-copy-to-clipboard";
|
||
import { useTranslation } from "react-i18next";
|
||
import { toast } from "sonner";
|
||
|
||
import { Button } from "@/components/ui/button";
|
||
import { Label } from "@/components/ui/label";
|
||
import { cn } from "@/lib/utils";
|
||
|
||
type CopyableFieldProps = {
|
||
label: string;
|
||
value?: string;
|
||
text?: string;
|
||
} & Omit<ComponentPropsWithoutRef<"div">, "children">;
|
||
|
||
export function CopyableField({
|
||
label,
|
||
value,
|
||
text,
|
||
className,
|
||
...divProps
|
||
}: Readonly<CopyableFieldProps>) {
|
||
const [hasCopied, setHasCopied] = useState(false);
|
||
const { t } = useTranslation(["common"]);
|
||
|
||
useEffect(() => {
|
||
if (hasCopied) {
|
||
const timer = setTimeout(() => {
|
||
setHasCopied(false);
|
||
}, 1000);
|
||
return () => clearTimeout(timer);
|
||
}
|
||
}, [hasCopied]);
|
||
|
||
const handleCopy = useCallback(() => {
|
||
if (!hasCopied) {
|
||
setHasCopied(true);
|
||
toast.success(t("copyLabelSuccess", { label }), {
|
||
duration: 1000,
|
||
});
|
||
}
|
||
}, [hasCopied, label, t]);
|
||
|
||
return (
|
||
<div className={cn("flex flex-col gap-1 py-1", className)} {...divProps}>
|
||
<div className="flex h-6 items-center justify-between gap-2">
|
||
<Label className="text-sm text-muted-foreground">{label}:</Label>
|
||
{value && (
|
||
<CopyToClipboard.CopyToClipboard text={value} onCopy={handleCopy}>
|
||
<Button
|
||
variant="ghost"
|
||
size="icon"
|
||
type="button"
|
||
className="h-8 w-8"
|
||
disabled={hasCopied}
|
||
>
|
||
{hasCopied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
|
||
</Button>
|
||
</CopyToClipboard.CopyToClipboard>
|
||
)}
|
||
</div>
|
||
<p className="text-sm break-all">{text}</p>
|
||
</div>
|
||
);
|
||
}
|