mirror of
https://github.com/jbowdre/runtimeterror.git
synced 2024-11-29 18:02:18 +00:00
add copy button to code blocks
This commit is contained in:
parent
77a1eb2e59
commit
ebef538379
6 changed files with 173 additions and 2 deletions
105
assets/css/code-copy-button.css
Normal file
105
assets/css/code-copy-button.css
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/* adapted from https://aaronluna.dev/blog/add-copy-button-to-code-blocks-hugo-chroma/ */
|
||||||
|
|
||||||
|
.highlight-wrapper {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start: Turn off individual column border, margin, and padding when line numbers are showing */
|
||||||
|
.highlight-wrapper .lntd pre {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chroma .lntd pre {
|
||||||
|
border: 0px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chroma .lntd:first-child {
|
||||||
|
padding: 7px 7px 7px 10px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chroma .lntd:last-child {
|
||||||
|
padding: 7px 10px 7px 7px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
/* End: Turn off individual column border, margin, and padding when line numbers are showing */
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin:40px 0 10px 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight > .chroma {
|
||||||
|
position: static;
|
||||||
|
z-index: 1;
|
||||||
|
border-top-left-radius: 0px;
|
||||||
|
border-top-right-radius: 0px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-code-button {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2;
|
||||||
|
right: 0;
|
||||||
|
top: -29px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 14px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
width: 65px;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #000000;
|
||||||
|
border: 1.25px solid #232326;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-right-radius: 0px;
|
||||||
|
border-bottom-left-radius: 0px;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 6px 6px 7px 6px;
|
||||||
|
margin: 0 0 0 1px;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-code-button:hover,
|
||||||
|
.copy-code-button:focus,
|
||||||
|
.copy-code-button:active,
|
||||||
|
.copy-code-button:active:hover {
|
||||||
|
color: #222225;
|
||||||
|
background-color: #b3b3b3;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copyable-text-area {
|
||||||
|
position: absolute;
|
||||||
|
height: 0;
|
||||||
|
z-index: -1;
|
||||||
|
opacity: .01;
|
||||||
|
}
|
||||||
|
.chroma [data-lang]:before {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 0;
|
||||||
|
top: -29px;
|
||||||
|
left: 0;
|
||||||
|
content: attr(data-lang);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: white;
|
||||||
|
background-color: black;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
padding: 6px 6px 7px 6px;
|
||||||
|
line-height: 14px;
|
||||||
|
opacity: 0.6;
|
||||||
|
position: absolute;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
border: 1.25px solid #232326;
|
||||||
|
margin: 0 0 0 1px;
|
||||||
|
}
|
56
assets/js/code-copy-button.js
Normal file
56
assets/js/code-copy-button.js
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// adapted from https://aaronluna.dev/blog/add-copy-button-to-code-blocks-hugo-chroma/
|
||||||
|
|
||||||
|
function createCopyButton(highlightDiv) {
|
||||||
|
const button = document.createElement("button");
|
||||||
|
button.className = "copy-code-button";
|
||||||
|
button.type = "button";
|
||||||
|
button.innerText = "Copy";
|
||||||
|
button.addEventListener("click", () => copyCodeToClipboard(button, highlightDiv));
|
||||||
|
highlightDiv.insertBefore(button, highlightDiv.firstChild);
|
||||||
|
const wrapper = document.createElement("div");
|
||||||
|
wrapper.className = "highlight-wrapper";
|
||||||
|
highlightDiv.parentNode.insertBefore(wrapper, highlightDiv);
|
||||||
|
wrapper.appendChild(highlightDiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelectorAll(".highlight").forEach((highlightDiv) => createCopyButton(highlightDiv));
|
||||||
|
|
||||||
|
async function copyCodeToClipboard(button, highlightDiv) {
|
||||||
|
// Need to get just the last-child of each .line element to avoid copying the line numbers
|
||||||
|
const nodeListToCopy = highlightDiv.querySelectorAll(":last-child > .chroma > code > .line > :last-child");
|
||||||
|
// Reduce the nodeList to a string of text with each line separated by a newline
|
||||||
|
const codeToCopy = Array.from(nodeListToCopy).reduce((accumulator, line) => accumulator + line.innerText, "");
|
||||||
|
try {
|
||||||
|
var result = await navigator.permissions.query({ name: "clipboard-write" });
|
||||||
|
if (result.state == "granted" || result.state == "prompt") {
|
||||||
|
await navigator.clipboard.writeText(codeToCopy);
|
||||||
|
} else {
|
||||||
|
copyCodeBlockExecCommand(codeToCopy, highlightDiv);
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
copyCodeBlockExecCommand(codeToCopy, highlightDiv);
|
||||||
|
} finally {
|
||||||
|
button.blur();
|
||||||
|
button.innerText = "Copied!";
|
||||||
|
setTimeout(function () {
|
||||||
|
button.innerText = "Copy";
|
||||||
|
}, 2000); }
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyCodeBlockExecCommand(codeToCopy, highlightDiv) {
|
||||||
|
console.log("We shouldn't get here...");
|
||||||
|
const textArea = document.createElement("textArea");
|
||||||
|
textArea.contentEditable = "true";
|
||||||
|
textArea.readOnly = "false";
|
||||||
|
textArea.className = "copyable-text-area";
|
||||||
|
textArea.value = codeToCopy;
|
||||||
|
highlightDiv.insertBefore(textArea, highlightDiv.firstChild);
|
||||||
|
const range = document.createRange();
|
||||||
|
range.selectNodeContents(textArea);
|
||||||
|
const sel = window.getSelection();
|
||||||
|
sel.removeAllRanges();
|
||||||
|
sel.addRange(range);
|
||||||
|
textArea.setSelectionRange(0, 999999);
|
||||||
|
document.execCommand("copy");
|
||||||
|
highlightDiv.removeChild(textArea);
|
||||||
|
}
|
|
@ -2,3 +2,8 @@
|
||||||
<p class="copyright">{{ .Site.Copyright | markdownify }}</p>
|
<p class="copyright">{{ .Site.Copyright | markdownify }}</p>
|
||||||
<p class="advertisement">Powered by <a target="_blank" href="https://gohugo.io/">hugo</a>, <a target="_blank" href="https://github.com/joeroe/risotto">risotto</a>, and <a target="_blank" href="https://www.netlify.com">netlify</a>. Analytics with <a href="https://withcabin.com/privacy/runtimeterror.dev">Cabin</a>.
|
<p class="advertisement">Powered by <a target="_blank" href="https://gohugo.io/">hugo</a>, <a target="_blank" href="https://github.com/joeroe/risotto">risotto</a>, and <a target="_blank" href="https://www.netlify.com">netlify</a>. Analytics with <a href="https://withcabin.com/privacy/runtimeterror.dev">Cabin</a>.
|
||||||
<br><a target="_blank" href="https://github.com/jbowdre/runtimeterror">View source</a>.</p>
|
<br><a target="_blank" href="https://github.com/jbowdre/runtimeterror">View source</a>.</p>
|
||||||
|
|
||||||
|
{{ if (findRE "<pre" .Content 1) }}
|
||||||
|
{{ $jsCopy := resources.Get "js/code-copy-button.js" | minify }}
|
||||||
|
<script src="{{ $jsCopy.RelPermalink }}"></script>
|
||||||
|
{{ end }}
|
||||||
|
|
|
@ -21,3 +21,10 @@
|
||||||
|
|
||||||
<!-- cabin analytics -->
|
<!-- cabin analytics -->
|
||||||
<script async defer src="https://scripts.withcabin.com/hello.js"></script>
|
<script async defer src="https://scripts.withcabin.com/hello.js"></script>
|
||||||
|
|
||||||
|
{{ if (findRE "<pre" .Content 1) }}
|
||||||
|
{{ $syntax := resources.Get "css/syntax.css" | minify }}
|
||||||
|
<link href="{{ $syntax.RelPermalink }}" rel="stylesheet">
|
||||||
|
{{ $copyCss := resources.Get "css/code-copy-button.css" | minify }}
|
||||||
|
<link href="{{ $copyCss.RelPermalink }}" rel="stylesheet">
|
||||||
|
{{ end }}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
@import 'syntax.css';
|
|
||||||
|
|
||||||
/* override page max-width */
|
/* override page max-width */
|
||||||
.page {
|
.page {
|
||||||
max-width: 72rem;
|
max-width: 72rem;
|
||||||
|
|
Loading…
Reference in a new issue