Add copy button to code blocks (closes #18)

This commit is contained in:
Ondřej 2022-10-24 00:40:58 +02:00
parent 6354a2fc09
commit 8a18e31ced
5 changed files with 61 additions and 2 deletions

View file

@ -113,6 +113,7 @@ pre {
border-radius: 0.25em; border-radius: 0.25em;
overflow-x: auto; overflow-x: auto;
padding: 1em; padding: 1em;
position: relative;
} }
.code-inline, .code-inline,

View file

@ -5,3 +5,47 @@ window.Alpine = Alpine;
Alpine.plugin(persist); Alpine.plugin(persist);
Alpine.start(); Alpine.start();
/* Copy button component */
const copyIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M7 4V2h10v2h3.007c.548 0 .993.445.993.993v16.014a.994.994 0 0 1-.993.993H3.993A.994.994 0 0 1 3 21.007V4.993C3 4.445 3.445 4 3.993 4H7zm0 2H5v14h14V6h-2v2H7V6zm2-2v2h6V4H9z"/></svg>`;
const checkIcon = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"/></svg>`;
const defaultButtonLabel = `Zkopírovat text do schránky`;
const copiedButtonLabel = `Text zkopírován`;
const revertTimeout = 5_000;
const accessibleLabel = (text) => `<span class="sr-only">${text}</span>`;
const defaultButtonContent = copyIcon + accessibleLabel(defaultButtonLabel);
const copiedButtonContent = checkIcon + accessibleLabel(copiedButtonLabel);
navigator.permissions
.query({
name: "clipboard-read",
})
.then((permission) => {
if (permission.state == "denied") {
return;
}
document
.querySelectorAll(".prose pre code, .code-block")
.forEach((codeBlock) => {
const button = document.createElement("button");
button.innerHTML = defaultButtonContent;
button.className = "button absolute top-3 right-3 bg-white";
button.addEventListener("click", () => {
navigator.clipboard.writeText(codeBlock.innerHTML).then(() => {
button.innerHTML = copiedButtonContent;
setTimeout(() => {
button.innerHTML = defaultButtonContent;
}, revertTimeout);
});
});
codeBlock.parentElement.insertBefore(
button,
codeBlock.nextElementSibling
);
});
});

View file

@ -630,6 +630,14 @@ video {
right: 1.5rem; right: 1.5rem;
} }
.top-3 {
top: 0.75rem;
}
.right-3 {
right: 0.75rem;
}
.z-10 { .z-10 {
z-index: 10; z-index: 10;
} }
@ -784,6 +792,11 @@ video {
background-color: rgb(243 244 246 / var(--tw-bg-opacity)); background-color: rgb(243 244 246 / var(--tw-bg-opacity));
} }
.bg-white {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
}
.px-6 { .px-6 {
padding-left: 1.5rem; padding-left: 1.5rem;
padding-right: 1.5rem; padding-right: 1.5rem;
@ -966,6 +979,7 @@ pre {
border-radius: 0.25em; border-radius: 0.25em;
overflow-x: auto; overflow-x: auto;
padding: 1em; padding: 1em;
position: relative;
} }
.code-inline, .code-inline,

View file

@ -1,2 +1,2 @@
{{- $js := resources.Get "main.js" | js.Build | resources.Minify | fingerprint -}} {{- $js := resources.Get "main.js" | js.Build | fingerprint -}}
<script src="{{ $js.RelPermalink }}" integrity="{{ $js.Data.Integrity }}" defer></script> <script src="{{ $js.RelPermalink }}" integrity="{{ $js.Data.Integrity }}" defer></script>

View file

@ -2,7 +2,7 @@ const colors = require("tailwindcss/");
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
module.exports = { module.exports = {
content: ["content/**/*.md", "layouts/**/*.html"], content: ["content/**/*.md", "layouts/**/*.html", "assets/**/*.js"],
theme: { theme: {
fontFamily: { fontFamily: {
main: [ main: [