From 99722c58fa1455702cff7815a1295f3e2887f422 Mon Sep 17 00:00:00 2001 From: XhmikosR <xhmikosr@gmail.com> Date: Sun, 27 Nov 2022 17:34:33 +0200 Subject: [PATCH] Add copy support --- package-lock.json | 39 +++++++++++++++++++++++ package.json | 1 + src/assets/js/application.js | 51 ++++++++++++++++++++++++++++++ src/assets/scss/_clipboard-js.scss | 35 ++++++++++++++++++++ src/assets/scss/style.scss | 1 + src/layouts/partials/icons.html | 9 +++++- src/layouts/partials/scripts.html | 11 +++++-- 7 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 src/assets/js/application.js create mode 100644 src/assets/scss/_clipboard-js.scss diff --git a/package-lock.json b/package-lock.json index 1395da5da..e548ac747 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "devDependencies": { "autoprefixer": "^10.4.13", "bootstrap": "5.2.3", + "clipboard": "^2.0.11", "cross-env": "^7.0.3", "find-unused-sass-variables": "^4.0.5", "hugo-bin": "^0.95.0", @@ -840,6 +841,17 @@ "fsevents": "~2.3.2" } }, + "node_modules/clipboard": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz", + "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==", + "dev": true, + "dependencies": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -1238,6 +1250,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==", + "dev": true + }, "node_modules/dependency-graph": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", @@ -2085,6 +2103,15 @@ "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", "dev": true }, + "node_modules/good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", + "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==", + "dev": true, + "dependencies": { + "delegate": "^3.1.2" + } + }, "node_modules/got": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", @@ -4624,6 +4651,12 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "node_modules/select": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz", + "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==", + "dev": true + }, "node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -5623,6 +5656,12 @@ "node": ">=0.10.0" } }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", + "dev": true + }, "node_modules/to-buffer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", diff --git a/package.json b/package.json index 8770bfea7..b92915060 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "devDependencies": { "autoprefixer": "^10.4.13", "bootstrap": "5.2.3", + "clipboard": "^2.0.11", "cross-env": "^7.0.3", "find-unused-sass-variables": "^4.0.5", "hugo-bin": "^0.95.0", diff --git a/src/assets/js/application.js b/src/assets/js/application.js new file mode 100644 index 000000000..031a07e28 --- /dev/null +++ b/src/assets/js/application.js @@ -0,0 +1,51 @@ +/* eslint-env browser */ + +/* global ClipboardJS:false */ + +import ClipboardJS from 'clipboard'; + +const btnHtml = `<div class="bd-clipboard"> + <button type="button" class="btn-clipboard" title="Copy to clipboard"> + <svg class="bi" width="1em" height="1em" fill="currentColor"> + <use xlink:href="#clipboard"/> + </svg> + </button> +</div>`; + +document.querySelectorAll('div.highlight') + .forEach((element) => { + element.insertAdjacentHTML('beforebegin', btnHtml) + }) + +const clipboard = new ClipboardJS('.btn-clipboard', { + target: trigger => trigger.parentNode.nextElementSibling +}) + +clipboard.on('success', (event) => { + const iconFirstChild = event.trigger.querySelector('.bi').firstElementChild + const namespace = 'http://www.w3.org/1999/xlink' + const originalXhref = iconFirstChild.getAttributeNS(namespace, 'href') + const originalTitle = event.trigger.title + + event.clearSelection() + iconFirstChild.setAttributeNS(namespace, 'href', originalXhref.replace('clipboard', 'check2')) + event.trigger.title = 'Copied!' + + setTimeout(() => { + iconFirstChild.setAttributeNS(namespace, 'href', originalXhref) + event.trigger.title = originalTitle + }, 2000) +}) + +/*clipboard.on('error', () => { + const modifierKey = /mac/i.test(navigator.userAgent) ? '\u2318' : 'Ctrl-' + const fallbackMsg = 'Press ' + modifierKey + 'C to copy' + const errorElement = document.getElementById('copy-error-callout') + + if (!errorElement) { + return + } + + errorElement.classList.remove('d-none') + errorElement.insertAdjacentHTML('afterbegin', fallbackMsg) +})*/ diff --git a/src/assets/scss/_clipboard-js.scss b/src/assets/scss/_clipboard-js.scss new file mode 100644 index 000000000..5d4cbba44 --- /dev/null +++ b/src/assets/scss/_clipboard-js.scss @@ -0,0 +1,35 @@ +// clipboard.js +// +// JS-based `Copy` buttons for code snippets. + +.bd-clipboard { + position: relative; + display: none; + float: right; + + + .highlight { + margin-top: 0; + } + + @media (min-width: 768px) { + display: block; + } +} + +.btn-clipboard { + position: absolute; + top: .75em; + right: .5em; + z-index: 10; + display: block; + padding: .5em .75em .625em; + line-height: 1; + color: var(--bs-gray-900); + background-color: var(--bs-gray-100); + border: 0; + border-radius: .25rem; + + &:hover { + color: var(--bs-primary); + } +} diff --git a/src/assets/scss/style.scss b/src/assets/scss/style.scss index f25ea4633..8e6cc6dd1 100644 --- a/src/assets/scss/style.scss +++ b/src/assets/scss/style.scss @@ -46,6 +46,7 @@ @import "footer"; @import "theme"; @import "ads"; +@import "clipboard-js"; .f0 { font-size: 3rem; } .f2 { font-size: 2rem; } diff --git a/src/layouts/partials/icons.html b/src/layouts/partials/icons.html index 722a8ab8b..46b6753ab 100644 --- a/src/layouts/partials/icons.html +++ b/src/layouts/partials/icons.html @@ -1,7 +1,14 @@ -<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> +<svg xmlns="http://www.w3.org/2000/svg" style="display: none"> <symbol id="archive" viewBox="0 0 16 16"> <path d="M0 2a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1v7.5a2.5 2.5 0 0 1-2.5 2.5h-9A2.5 2.5 0 0 1 1 12.5V5a1 1 0 0 1-1-1V2zm2 3v7.5A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5V5H2zm13-3H1v2h14V2zM5 7.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5z"/> </symbol> + <symbol id="check2" viewBox="0 0 16 16"> + <path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/> + </symbol> + <symbol id="clipboard" viewBox="0 0 16 16"> + <path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/> + <path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/> + </symbol> <symbol id="file-earmark-richtext" viewBox="0 0 16 16"> <path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/> <path d="M4.5 12.5A.5.5 0 0 1 5 12h3a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm0-2A.5.5 0 0 1 5 10h6a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm1.639-3.708 1.33.886 1.854-1.855a.25.25 0 0 1 .289-.047l1.888.974V8.5a.5.5 0 0 1-.5.5H5a.5.5 0 0 1-.5-.5V8s1.54-1.274 1.639-1.208zM6.25 6a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5z"/> diff --git a/src/layouts/partials/scripts.html b/src/layouts/partials/scripts.html index d4c42dcce..fe1ac08aa 100644 --- a/src/layouts/partials/scripts.html +++ b/src/layouts/partials/scripts.html @@ -1,11 +1,18 @@ {{- $bootstrapJs := resources.Get "/js/bootstrap.min.js" | resources.Copy "/assets/js/vendor/bootstrap.min.js" -}} <script async src="{{ $bootstrapJs.Permalink | relURL }}"></script> -{{- $esbuildOptions := dict "targetPath" "/assets/js/lazyload.js" "target" "es2019" -}} +{{- $esbuildOptions := dict "target" "es2019" -}} +{{- $lazyloadOptions := dict "targetPath" "/assets/js/lazyload.js" -}} +{{- $appOptions := dict "targetPath" "/assets/js/application.js" -}} {{- if eq hugo.Environment "production" -}} {{- $esbuildOptions = merge $esbuildOptions (dict "minify" "true") -}} {{- end -}} -{{- $lazyload := resources.Get "js/lazyload.js" | js.Build $esbuildOptions }} +{{- $lazyloadOptions = merge $esbuildOptions $lazyloadOptions -}} +{{- $appOptions = merge $esbuildOptions $appOptions -}} +{{- $lazyload := resources.Get "js/lazyload.js" | js.Build $lazyloadOptions -}} +{{- $application := resources.Get "js/application.js" | js.Build $appOptions -}} + <script async src="{{ $lazyload.RelPermalink | relURL }}"></script> +<script async src="{{ $application.RelPermalink | relURL }}"></script>