From de7048f920d410c3fb33f4522da8bc94bdd9fcc0 Mon Sep 17 00:00:00 2001 From: Geri Ochoa Date: Wed, 28 Sep 2022 15:45:24 -0400 Subject: [PATCH] Add click to copy button Adds a click to copy button to code samples that don't use the Prism highlighter. --- assets/js/click-to-copy.js | 86 +++++++++++++++++++++++++++++++++++ assets/scss/_code.scss | 48 ++++++++++++++++++- layouts/partials/scripts.html | 5 +- 3 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 assets/js/click-to-copy.js diff --git a/assets/js/click-to-copy.js b/assets/js/click-to-copy.js new file mode 100644 index 0000000000..5fdb041a86 --- /dev/null +++ b/assets/js/click-to-copy.js @@ -0,0 +1,86 @@ +let codeListings = document.querySelectorAll('.highlight > pre'); + +for (let index = 0; index < codeListings.length; index++) +{ + const codeSample = codeListings[index].querySelector('code'); + const copyButton = document.createElement("button"); + copyButton.setAttribute('type', 'button'); + copyButton.onclick = function() { copyCode(codeSample); }; + copyButton.classList.add('fas', 'fa-copy'); + + const buttonTooltip = document.createElement('div'); + buttonTooltip.classList.add('c2c-tooltip'); + buttonTooltip.setAttribute('role', 'tooltip'); + buttonTooltip.innerHTML += 'Copy to clipboard'; + + const buttonDiv = document.createElement('div'); + buttonDiv.classList.add('click-to-copy'); + + // Use Popper to create and handle the tooltip behavior. + + const popperInstance = Popper.createPopper(copyButton, buttonTooltip, + { + modifiers: + [ + { + name: 'offset', + options: + { + offset: [0, -48], + }, + }, + ], + }); + + copyButton.addEventListener('click', () => + { + buttonTooltip.innerHTML = 'Copied!'; + }); + + copyButton.addEventListener('mouseenter', () => + { + buttonTooltip.setAttribute('show-tooltip', ''); + + // Enable eventListeners when the code block is on the viewport + + popperInstance.setOptions((options) => ({ + ...options, + modifiers: + [ + ...options.modifiers, + { name: 'eventListeners', enabled: true }, + ], + })); + popperInstance.update(); + }); + + copyButton.addEventListener('mouseleave', () => + { + buttonTooltip.removeAttribute('show-tooltip'); + + // Reset the message in case the button was clicked + buttonTooltip.innerHTML = 'Copy to clipboard'; + + // Disble eventListeners when the code block is NOT on the viewport + + popperInstance.setOptions((options) => ({ + ...options, + modifiers: + [ + ...options.modifiers, + { name: 'eventListeners', enabled: false }, + ], + })); + }); + + buttonDiv.append(copyButton); + buttonDiv.append(buttonTooltip); + codeListings[index].insertBefore(buttonDiv, codeSample); + +} + +function copyCode(codeSample) +{ + navigator.clipboard.writeText(codeSample.textContent.trim()); +} + diff --git a/assets/scss/_code.scss b/assets/scss/_code.scss index d491beeb45..b96850bff6 100644 --- a/assets/scss/_code.scss +++ b/assets/scss/_code.scss @@ -7,10 +7,56 @@ margin: 2rem 0; padding: 0; - + position: relative; + + .click-to-copy { + display: block; + text-align: right; + height: 1ex; + } + pre { margin: 0; padding: 1rem; + + // Default click-to-copy button + + button { + position: absolute; + color: $gray-400; + border-radius: 3px; + border-width: 0; + background-color: inherit; + box-shadow: 1px 1px $gray-400; + right: 8px; + top: 6px; + + &:hover { + color: $dark; + background-color: $gray-400; + } + &:active { + color: $dark; + background-color: $gray-400; + transform: translateY(2px); + } + } + + .c2c-tooltip { + background: $dark; + color: $white; + padding: 2px 4px; + border-radius: 3px; + display: block; + visibility: hidden; + opacity: 0; + transition: visibility 0s, opacity 0.5s linear; + } + + .c2c-tooltip[show-tooltip] { + visibility: visible; + opacity: 1; + } } } diff --git a/layouts/partials/scripts.html b/layouts/partials/scripts.html index cbe15dc7cb..1ecc0c5f1b 100644 --- a/layouts/partials/scripts.html +++ b/layouts/partials/scripts.html @@ -48,7 +48,7 @@ {{ printf "onload='renderMathInElement(%s, %s);'" (( .Site.Params.katex.html_dom_element | default "document.body" ) | safeJS ) ( printf "%s" ( $.Site.Params.katex.options | jsonify )) | safeHTMLAttr }}> {{ end -}} -{{ $jsPopper := resources.GetRemote "https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" -}} +{{ $jsPopper := resources.GetRemote "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.9.2/umd/popper.min.js" -}} {{ $jsBs := resources.Get "vendor/bootstrap/dist/js/bootstrap.js" -}} {{ $jsBase := resources.Get "js/base.js" -}} {{ $jsAnchor := resources.Get "js/anchor.js" -}} @@ -70,6 +70,9 @@ {{ if .Site.Params.prism_syntax_highlighting -}} +{{ else -}} + {{ $c2cJS := resources.Get "js/click-to-copy.js" | minify | fingerprint }} + {{ end -}}