Add code copy button to all code fields in the blog (#81)

This commit is contained in:
Alexander Dunkel 2019-08-18 16:27:31 +02:00 committed by Track3
parent a9f2ddcfa9
commit 16c9d78ae5
10 changed files with 110 additions and 6 deletions

View file

@ -13,6 +13,7 @@ Hermit is a minimal and fast theme for Hugo. It's built for bloggers who want a
* Featured image is supported. It will be displayed as a dimmed background of the page. * Featured image is supported. It will be displayed as a dimmed background of the page.
* Displays all of your posts on a single page, with one section per year, simple and compact. * Displays all of your posts on a single page, with one section per year, simple and compact.
* Extremely lightweight and load fast. No third party framework, no unnecessary code. * Extremely lightweight and load fast. No third party framework, no unnecessary code.
* All code fields feature syntax highlighting and a code-copy function
* Responsive & Retina Ready. Scales gracefully from a big screen all the way down to the smallest mobile phone. Assets in vector format ensures that it looks sharp on high-resolution screens. * Responsive & Retina Ready. Scales gracefully from a big screen all the way down to the smallest mobile phone. Assets in vector format ensures that it looks sharp on high-resolution screens.
**[Theme Demo](https://hugo-theme-hermit.netlify.com/)** (uses contents and config from the `exampleSite` folder) **[Theme Demo](https://hugo-theme-hermit.netlify.com/)** (uses contents and config from the `exampleSite` folder)
@ -111,5 +112,6 @@ You can inject any html code to every page's document head or right above the cl
* [normalize.css](https://necolas.github.io/normalize.css/) - [MIT](https://github.com/necolas/normalize.css/blob/master/LICENSE.md) * [normalize.css](https://necolas.github.io/normalize.css/) - [MIT](https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
* [animate.css](https://daneden.github.io/animate.css/) - [MIT](https://github.com/daneden/animate.css/blob/master/LICENSE) * [animate.css](https://daneden.github.io/animate.css/) - [MIT](https://github.com/daneden/animate.css/blob/master/LICENSE)
* [feather](https://feathericons.com/) - [MIT](https://github.com/feathericons/feather/blob/master/LICENSE) * [feather](https://feathericons.com/) - [MIT](https://github.com/feathericons/feather/blob/master/LICENSE)
* [code-copy.js](assets/js/code-copy.js) - [Tom Spencer](https://www.fiznool.com/blog/2018/09/14/adding-click-to-copy-buttons-to-a-hugo-powered-blog/)
Thanks! Thanks!

56
assets/js/code-copy.js Normal file
View file

@ -0,0 +1,56 @@
/**
* Utils
*/
// Add code-copy buttons using progressive enhancement
// © 2019. Tom Spencer
// https://www.fiznool.com/blog/2018/09/14/adding-click-to-copy-buttons-to-a-hugo-powered-blog/
(function() {
'use strict';
if(!document.queryCommandSupported('copy')) {
return;
}
function flashCopyMessage(el, msg) {
el.textContent = msg;
setTimeout(function() {
el.textContent = "Copy";
}, 1000);
}
function selectText(node) {
var selection = window.getSelection();
var range = document.createRange();
range.selectNodeContents(node);
selection.removeAllRanges();
selection.addRange(range);
return selection;
}
function addCopyButton(containerEl) {
var copyBtn = document.createElement("button");
copyBtn.className = "highlight-copy-btn";
copyBtn.textContent = "Copy";
var codeEl = containerEl.firstElementChild;
copyBtn.addEventListener('click', function() {
try {
var selection = selectText(codeEl);
document.execCommand('copy');
selection.removeAllRanges();
flashCopyMessage(copyBtn, 'Copied!')
} catch(e) {
console && console.log(e);
flashCopyMessage(copyBtn, 'Failed :\'(')
}
});
containerEl.appendChild(copyBtn);
}
// Add copy button to code blocks
var highlightBlocks = document.getElementsByClassName('highlight');
Array.prototype.forEach.call(highlightBlocks, addCopyButton);
})();

View file

@ -153,6 +153,37 @@ table {
display: none; display: none;
} }
// Code copy buttons
//
.highlight {
position: relative;
}
.highlight pre {
padding-right: 75px;
}
.highlight-copy-btn {
position: absolute;
bottom: 7px;
right: 7px;
border: 0;
border-radius: 4px;
padding: 1px;
font-size: 0.7em;
line-height: 1.8;
color: #fff;
background-color: #777;
opacity: 0.6;
min-width: 55px;
text-align: center;
}
.highlight-copy-btn:hover {
background-color: #666;
}
// Accessibility // Accessibility
// //
.screen-reader-text { .screen-reader-text {

View file

@ -48,6 +48,8 @@ enableEmoji = true # Shorthand emojis in content files - https://gohugo.io/func
relatedPosts = false # Add a related content section to all single posts page relatedPosts = false # Add a related content section to all single posts page
code_copy_button = true # Turn on/off the code-copy-button for code-fields
# Add custom css # Add custom css
# customCSS = ["css/foo.css", "css/bar.css"] # customCSS = ["css/foo.css", "css/bar.css"]

View file

@ -30,8 +30,17 @@
{{ block "header" . -}}{{ end -}} {{ block "header" . -}}{{ end -}}
{{ block "main" . -}}{{ end -}} {{ block "main" . -}}{{ end -}}
{{ block "footer" . -}}{{ end }} {{ block "footer" . -}}{{ end }}
{{ $script := resources.Get "js/main.js" | minify | fingerprint -}}
<script src="{{ $script.Permalink }}" {{ printf "integrity=%q" $script.Data.Integrity | safeHTMLAttr }} crossorigin="anonymous"></script> {{ $main := resources.Get "js/main.js" -}}
{{ if .Site.Params.code_copy_button | default true -}}
{{ $codeCopy := resources.Get "js/code-copy.js" -}}
{{ $script := slice $main $codeCopy | resources.Concat "js/bundle.js" | minify | fingerprint -}}
<script src="{{ $script.Permalink }}" {{ printf "integrity=%q" $script.Data.Integrity | safeHTMLAttr }} crossorigin="anonymous"></script>
{{ else -}}
{{ $script := $main | minify | fingerprint -}}
<script src="{{ $script.Permalink }}" {{ printf "integrity=%q" $script.Data.Integrity | safeHTMLAttr }} crossorigin="anonymous"></script>
{{ end }}
{{- partial "analytics.html" . }} {{- partial "analytics.html" . }}
{{- if templates.Exists "partials/extra-foot.html" -}} {{- if templates.Exists "partials/extra-foot.html" -}}
{{ partial "extra-foot.html" . }} {{ partial "extra-foot.html" . }}

View file

@ -6,4 +6,8 @@ mobileMenuVisible=false;}}
const showImg=()=>{document.querySelector('.bg-img').classList.add('show-bg-img');} const showImg=()=>{document.querySelector('.bg-img').classList.add('show-bg-img');}
const hideImg=()=>{document.querySelector('.bg-img').classList.remove('show-bg-img');} const hideImg=()=>{document.querySelector('.bg-img').classList.remove('show-bg-img');}
const toggleToc=()=>{document.getElementById('toc').classList.toggle('show-toc');} const toggleToc=()=>{document.getElementById('toc').classList.toggle('show-toc');}
if(header!==null){listen('#menu-btn',"click",toggleMobileMenu);listen('#toc-btn',"click",toggleToc);listen('#img-btn',"click",showImg);listen('.bg-img',"click",hideImg);document.querySelectorAll('.post-year').forEach((ele)=>{ele.addEventListener('click',()=>{window.location.hash='#'+ele.id;});});window.addEventListener('scroll',throttle(()=>{autoHideHeader();if(mobileMenuVisible==true){toggleMobileMenu();}},250));} if(header!==null){listen('#menu-btn',"click",toggleMobileMenu);listen('#toc-btn',"click",toggleToc);listen('#img-btn',"click",showImg);listen('.bg-img',"click",hideImg);document.querySelectorAll('.post-year').forEach((ele)=>{ele.addEventListener('click',()=>{window.location.hash='#'+ele.id;});});window.addEventListener('scroll',throttle(()=>{autoHideHeader();if(mobileMenuVisible==true){toggleMobileMenu();}},250));};(function(){'use strict';if(!document.queryCommandSupported('copy')){return;}
function flashCopyMessage(el,msg){el.textContent=msg;setTimeout(function(){el.textContent="Copy";},1000);}
function selectText(node){var selection=window.getSelection();var range=document.createRange();range.selectNodeContents(node);selection.removeAllRanges();selection.addRange(range);return selection;}
function addCopyButton(containerEl){var copyBtn=document.createElement("button");copyBtn.className="highlight-copy-btn";copyBtn.textContent="Copy";var codeEl=containerEl.firstElementChild;copyBtn.addEventListener('click',function(){try{var selection=selectText(codeEl);document.execCommand('copy');selection.removeAllRanges();flashCopyMessage(copyBtn,'Copied!')}catch(e){console&&console.log(e);flashCopyMessage(copyBtn,'Failed :\'(')}});containerEl.appendChild(copyBtn);}
var highlightBlocks=document.getElementsByClassName('highlight');Array.prototype.forEach.call(highlightBlocks,addCopyButton);})();

View file

@ -0,0 +1 @@
{"Target":"js/bundle.min.4a9a0ac3d2217822c7865b4161e6c2a71de1d70492264337755427898dd718f6.js","MediaType":"application/javascript","Data":{"Integrity":"sha256-SpoKw9IheCLHhltBYebCpx3h1wSSJkM3dVQniY3XGPY="}}

View file

@ -1 +0,0 @@
{"Target":"js/main.min.35ccbf1cdceb91e4c64c06b5d009d6e2977fafe56beda7762febd4e67528d2ac.js","MediaType":"application/javascript","Data":{"Integrity":"sha256-Ncy/HNzrkeTGTAa10AnW4pd/r+Vr7ad2L+vU5nUo0qw="}}

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
{"Target":"css/style.min.568c54a56780af2a70c45272522247710b69dbfc080b315211fb98381e3796f8.css","MediaType":"text/css","Data":{"Integrity":"sha256-VoxUpWeArypwxFJyUiJHcQtp2/wICzFSEfuYOB43lvg="}} {"Target":"css/style.min.5ee5e7976cd09872c64e40a582206543f6aa38c69a8c43898aadc70040344b92.css","MediaType":"text/css","Data":{"Integrity":"sha256-XuXnl2zQmHLGTkClgiBlQ/aqOMaajEOJiq3HAEA0S5I="}}