Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 170x 989x 989x 989x 989x 1978x 6x 1972x 989x 989x 3x 986x 989x 2x 2x 2x 1x 2x 2x 2x 989x 173x 173x 2x 989x 989x 989x | import { CheckIcon, CopyIcon, WarningIcon } from "@phosphor-icons/react";
import GiveIconButton from "@shared/IconButton/GiveIconButton";
import GiveTooltip from "@shared/Tooltip/GiveTooltip";
import { useState, useCallback, useRef, useEffect } from "react";
type CopyState = "idle" | "copied" | "error";
type Props = {
text: string;
disabled?: boolean;
tooltipText?: string;
testId?: string;
hidden?: boolean;
fluidWidth?: boolean;
};
const CopyButton = ({
text,
disabled = false,
tooltipText = "link",
testId,
hidden,
fluidWidth,
}: Props) => {
const [copyState, setCopyState] = useState<CopyState>("idle");
const capitalizedText = `${tooltipText
.charAt(0)
.toUpperCase()}${tooltipText.slice(1)}`;
const resetStateRef = useRef<NodeJS.Timeout | null>(null);
const getTooltipTitle = () => {
switch (copyState) {
case "copied":
return `${capitalizedText} Copied`;
case "error":
return `Failed to copy ${capitalizedText}`;
case "idle":
default:
return `Copy ${capitalizedText}`;
}
};
const getTooltipIcon = () => {
switch (copyState) {
case "copied":
return <CheckIcon size={18} weight="bold" />;
case "error":
return <WarningIcon size={18} weight="bold" />;
case "idle":
default:
return null;
}
};
const handleCopy = useCallback(
async (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
// Don't try to copy if already processing or no text
Iif (copyState !== "idle" || !text) {
return;
}
// Reset state after a delay, regardless of success/failure
resetStateRef.current = setTimeout(() => {
setCopyState("idle");
}, 2500); // Slightly longer for error visibility
try {
await navigator.clipboard.writeText(text);
setCopyState("copied");
} catch (err) {
console.error(`Failed to copy ${tooltipText}:`, err);
setCopyState("error");
}
},
[text, copyState, tooltipText, capitalizedText],
);
useEffect(() => {
return () => {
if (resetStateRef.current) {
clearTimeout(resetStateRef.current);
}
};
}, []);
const isProcessing = copyState !== "idle";
const effectiveDisabled = disabled || isProcessing;
return (
<GiveTooltip
title={getTooltipTitle()}
placement="top"
color="default"
iconRight={getTooltipIcon()}
disableHoverListener={disabled}
fluidWidth={fluidWidth}
>
<GiveIconButton
Icon={CopyIcon}
size="small"
variant={copyState === "copied" ? "filled" : "ghost"}
onClick={handleCopy}
sx={{
pointerEvents: copyState === "copied" ? "none" : "auto",
}}
disabled={effectiveDisabled}
data-testid={testId}
hidden={hidden || !text}
aria-label={getTooltipTitle()}
/>
</GiveTooltip>
);
};
export default CopyButton;
|