Add code copy button to all code fields in the blog (#81)
This commit is contained in:
parent
a9f2ddcfa9
commit
16c9d78ae5
10 changed files with 110 additions and 6 deletions
|
@ -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
56
assets/js/code-copy.js
Normal 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);
|
||||||
|
})();
|
|
@ -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 {
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
||||||
|
|
|
@ -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" . }}
|
||||||
|
|
|
@ -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);})();
|
|
@ -0,0 +1 @@
|
||||||
|
{"Target":"js/bundle.min.4a9a0ac3d2217822c7865b4161e6c2a71de1d70492264337755427898dd718f6.js","MediaType":"application/javascript","Data":{"Integrity":"sha256-SpoKw9IheCLHhltBYebCpx3h1wSSJkM3dVQniY3XGPY="}}
|
|
@ -1 +0,0 @@
|
||||||
{"Target":"js/main.min.35ccbf1cdceb91e4c64c06b5d009d6e2977fafe56beda7762febd4e67528d2ac.js","MediaType":"application/javascript","Data":{"Integrity":"sha256-Ncy/HNzrkeTGTAa10AnW4pd/r+Vr7ad2L+vU5nUo0qw="}}
|
|
2
resources/_gen/assets/scss/scss/style.scss_c16d144eee185fbddd582cd5e25a4fae.content
Normal file → Executable file
2
resources/_gen/assets/scss/scss/style.scss_c16d144eee185fbddd582cd5e25a4fae.content
Normal file → Executable file
File diff suppressed because one or more lines are too long
2
resources/_gen/assets/scss/scss/style.scss_c16d144eee185fbddd582cd5e25a4fae.json
Normal file → Executable file
2
resources/_gen/assets/scss/scss/style.scss_c16d144eee185fbddd582cd5e25a4fae.json
Normal file → Executable 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="}}
|
Loading…
Reference in a new issue