import { useCallback, useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { cn } from "@/lib/utils"; interface CopyTextProps { text: string; /** What to display. Defaults to `text`. */ children?: React.ReactNode; containerClassName?: string; className?: string; ariaLabel?: string; title?: string; /** Tooltip message shown after copying. Default: "Copied!" */ copiedLabel?: string; } export function CopyText({ text, children, containerClassName, className, ariaLabel, title, copiedLabel = "Copied!", }: CopyTextProps) { const { t } = useTranslation(); const successLabel = copiedLabel === "Copied!" ? t("Copied!", { defaultValue: "Copied!" }) : copiedLabel; const failureLabel = t("Copy failed", { defaultValue: "Copy failed" }); const [visible, setVisible] = useState(false); const [label, setLabel] = useState(successLabel); const timerRef = useRef>(undefined); const triggerRef = useRef(null); useEffect(() => () => clearTimeout(timerRef.current), []); const handleClick = useCallback(async () => { try { if (navigator.clipboard && window.isSecureContext) { await navigator.clipboard.writeText(text); } else { // Fallback for non-secure contexts (e.g. HTTP on non-localhost) const textarea = document.createElement("textarea"); textarea.value = text; textarea.style.position = "fixed"; textarea.style.left = "-9999px"; document.body.appendChild(textarea); try { textarea.select(); const success = document.execCommand("copy"); if (!success) throw new Error("execCommand copy failed"); } finally { document.body.removeChild(textarea); } } setLabel(successLabel); } catch { setLabel(failureLabel); } clearTimeout(timerRef.current); setVisible(true); timerRef.current = setTimeout(() => setVisible(false), 1500); }, [failureLabel, successLabel, text]); return ( {label} ); }