diff --git a/website/siteConfig.js b/website/siteConfig.js
index b4d93ab11a45..a8046d438bde 100644
--- a/website/siteConfig.js
+++ b/website/siteConfig.js
@@ -56,7 +56,11 @@ const siteConfig = {
},
usePrism: ["javascript", "jsx", "typescript", "ts", "js", "html", "css"],
useEnglishUrl: true,
- scripts: ["https://buttons.github.io/buttons.js"],
+ scripts: [
+ "https://buttons.github.io/buttons.js",
+ "https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js",
+ "/js/code-block-buttons.js",
+ ],
stylesheets: [
"//unpkg.com/@sandhose/prettier-animated-logo@1.0.3/dist/wide.css",
],
diff --git a/website/static/css/code-block-buttons.css b/website/static/css/code-block-buttons.css
new file mode 100644
index 000000000000..73207981c241
--- /dev/null
+++ b/website/static/css/code-block-buttons.css
@@ -0,0 +1,63 @@
+/* "Copy" code block button, style from docusaurus v2 */
+
+.code-block-with-actions {
+ position: relative;
+}
+
+.code-block-actions {
+ position: absolute;
+ top: 10px;
+ right: 10px;
+}
+
+.code-block-copy-button {
+ width: 32px;
+ height: 32px;
+ border: 1px solid #dadde1;
+ border-radius: 6px;
+ display: flex;
+ transition: opacity 0.2s ease-in-out;
+ opacity: 0;
+ position: relative;
+ cursor: pointer;
+ color: #393a34;
+ background-color: #f6f8fa;
+}
+
+.code-block-with-actions:hover .code-block-copy-button {
+ opacity: 0.4;
+}
+
+.code-block-with-actions:hover .code-block-copy-button:hover {
+ opacity: 1;
+}
+
+.code-block-copy-button__icon {
+ fill: currentColor;
+ position: absolute;
+ width: 18px;
+ height: 18px;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ margin: auto;
+ transition: 0.15s;
+}
+
+.code-block-copy-button__icon--copied {
+ color: #00d600;
+ opacity: 0;
+ transform: scale(0.33);
+}
+
+.code-block-copy-button--copied .code-block-copy-button__icon--copy {
+ opacity: 0;
+ transform: scale(0.33);
+}
+
+.code-block-copy-button--copied .code-block-copy-button__icon--copied {
+ transition-delay: 75ms;
+ transform: scale(1);
+ opacity: 1;
+}
diff --git a/website/static/js/code-block-buttons.js b/website/static/js/code-block-buttons.js
new file mode 100644
index 000000000000..dd09911f34b6
--- /dev/null
+++ b/website/static/js/code-block-buttons.js
@@ -0,0 +1,57 @@
+/* global ClipboardJS */
+
+"use strict";
+
+(function () {
+ const CONTAINER_CLASS_NAME = "code-block-with-actions";
+ const ACTIONS_CONTAINER_CLASS_NAME = "code-block-actions";
+ const COPY_BUTTON_CLASS_NAME = "code-block-copy-button";
+ const COPY_BUTTON_COPIED_CLASS_NAME = `${COPY_BUTTON_CLASS_NAME}--copied`;
+ const COPY_BUTTON_ICON_CLASS_NAME = `${COPY_BUTTON_CLASS_NAME}__icon`;
+ const COPY_BUTTON_COPY_ICON_CLASS_NAME = `${COPY_BUTTON_ICON_CLASS_NAME}--copy`;
+ const COPY_BUTTON_COPIED_ICON_CLASS_NAME = `${COPY_BUTTON_ICON_CLASS_NAME}--copied`;
+ const ARIA_LABEL = "Copy code to clipboard";
+ const ARIA_LABEL_COPIED = "Copied";
+
+ function init(codeBlock) {
+ const container = codeBlock.parentNode;
+ container.classList.add(CONTAINER_CLASS_NAME);
+
+ const actionsContainer = Object.assign(document.createElement("div"), {
+ className: ACTIONS_CONTAINER_CLASS_NAME,
+ });
+ const copyButton = Object.assign(document.createElement("button"), {
+ className: COPY_BUTTON_CLASS_NAME,
+ type: "button",
+ innerHTML:
+ `` +
+ ``,
+ });
+ copyButton.setAttribute("aria-label", ARIA_LABEL);
+
+ new ClipboardJS(copyButton, { target: () => codeBlock }).on(
+ "success",
+ (event) => {
+ event.clearSelection();
+ copyButton.classList.add(COPY_BUTTON_COPIED_CLASS_NAME);
+ copyButton.setAttribute("aria-label", ARIA_LABEL_COPIED);
+ copyButton.disabled = true;
+
+ setTimeout(() => {
+ copyButton.classList.remove(COPY_BUTTON_COPIED_CLASS_NAME);
+ copyButton.setAttribute("aria-label", ARIA_LABEL);
+ copyButton.disabled = false;
+ }, 2000);
+ }
+ );
+
+ actionsContainer.appendChild(copyButton);
+ container.appendChild(actionsContainer);
+ }
+
+ window.addEventListener("load", () => {
+ for (const codeBlock of document.querySelectorAll("pre > code.hljs")) {
+ init(codeBlock);
+ }
+ });
+})();
diff --git a/website/yarn.lock b/website/yarn.lock
index 3abc0b3ae398..99a7ef961a5f 100644
--- a/website/yarn.lock
+++ b/website/yarn.lock
@@ -2558,9 +2558,9 @@ dashdash@^1.12.0:
assert-plus "^1.0.0"
date-fns@^2.16.1:
- version "2.28.0"
- resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2"
- integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==
+ version "2.29.3"
+ resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8"
+ integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0:
version "2.6.9"