Skip to content

Commit 280128f

Browse files
authored
docs: add copy button (#19512)
* docs: add copy button * address review
1 parent 833c4a3 commit 280128f

4 files changed

Lines changed: 113 additions & 0 deletions

File tree

‎docs/.eleventy.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const {
1717
const markdownIt = require("markdown-it");
1818
const markdownItRuleExample = require("./tools/markdown-it-rule-example");
1919
const prismESLintHook = require("./tools/prism-eslint-hook");
20+
const preWrapperPlugin = require("./src/_plugins/pre-wrapper.js");
2021

2122
module.exports = function(eleventyConfig) {
2223

@@ -254,6 +255,7 @@ module.exports = function(eleventyConfig) {
254255
}
255256
})
256257
.use(lineNumberPlugin)
258+
.use(preWrapperPlugin)
257259
.disable("code");
258260

259261
eleventyConfig.setLibrary("md", md);

‎docs/src/_includes/layouts/doc.html‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,4 +148,34 @@ <h1>{{ title }}</h1>
148148
</a>
149149
</div>
150150

151+
<div id="copy-announcement" class="visually-hidden" aria-live="polite" aria-atomic="true"></div>
152+
153+
<script>
154+
(function() {
155+
const copyBtns = document.querySelectorAll(".copy-btn");
156+
const announcementRegion = document.getElementById("copy-announcement");
157+
158+
for (const copyBtn of copyBtns) {
159+
const copyText = copyBtn.previousElementSibling.firstChild.textContent;
160+
161+
copyBtn.onclick = () => {
162+
navigator.clipboard
163+
.writeText(copyText)
164+
.then(() => {
165+
announcementRegion.innerHTML = "Copied!";
166+
copyBtn.setAttribute("data-copied", "true");
167+
168+
setTimeout(() => {
169+
copyBtn.removeAttribute("data-copied");
170+
announcementRegion.innerHTML = "";
171+
}, 2000);
172+
})
173+
.catch((err) => {
174+
announcementRegion.innerHTML = "Failed to copy!";
175+
console.error("Clipboard write failed: ", err);
176+
});
177+
};
178+
}
179+
})();
180+
</script>
151181
<script src="{{ '/assets/js/tabs.js' | url }}"></script>

‎docs/src/_plugins/pre-wrapper.js‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
module.exports = (md) => {
2+
const defaultFenceRenderer = md.renderer.rules.fence;
3+
4+
md.renderer.rules.fence = (...args) => {
5+
const [tokens, index] = args;
6+
7+
if (/^\s*(?:in)?correct(?!\S)/u.test(tokens[index - 1].info)) {
8+
return defaultFenceRenderer(...args);
9+
}
10+
11+
return `
12+
<div class="code-wrapper">
13+
${defaultFenceRenderer(...args)}
14+
<button class="copy-btn" aria-labelledby="copy-button-label">
15+
<span hidden id="copy-button-label">Copy code to clipboard</span>
16+
<svg width="20" height="20" viewBox="0 0 20 20" role="img" aria-label="copy" fill="none" focusable="false">
17+
<path d="M4.16667 12.5H3.33333C2.89131 12.5 2.46738 12.3244 2.15482 12.0118C1.84226 11.6993 1.66667 11.2754 1.66667 10.8333V3.33332C1.66667 2.8913 1.84226 2.46737 2.15482 2.15481C2.46738 1.84225 2.89131 1.66666 3.33333 1.66666H10.8333C11.2754 1.66666 11.6993 1.84225 12.0118 2.15481C12.3244 2.46737 12.5 2.8913 12.5 3.33332V4.16666M9.16667 7.49999H16.6667C17.5871 7.49999 18.3333 8.24618 18.3333 9.16666V16.6667C18.3333 17.5871 17.5871 18.3333 16.6667 18.3333H9.16667C8.24619 18.3333 7.5 17.5871 7.5 16.6667V9.16666C7.5 8.24618 8.24619 7.49999 9.16667 7.49999Z" stroke="currentColor" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round" />
18+
</svg>
19+
</button>
20+
</div>
21+
`;
22+
};
23+
};

‎docs/src/assets/scss/docs.scss‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,61 @@ pre[class*="language-"] {
214214
right: 25px;
215215
}
216216
}
217+
218+
.code-wrapper {
219+
position: relative;
220+
221+
> pre {
222+
padding-right: 3.5rem;
223+
}
224+
}
225+
226+
.copy-btn {
227+
position: absolute;
228+
top: 1rem;
229+
right: 1rem;
230+
padding: 0.5rem;
231+
border-radius: var(--border-radius);
232+
color: var(--body-text-color);
233+
background-color: var(--color-neutral-100);
234+
border: 1px solid var(--border-color);
235+
transition: background-color 0.1s linear;
236+
237+
&:hover {
238+
background-color: var(--color-neutral-200);
239+
}
240+
241+
[data-theme="dark"] & {
242+
background-color: var(--color-neutral-700);
243+
244+
&:hover {
245+
background-color: var(--color-neutral-600);
246+
}
247+
}
248+
249+
&::after {
250+
content: "Copied!";
251+
padding: 0.5rem 0.75rem;
252+
font-size: var(--step--1);
253+
border-radius: var(--border-radius);
254+
position: absolute;
255+
top: 0;
256+
right: 2.5rem;
257+
height: 2.25rem;
258+
display: flex;
259+
align-items: center;
260+
justify-content: center;
261+
background-color: var(--color-neutral-100);
262+
border: 1px solid var(--border-color);
263+
opacity: 0;
264+
pointer-events: none;
265+
266+
[data-theme="dark"] & {
267+
background-color: var(--color-neutral-700);
268+
}
269+
}
270+
271+
&[data-copied="true"]::after {
272+
opacity: 1;
273+
}
274+
}

0 commit comments

Comments
 (0)