84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
import { useCallback } from "react";
|
||
import clsx from "clsx";
|
||
|
||
import { useAdminContext } from "../context";
|
||
|
||
type CopyButtonProps = {
|
||
value: string | number;
|
||
className?: string;
|
||
"aria-label"?: string;
|
||
successMessage?: string;
|
||
};
|
||
|
||
const copyFallback = (text: string) => {
|
||
if (typeof document === "undefined") {
|
||
throw new Error("Clipboard API недоступен");
|
||
}
|
||
const textarea = document.createElement("textarea");
|
||
textarea.value = text;
|
||
textarea.style.position = "fixed";
|
||
textarea.style.top = "-1000px";
|
||
textarea.style.left = "-1000px";
|
||
document.body.appendChild(textarea);
|
||
textarea.focus();
|
||
textarea.select();
|
||
try {
|
||
document.execCommand("copy");
|
||
} finally {
|
||
document.body.removeChild(textarea);
|
||
}
|
||
};
|
||
|
||
export const CopyButton = ({ value, className, "aria-label": ariaLabel, successMessage }: CopyButtonProps) => {
|
||
const { pushFlash } = useAdminContext();
|
||
|
||
const handleCopy = useCallback(async () => {
|
||
const text = String(value ?? "");
|
||
if (!text) {
|
||
return;
|
||
}
|
||
try {
|
||
if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
|
||
await navigator.clipboard.writeText(text);
|
||
} else {
|
||
copyFallback(text);
|
||
}
|
||
pushFlash({
|
||
type: "success",
|
||
message: successMessage ?? "Значение скопировано в буфер обмена",
|
||
});
|
||
} catch (error) {
|
||
pushFlash({
|
||
type: "error",
|
||
message: error instanceof Error ? `Не удалось скопировать: ${error.message}` : "Не удалось скопировать значение",
|
||
});
|
||
}
|
||
}, [pushFlash, successMessage, value]);
|
||
|
||
return (
|
||
<button
|
||
type="button"
|
||
className={clsx(
|
||
"inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md border border-slate-700 bg-slate-900 text-slate-300 transition hover:border-sky-500 hover:text-white focus:outline-none focus:ring-2 focus:ring-sky-500/40",
|
||
className,
|
||
)}
|
||
aria-label={ariaLabel ?? "Скопировать значение"}
|
||
onClick={handleCopy}
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
className="h-4 w-4"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
strokeWidth="1.5"
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
>
|
||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
|
||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
||
</svg>
|
||
</button>
|
||
);
|
||
};
|