File size: 1,953 Bytes
f1dd159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import { Check, Copy } from "lucide-react";
import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";

import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { copyToClipboard } from "@/shared/clipboard";
import { cn } from "@/shared/lib/cn";

export function CopyButton({
  value,
  className,
  disabled,
  copyLabel,
  onCopied,
  onError,
}: {
  value: string;
  className?: string;
  disabled?: boolean;
  copyLabel?: string;
  onCopied?: () => void;
  onError?: (error: unknown) => void;
}) {
  const { t } = useTranslation();
  const [copiedValue, setCopiedValue] = useState<string | null>(null);
  const resetTimerRef = useRef<number | null>(null);

  useEffect(() => () => {
    if (resetTimerRef.current !== null) window.clearTimeout(resetTimerRef.current);
  }, []);

  async function handleClick() {
    const ok = await copyToClipboard(value);
    if (ok) {
      if (resetTimerRef.current !== null) window.clearTimeout(resetTimerRef.current);
      setCopiedValue(value);
      resetTimerRef.current = window.setTimeout(() => {
        setCopiedValue(null);
        resetTimerRef.current = null;
      }, 1500);
      onCopied?.();
    } else {
      const message = t("common.copyFailed");
      toast.error(message);
      onError?.(new Error(message));
    }
  }

  const copied = copiedValue === value;
  const label = copied ? t("common.copied") : copyLabel ?? t("common.copy");
  return (
    <Tooltip>
      <TooltipTrigger asChild>
        <Button type="button" variant="ghost" size="icon" className={cn("size-7 shrink-0 text-muted-foreground", className)} aria-label={label} disabled={disabled} onClick={handleClick}>
          {copied ? <Check /> : <Copy />}
        </Button>
      </TooltipTrigger>
      <TooltipContent>{label}</TooltipContent>
    </Tooltip>
  );
}