From 05206ec0215fc0f004b5bd7bf225981aed655b3d Mon Sep 17 00:00:00 2001 From: Dmitry Kolosov Date: Tue, 18 Jan 2022 19:56:02 +0300 Subject: [PATCH] add copy code button and config param #122 --- README.md | 7 ++-- assets/css/main.css | 41 ++++++++++++++++++ assets/js/copy-code.js | 64 +++++++++++++++++++++++++++++ exampleSite/config.toml | 1 + exampleSiteMultilingual/config.toml | 1 + layouts/partials/head.html | 4 ++ 6 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 assets/js/copy-code.js diff --git a/README.md b/README.md index 83f1ede..8541a0d 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ params: style: light-without-switcher readMore: false disableSummary: false + copyCodeButton: true # true by default # utteranc.es support utterancesRepo: "" # mandatory utterancesTheme: "" # optional @@ -131,9 +132,9 @@ Now enter [`localhost:1313`](http://localhost:1313/) in the address bar of your ### Dark Mode Customize via `style` param in `params` section of config. Options: -- `light-without-switcher` - light theme, without switcher, JS-free (by default) -- `dark-without-switcher` - dark theme, without switcher, JS-free -- `auto-without-switcher` - theme based on user system settings, without switcher, JS-free +- `light-without-switcher` - light theme, without switcher (by default) +- `dark-without-switcher` - dark theme, without switcher +- `auto-without-switcher` - theme based on user system settings, without switcher - `light` - light theme by default, can be switched by user to dark theme and back. Theme settings are saved for user - `dark` - dark theme by default, can be switched by user to light theme and back. Theme settings are saved for user - `auto` - theme based on user system settings by default, can be switched by user to dark/light theme. Theme settings are saved for user (by default in example sites) diff --git a/assets/css/main.css b/assets/css/main.css index c2b9032..3015f38 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -640,6 +640,47 @@ ul.language-select > li, ul.footer-menu > li { display: none; } +/* Copy code */ +.highlight { + position: relative; +} +.highlight pre { + padding-right: 75px; +} + +.highlight:hover .highlight-copy-btn { + display: inline-block; + border: 1px solid var(--bg-color); +} + +.highlight-copy-btn { + display: none; + position: absolute; + top: 7px; + right: 7px; + border: 0; + border-radius: 4px; + padding: 1px; + font-size: 0.7em; + line-height: 1.8; + color: #fff; + background-color: #777; + min-width: 25px; + text-align: center; + border-radius: 5px; +} +.highlight-copy-btn:hover { + transition-duration: .1s; + background-color: #666; + border: 1px solid var(--bq-color) !important; +} + +.highlight-copy-btn, +.highlight-copy-btn svg { + vertical-align: middle; + margin: 8px; +} + /* Media Queries */ @media (max-width: 840px) { diff --git a/assets/js/copy-code.js b/assets/js/copy-code.js new file mode 100644 index 0000000..e772f52 --- /dev/null +++ b/assets/js/copy-code.js @@ -0,0 +1,64 @@ +/* +This file has been taken from following blogpost with some modifications: +https://koki-nakamura22.github.io/blog/2019/10/03/hugo-adding-copy-button/ +Many thanks to Koki Nakamura! +*/ + +document.addEventListener("DOMContentLoaded", function(event) { + 'use strict'; + + if(!document.queryCommandSupported('copy')) { + return; + } + + let svgCopyCode = ''; + let svgSuccessCode = ''; + let svgFailCode = ''; + + function changeIcon(el, innerHtml) { + el.innerHTML = innerHtml; + setTimeout(() => { + el.innerHTML = svgCopyCode; + }, 1000); + } + + function selectText(node) { + let selection = window.getSelection(); + let range = document.createRange(); + if (node.childElementCount === 2) { + // Skip the title. + range.selectNodeContents(node.children[1]); + } else { + range.selectNodeContents(node); + } + selection.removeAllRanges(); + selection.addRange(range); + return selection; + } + + function addCopyButton(containerEl) { + let copyBtn = document.createElement("button"); + copyBtn.className = "highlight-copy-btn"; + copyBtn.innerHTML = svgCopyCode; + + let codeEl = containerEl.firstElementChild; + copyBtn.addEventListener('click', () => { + try { + let selection = selectText(codeEl); + document.execCommand('copy'); + selection.removeAllRanges(); + + changeIcon(copyBtn, svgSuccessCode) + } catch(e) { + console && console.log(e); + changeIcon(copyBtn, svgFailCode) + } + }); + + containerEl.appendChild(copyBtn); + } + + // Add copy button to code blocks + let highlightBlocks = document.getElementsByClassName('highlight'); + Array.prototype.forEach.call(highlightBlocks, addCopyButton); +}, false); \ No newline at end of file diff --git a/exampleSite/config.toml b/exampleSite/config.toml index 681db9c..10499ad 100644 --- a/exampleSite/config.toml +++ b/exampleSite/config.toml @@ -17,6 +17,7 @@ dateFormat = "2006-01-02" paginationSinglePost = true style = "auto" readMore = false +copyCodeButton = true # utteranc.es support utterancesRepo = "" # mandatory diff --git a/exampleSiteMultilingual/config.toml b/exampleSiteMultilingual/config.toml index eb3e094..f20c615 100644 --- a/exampleSiteMultilingual/config.toml +++ b/exampleSiteMultilingual/config.toml @@ -18,6 +18,7 @@ dateFormat = "2006-01-02" paginationSinglePost = true style = "auto" readMore = false +copyCodeButton = true # utteranc.es support utterancesRepo = "" # mandatory diff --git a/layouts/partials/head.html b/layouts/partials/head.html index e882d5d..e62029c 100644 --- a/layouts/partials/head.html +++ b/layouts/partials/head.html @@ -18,6 +18,10 @@ {{ partial "favicons.html" . }} {{ partial "resource.html" (dict "context" . "type" "css" "filename" "css/main.css") }} +{{ if .Site.Params.copyCodeButton | default true }} + {{ partial "resource.html" (dict "context" . "type" "js" "filename" "js/copy-code.js") }} +{{ end }} + {{ range .Site.Params.customJS -}} {{ partial "resource.html" (dict "context" $ "type" "js" "filename" . ) }} {{- end }}