CopyButton.tsx•3.26 kB
// Extracted CopyButton from Docusaurus 3.7 to avoid breaking changes in 3.8
// https://github.com/facebook/docusaurus/blob/v3.7.0/packages/docusaurus-theme-classic/src/theme/CodeBlock/CopyButton/index.tsx
/**
* MIT License
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React, {
useCallback,
useState,
useRef,
useEffect,
type ReactNode,
} from "react";
import clsx from "clsx";
import copy from "copy-text-to-clipboard";
import { translate } from "@docusaurus/Translate";
import IconCopy from "@theme/Icon/Copy";
import IconSuccess from "@theme/Icon/Success";
import styles from "./CopyButton.module.css";
export default function CopyButton({
code,
className,
}: {
code: string;
className?: string;
}): ReactNode {
const [isCopied, setIsCopied] = useState(false);
const copyTimeout = useRef<number | undefined>(undefined);
const handleCopyCode = useCallback(() => {
copy(code);
setIsCopied(true);
copyTimeout.current = window.setTimeout(() => {
setIsCopied(false);
}, 1000);
}, [code]);
useEffect(() => () => window.clearTimeout(copyTimeout.current), []);
return (
<button
type="button"
aria-label={
isCopied
? translate({
id: "theme.CodeBlock.copied",
message: "Copied",
description: "The copied button label on code blocks",
})
: translate({
id: "theme.CodeBlock.copyButtonAriaLabel",
message: "Copy code to clipboard",
description: "The ARIA label for copy code blocks button",
})
}
title={translate({
id: "theme.CodeBlock.copy",
message: "Copy",
description: "The copy button label on code blocks",
})}
className={clsx(
"clean-btn",
className,
styles.copyButton,
isCopied && styles.copyButtonCopied,
)}
onClick={handleCopyCode}
>
<span className={styles.copyButtonIcons} aria-hidden="true">
<IconCopy className={styles.copyButtonIcon} />
<IconSuccess className={styles.copyButtonSuccessIcon} />
</span>
</button>
);
}