From e906cf0c1f6cb1572ec6994e50c79b7218a18924 Mon Sep 17 00:00:00 2001 From: Robert McGovern Date: Sun, 19 Feb 2023 15:32:01 +0000 Subject: [PATCH] Article about copy-code-blocks Plus some changes to CSS so behaves, and mostly looks the same across Safari, Chrome and Firefox. Plus amendment to not make the footnotes absolutely tiny. --- src/_posts/2023-02-19-copy-code-blocks.md | 157 ++++++++++++++++++++ src/assets/css/main.scss.liquid | 48 +++++- src/assets/images/posts/copy-code-block.png | Bin 0 -> 7015 bytes 3 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 src/_posts/2023-02-19-copy-code-blocks.md create mode 100644 src/assets/images/posts/copy-code-block.png diff --git a/src/_posts/2023-02-19-copy-code-blocks.md b/src/_posts/2023-02-19-copy-code-blocks.md new file mode 100644 index 0000000..32a3261 --- /dev/null +++ b/src/_posts/2023-02-19-copy-code-blocks.md @@ -0,0 +1,157 @@ +--- +title: Copy Code Blocks +tags: [webdev, site] +category: [site, webdev] +date: 2023-02-19 +--- + +I've been reading [Bryce Wray's](https://www.brycewray.com) site for a little while. Its been interesting watching Bryce switch back and forth between [Hugo](https://github.com/brycewray/hugo_site) and [Eleventy / 11ty](https://github.com/brycewray/eleventy_site). Keeping both in sync feature wise. + +Just recently Bryce did a post about adding [Code for Copying Code](https://www.brycewray.com/posts/2023/02/code-copying-code-eleventy-edition/) in Eleventy. Essentially for any code block, it adds an icon (or you could switch it out for text) inside a code block that you can click and it will copy the code to your clipboard. + +On my site, the icon looks like this[^1]: + +![Screenshot of the top right corned of a code block. There is a small grey button with two overlapping squares shown](/assets/images/posts/copy-code-block.png) + +I am using Bryce's JavaScript virtually verbatim. The only difference is that I hook into the `load` event. Source from [copy-code-button.js](https://github.com/tarasis/tarasis.net/blob/main/src/assets/js/copy-code-button.js), with a minor change to the colour of the `svgCheck`. + +```js +/* +h/t to... +- https://www.brycewray.com/posts/2023/02/code-copying-code-eleventy-edition/ & Bryce's gitrepo +- https://github.com/brycewray/eleventy_site/blob/main/src/assets/js/copy-code-button.js +- https://www.dannyguo.com/blog/how-to-add-copy-to-clipboard-buttons-to-code-blocks-in-hugo/ +- https://aaronluna.dev/blog/add-copy-button-to-code-blocks-hugo-chroma/ +- https://simplernerd.com/hugo-add-copy-to-clipboard-button/ +- https://digitaldrummerj.me/hugo-add-copy-code-snippet-button/ +*/ + +const svgCopy = + ' Click to copy code'; +const svgCheck = + ' Copied!'; + +const addCopyButtons = (clipboard) => { + // 1. Look for pre > code elements in the DOM + document.querySelectorAll("pre > code").forEach((codeBlock) => { + // 2. Create a button that will trigger a copy operation + const button = document.createElement("button"); + button.className = "clipboard-button"; + button.type = "button"; + button.innerHTML = svgCopy; + button.addEventListener("click", () => { + clipboard.writeText(codeBlock.innerText).then( + () => { + button.blur(); + button.innerHTML = svgCheck; + setTimeout(() => (button.innerHTML = svgCopy), 2000); + }, + (error) => (button.innerHTML = "Error") + ); + }); + // 3. Append the button directly before the pre tag + // (with content-searching fix in place for Prism) + const pre = codeBlock.parentNode; + pre.parentNode.insertBefore(button, pre); + }); +}; + +function addCopyButtonsAfterLoad() { + if (navigator && navigator.clipboard) { + addCopyButtons(navigator.clipboard); + } else { + const script = document.createElement("script"); + script.src = + "https://cdnjs.cloudflare.com/ajax/libs/clipboard-polyfill/2.7.0/clipboard-polyfill.promise.js"; + script.integrity = + "sha256-waClS2re9NUbXRsryKoof+F9qc1gjjIhc2eT7ZbIv94="; + script.crossOrigin = "anonymous"; + script.onload = () => addCopyButtons(clipboard); + document.body.appendChild(script); + } +} + +window.addEventListener("load", addCopyButtonsAfterLoad); +``` + +As this site is a franken-baby, an 11ty site using the Minimal Mistakes theme, I needed to use `liquid` rather than `njk` code in my page layout file. So in [single.html](https://github.com/tarasis/tarasis.net/blob/main/src/_includes/layouts/single.html) I removed {% raw %}`{{content}}`{% endraw %} and replaced it with: + +{% raw %} +```liquid +{% assign Content = content %} +{% assign withoutDivStart = '
 svg {
+//	fill: gray-500;
+}
+.clipboard-button:hover {
+	cursor: pointer;
+	border-color: #01b139;
+	background-color: #87d09e;
+	opacity: 1;
+}
+.clipboard-button:hover > svg {
+	fill: #1900ff;
+}
+.clipboard-button:focus {
+	outline: 0;
+}
+// .highlight {
+//   position: relative;
+// }
+.highlight:hover > .clipboard-button {
+	opacity: 1;
+	transition: 0.2s;
+}
+
+.highlight {
+	line-height: 1.5;
+}
+```
+
+And thats it, the site now supports copying the code from the blocks at the press of a button.
+
+[^1]: I'm well aware that the following CSS should be out in its own file. Aside, the reason this file has a `liquid` file extension is that during the site build the theme is baked into it from a variable in [site.json](https://github.com/tarasis/tarasis.net/blob/main/src/_data/site.json). I then use a separate step to sass build the compiled file.
+
+[^2]: I had to do a hack to great rid of additional space that was being added despite `margin`, `padding` and anything else being set to `0`. I have tried various combinations. In theory nothing is adding `padding` or `margin` to the `table`, `tbody`, `tr`, or `td`; indeed they are explicitly set to 0. And yet there was still a chunk of spacing being added. I discovered that setting portions to `display:` `flex` & `inline-flex` solved the issue for Safari / Chrome, and `flex` and `ruby-base` for Firefox. All are hacks, but they provide the results that I want. (I was up to 4am trying to find that behaviours I wanted; not helped by Firefox not yet supporting `:has()` 🙈)
diff --git a/src/assets/css/main.scss.liquid b/src/assets/css/main.scss.liquid
index 016c908..fa1895a 100644
--- a/src/assets/css/main.scss.liquid
+++ b/src/assets/css/main.scss.liquid
@@ -147,9 +147,43 @@ div.highlight {
 	position: relative;
 }
 
-.codeblock {
-  outline: 4px #7e1c1c solid;
-  border-radius: 5px;
+div pre {
+  border: 4px #7e1c1c solid;
+  border-radius: 20px;
+}
+
+code {
+  outline: 1px #7e1c1c solid;
+}
+
+code > span {
+  padding-left: 10px;
+}
+
+// messes up table code blocks
+//code > span:first-child {
+//  padding-top: 5px;
+//}
+
+//code > span:last-child {
+//  padding-bottom: 5px;
+//}
+
+// hack to remove outline that above sets
+td > div > pre {
+  border: unset;
+}
+
+pre > code > div {
+  display: flex;
+}
+
+td {
+  display: inline-flex;
+  // below is hack so Firefox looks the same as Safari / Chrome.
+  // Both will ignore the assignment because they don't know about "ruby-base"
+  // https://caniuse.com/mdn-css_properties_display_ruby_values
+  display: ruby-base;
 }
 
 // === for copy-code-to-clipboard
@@ -187,9 +221,7 @@ div.highlight {
 .clipboard-button:focus {
 	outline: 0;
 }
-// .highlight {
-//   position: relative;
-// }
+
 .highlight:hover > .clipboard-button {
 	opacity: 1;
 	transition: 0.2s;
@@ -199,7 +231,6 @@ div.highlight {
 	line-height: 1.5;
 }
 
-
 //@import "progress.css"; // for progress bar
 //@import "minimal-mistakes/skins/{{ site.minimal_mistakes_skin | default: 'default' }}"; // skin
 @import "minimal-mistakes/skins/_{{ site.minimal_mistakes_skin | default: 'default' }}.scss"; // skin
@@ -207,3 +238,6 @@ div.highlight {
 //@import "assets/css/override-notices.scss"
 @import "override-notices.scss";
 
+.footnotes ol, .footnotes li {
+  font-size: 1em;
+}
\ No newline at end of file
diff --git a/src/assets/images/posts/copy-code-block.png b/src/assets/images/posts/copy-code-block.png
new file mode 100644
index 0000000000000000000000000000000000000000..32f3993489d414310a1031f32a29311c865ee40a
GIT binary patch
literal 7015
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
HErS0C65CrG

literal 0
HcmV?d00001