Skip to content

Commit

Permalink
Add copy code button
Browse files Browse the repository at this point in the history
  • Loading branch information
GuillaumeGomez committed May 31, 2024
1 parent c0d6003 commit 121d688
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 40 deletions.
82 changes: 63 additions & 19 deletions src/librustdoc/html/static/css/rustdoc.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,28 @@
--desktop-sidebar-width: 200px;
--src-sidebar-width: 300px;
--desktop-sidebar-z-index: 100;
/* clipboard <https://github.com/rust-lang/crates.io/commits/main/public/assets/copy.svg> */
--clipboard-image: url('data:image/svg+xml,<svg width="19" height="18" viewBox="0 0 24 25" \
xmlns="http://www.w3.org/2000/svg" aria-label="Copy to clipboard">\
<path d="M18 20h2v3c0 1-1 2-2 2H2c-.998 0-2-1-2-2V5c0-.911.755-1.667 1.667-1.667h5A3.323 3.323 0 \
0110 0a3.323 3.323 0 013.333 3.333h5C19.245 3.333 20 4.09 20 5v8.333h-2V9H2v14h16v-3zM3 \
7h14c0-.911-.793-1.667-1.75-1.667H13.5c-.957 0-1.75-.755-1.75-1.666C11.75 2.755 10.957 2 10 \
2s-1.75.755-1.75 1.667c0 .911-.793 1.666-1.75 1.666H4.75C3.793 5.333 3 6.09 3 7z"/>\
<path d="M4 19h6v2H4zM12 11H4v2h8zM4 17h4v-2H4zM15 15v-3l-4.5 4.5L15 21v-3l8.027-.032L23 15z"/>\
</svg>');
--clipboard-image-big: url('data:image/svg+xml,<svg width="22" height="23" viewBox="0 0 24 25" \
xmlns="http://www.w3.org/2000/svg" aria-label="Copy to clipboard">\
<path d="M18 20h2v3c0 1-1 2-2 2H2c-.998 0-2-1-2-2V5c0-.911.755-1.667 1.667-1.667h5A3.323 3.323 0 \
0110 0a3.323 3.323 0 013.333 3.333h5C19.245 3.333 20 4.09 20 5v8.333h-2V9H2v14h16v-3zM3 \
7h14c0-.911-.793-1.667-1.75-1.667H13.5c-.957 0-1.75-.755-1.75-1.666C11.75 2.755 10.957 2 10 \
2s-1.75.755-1.75 1.667c0 .911-.793 1.666-1.75 1.666H4.75C3.793 5.333 3 6.09 3 7z"/>\
<path d="M4 19h6v2H4zM12 11H4v2h8zM4 17h4v-2H4zM15 15v-3l-4.5 4.5L15 21v-3l8.027-.032L23 15z"/>\
</svg>');
/* Checkmark <https://www.svgrepo.com/svg/335033/checkmark> */
--checkmark-image: url('data:image/svg+xml,<svg viewBox="-1 -1 23 23" \
xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\
<g><path d="M9 19.414l-6.707-6.707 1.414-1.414L9 16.586 20.293 5.293l1.414 1.414"></path>\
</g></svg>');
}

/* See FiraSans-LICENSE.txt for the Fira Sans license. */
Expand Down Expand Up @@ -1376,25 +1398,59 @@ documentation. */
top: 20px;
}

a.test-arrow {
.example-wrap > a.test-arrow, .example-wrap .button-holder {
visibility: hidden;
position: absolute;
padding: 5px 10px 5px 10px;
border-radius: 5px;
font-size: 1.375rem;
top: 5px;
right: 5px;
z-index: 1;
}
a.test-arrow {
padding: 5px 10px 5px 10px;
border-radius: 5px;
font-size: 1.375rem;
color: var(--test-arrow-color);
background-color: var(--test-arrow-background-color);
}
a.test-arrow:hover {
color: var(--test-arrow-hover-color);
background-color: var(--test-arrow-hover-background-color);
}
.example-wrap:hover .test-arrow {
.example-wrap .button-holder {
display: flex;
}
.example-wrap:hover > .test-arrow {
padding: 3px 10px;
}
.example-wrap:hover > .test-arrow, .example-wrap:hover > .button-holder {
visibility: visible;
}
.example-wrap .button-holder .copy-button {
color: var(--copy-path-button-color);
background: var(--main-background-color);
height: 43px;
width: 40px;
margin-left: 5px;
padding: 2px 0 0 4px;
border: 0;
cursor: pointer;
border-radius: 5px;
}
.example-wrap .button-holder .copy-button.clicked {
padding-top: 4px;
}
.example-wrap .button-holder .copy-button::before {
filter: var(--copy-path-img-filter);
content: var(--clipboard-image-big);
width: 23px;
height: 22px;
}
.example-wrap .button-holder .copy-button:hover::before {
filter: var(--copy-path-img-hover-filter);
}
.example-wrap .button-holder .copy-button.clicked::before {
content: var(--checkmark-image);
}

.code-attribute {
font-weight: 300;
Expand Down Expand Up @@ -1652,27 +1708,15 @@ a.tooltip:hover::after {
}
#copy-path::before {
filter: var(--copy-path-img-filter);
/* clipboard <https://github.com/rust-lang/crates.io/commits/main/public/assets/copy.svg> */
content: url('data:image/svg+xml,<svg width="19" height="18" viewBox="0 0 24 25" \
xmlns="http://www.w3.org/2000/svg" aria-label="Copy to clipboard">\
<path d="M18 20h2v3c0 1-1 2-2 2H2c-.998 0-2-1-2-2V5c0-.911.755-1.667 1.667-1.667h5A3.323 3.323 0 \
0110 0a3.323 3.323 0 013.333 3.333h5C19.245 3.333 20 4.09 20 5v8.333h-2V9H2v14h16v-3zM3 \
7h14c0-.911-.793-1.667-1.75-1.667H13.5c-.957 0-1.75-.755-1.75-1.666C11.75 2.755 10.957 2 10 \
2s-1.75.755-1.75 1.667c0 .911-.793 1.666-1.75 1.666H4.75C3.793 5.333 3 6.09 3 7z"/>\
<path d="M4 19h6v2H4zM12 11H4v2h8zM4 17h4v-2H4zM15 15v-3l-4.5 4.5L15 21v-3l8.027-.032L23 15z"/>\
</svg>');
content: var(--clipboard-image);
width: 19px;
height: 18px;
}
#copy-path:hover::before {
filter: var(--copy-path-img-hover-filter);
}
#copy-path.clicked::before {
/* Checkmark <https://www.svgrepo.com/svg/335033/checkmark> */
content: url('data:image/svg+xml,<svg viewBox="-1 -1 23 23" xmlns="http://www.w3.org/2000/svg" \
fill="black" height="18px">\
<g><path d="M9 19.414l-6.707-6.707 1.414-1.414L9 16.586 20.293 5.293l1.414 1.414"></path>\
</g></svg>');
content: var(--checkmark-image);
}

@keyframes rotating {
Expand Down
95 changes: 74 additions & 21 deletions src/librustdoc/html/static/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -1769,9 +1769,37 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
}());

// This section handles the copy button that appears next to the path breadcrumbs
// and the copy buttons on the code examples.
(function() {
let reset_button_timeout = null;
// Common functions to copy buttons.
function copyContentToClipboard(content) {
const el = document.createElement("textarea");
el.value = content;
el.setAttribute("readonly", "");
// To not make it appear on the screen.
el.style.position = "absolute";
el.style.left = "-9999px";

document.body.appendChild(el);
el.select();
document.execCommand("copy");
document.body.removeChild(el);
}

function copyButtonAnimation(button) {
button.classList.add("clicked");

if (button.reset_button_timeout !== undefined) {
window.clearTimeout(button.reset_button_timeout);
}

button.reset_button_timeout = window.setTimeout(() => {
button.reset_button_timeout = undefined;
button.classList.remove("clicked");
}, 1000);
}

// Copy button that appears next to the path breadcrumbs.
const but = document.getElementById("copy-path");
if (!but) {
return;
Expand All @@ -1786,29 +1814,54 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
}
});

const el = document.createElement("textarea");
el.value = path.join("::");
el.setAttribute("readonly", "");
// To not make it appear on the screen.
el.style.position = "absolute";
el.style.left = "-9999px";

document.body.appendChild(el);
el.select();
document.execCommand("copy");
document.body.removeChild(el);

but.classList.add("clicked");
copyContentToClipboard(path.join("::"));
copyButtonAnimation(but);
};

if (reset_button_timeout !== null) {
window.clearTimeout(reset_button_timeout);
// Copy buttons on code examples.
function copyCode(codeElem) {
if (!codeElem) {
// Should never happen, but the world is a dark and dangerous place.
return;
}
let text = "";
onEachLazy(codeElem.childNodes, elem => {
if (elem.nodeType === Node.TEXT_NODE) {
text += elem.textContent;
}
});
copyContentToClipboard(text);
}

function reset_button() {
reset_button_timeout = null;
but.classList.remove("clicked");
function addCopyButton(event) {
let elem = event.target;
while (!hasClass(elem, "example-wrap")) {
elem = elem.parentElement;
if (elem.tagName === "body" || hasClass(elem, "docblock")) {
return;
}
}
// Since the button will be added, no need to keep this listener around.
elem.removeEventListener("mouseover", addCopyButton);

const parent = document.createElement("div");
parent.className = "button-holder";
const runButton = elem.querySelector(".test-arrow");
if (runButton !== null) {
// If there is a run button, we move it into the same div.
parent.appendChild(runButton);
}
elem.appendChild(parent);
const copyButton = document.createElement("button");
copyButton.className = "copy-button";
copyButton.addEventListener("click", () => {
copyCode(parent.querySelector("pre > code"));
copyButtonAnimation(copyButton);
});
parent.appendChild(copyButton);
}

reset_button_timeout = window.setTimeout(reset_button, 1000);
};
onEachLazy(document.querySelectorAll(".docblock .example-wrap"), elem => {
elem.addEventListener("mouseover", addCopyButton);
});
}());

0 comments on commit 121d688

Please sign in to comment.