feat: add button to copy code blocks (#228)
BREAKING CHANGE: The `--code-max-height` formatting is applied only to code blocks that use syntax highlighting, see [documentation](https://geekdocs.de/features/code-blocks/).
This commit is contained in:
parent
d1267ac587
commit
75f56d8fad
13 changed files with 138 additions and 7 deletions
|
@ -2,7 +2,7 @@
|
||||||
title: Code Blocks
|
title: Code Blocks
|
||||||
---
|
---
|
||||||
|
|
||||||
There are multiple ways to add code blocks. Most of them works out of the box only the Hugo shortcode `<highlight>` need some configuration to work properly.
|
There are several ways to add code blocks. Most of them work out of the box, only the Hugo short code `<highlight>` needs to be configured to work properly. The theme also provides some additional features like a copy button and an option to set the maximum length of code blocks. Both of these functions and the dependent formatting rely on the `.highlight` CSS class. You must ensure that you always assign a language to your code blocks if you want to use these functions. If you do not want to apply syntax highlighting, you can also specify `plain` or `text` as the language.
|
||||||
|
|
||||||
{{< toc >}}
|
{{< toc >}}
|
||||||
|
|
||||||
|
@ -21,14 +21,14 @@ To display an inline shortcode use single quotes:
|
||||||
Code blocks can be uses without language specification:
|
Code blocks can be uses without language specification:
|
||||||
|
|
||||||
````markdown
|
````markdown
|
||||||
```
|
```Plain
|
||||||
some code
|
some code
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
**Example:**
|
**Example:**
|
||||||
|
|
||||||
```plain
|
```Plain
|
||||||
some code
|
some code
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,10 @@
|
||||||
--code-accent-color: #e3e7eb;
|
--code-accent-color: #e3e7eb;
|
||||||
--code-accent-color-lite: #eff1f3;
|
--code-accent-color-lite: #eff1f3;
|
||||||
|
|
||||||
|
--code-copy-font-color: #6b7784;
|
||||||
|
--code-copy-border-color: #adb4bc;
|
||||||
|
--code-copy-success-color: #00c853;
|
||||||
|
|
||||||
--accent-color: #e9ecef;
|
--accent-color: #e9ecef;
|
||||||
--accent-color-lite: #f8f9fa;
|
--accent-color-lite: #f8f9fa;
|
||||||
|
|
||||||
|
@ -51,6 +55,10 @@
|
||||||
--code-accent-color: #e3e7eb;
|
--code-accent-color: #e3e7eb;
|
||||||
--code-accent-color-lite: #eff1f3;
|
--code-accent-color-lite: #eff1f3;
|
||||||
|
|
||||||
|
--code-copy-font-color: #6b7784;
|
||||||
|
--code-copy-border-color: #adb4bc;
|
||||||
|
--code-copy-success-color: #00c853;
|
||||||
|
|
||||||
--accent-color: #e9ecef;
|
--accent-color: #e9ecef;
|
||||||
--accent-color-lite: #f8f9fa;
|
--accent-color-lite: #f8f9fa;
|
||||||
|
|
||||||
|
@ -81,6 +89,10 @@
|
||||||
--code-accent-color: #262b2f;
|
--code-accent-color: #262b2f;
|
||||||
--code-accent-color-lite: #2b3035;
|
--code-accent-color-lite: #2b3035;
|
||||||
|
|
||||||
|
--code-copy-font-color: #adb4bc;
|
||||||
|
--code-copy-border-color: #808c98;
|
||||||
|
--code-copy-success-color: #00c853;
|
||||||
|
|
||||||
--accent-color: #2b3035;
|
--accent-color: #2b3035;
|
||||||
--accent-color-lite: #2f353a;
|
--accent-color-lite: #2f353a;
|
||||||
|
|
||||||
|
@ -109,6 +121,10 @@
|
||||||
--code-accent-color: #262b2f;
|
--code-accent-color: #262b2f;
|
||||||
--code-accent-color-lite: #2b3035;
|
--code-accent-color-lite: #2b3035;
|
||||||
|
|
||||||
|
--code-copy-font-color: #adb4bc;
|
||||||
|
--code-copy-border-color: #808c98;
|
||||||
|
--code-copy-success-color: #00c853;
|
||||||
|
|
||||||
--accent-color: #2b3035;
|
--accent-color: #2b3035;
|
||||||
--accent-color-lite: #2f353a;
|
--accent-color-lite: #2f353a;
|
||||||
|
|
||||||
|
|
|
@ -10,3 +10,5 @@
|
||||||
<script defer src="{{ index .Site.Data.assets "js/clipboard.min.js" | relURL }}"></script>
|
<script defer src="{{ index .Site.Data.assets "js/clipboard.min.js" | relURL }}"></script>
|
||||||
<script defer src="{{ index .Site.Data.assets "js/clipboard-loader.min.js" | relURL }}"></script>
|
<script defer src="{{ index .Site.Data.assets "js/clipboard-loader.min.js" | relURL }}"></script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
<script defer src="{{ index .Site.Data.assets "js/copycode.min.js" | relURL }}"></script>
|
||||||
|
|
5
src/icons/check.svg
Normal file
5
src/icons/check.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<!-- Generated by IcoMoon.io -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 28 28">
|
||||||
|
<title>check</title>
|
||||||
|
<path d="M8.885 20.197l16.874-16.874 2.24 2.24-19.114 19.114-8.885-8.885 2.24-2.24z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 252 B |
5
src/icons/copy.svg
Normal file
5
src/icons/copy.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<!-- Generated by IcoMoon.io -->
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 28 28">
|
||||||
|
<title>copy</title>
|
||||||
|
<path d="M23.502 25.438v-17.812h-13.94v17.812h13.94zM23.502 5.123q1.013 0 1.787 0.745t0.774 1.757v17.812q0 1.013-0.774 1.787t-1.787 0.774h-13.94q-1.013 0-1.787-0.774t-0.774-1.787v-17.812q0-1.013 0.774-1.757t1.787-0.745h13.94zM19.689 0v2.562h-15.251v17.812h-2.502v-17.812q0-1.013 0.745-1.787t1.757-0.774h15.251z"></path>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 478 B |
|
@ -1,3 +1,21 @@
|
||||||
document.addEventListener("DOMContentLoaded", function (event) {
|
document.addEventListener("DOMContentLoaded", function (event) {
|
||||||
var clipboard = new ClipboardJS(".clip");
|
var clipboard = new ClipboardJS(".clip");
|
||||||
|
|
||||||
|
clipboard.on("success", function (e) {
|
||||||
|
const trigger = e.trigger;
|
||||||
|
|
||||||
|
if (trigger.hasAttribute("data-copy-feedback")) {
|
||||||
|
trigger.classList.add("gdoc-post__codecopy--success");
|
||||||
|
trigger.querySelector(".icon.copy").classList.add("hidden");
|
||||||
|
trigger.querySelector(".icon.check").classList.remove("hidden");
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
trigger.classList.remove("gdoc-post__codecopy--success");
|
||||||
|
trigger.querySelector(".icon.copy").classList.remove("hidden");
|
||||||
|
trigger.querySelector(".icon.check").classList.add("hidden");
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
e.clearSelection();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
34
src/js/copycode.js
Normal file
34
src/js/copycode.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
function createCopyButton(highlightDiv) {
|
||||||
|
const button = document.createElement("span");
|
||||||
|
|
||||||
|
if (highlightDiv.querySelector(".lntable")) {
|
||||||
|
selector = ".lntable .lntd:last-child pre > code";
|
||||||
|
} else {
|
||||||
|
selector = "pre > code";
|
||||||
|
}
|
||||||
|
|
||||||
|
const codeToCopy = highlightDiv.querySelector(selector).innerText.trim();
|
||||||
|
|
||||||
|
button.classList.add(
|
||||||
|
"flex",
|
||||||
|
"align-center",
|
||||||
|
"justify-center",
|
||||||
|
"clip",
|
||||||
|
"gdoc-post__codecopy"
|
||||||
|
);
|
||||||
|
button.type = "button";
|
||||||
|
button.innerHTML =
|
||||||
|
'<svg class="icon copy"><use xlink:href="#gdoc_copy"></use></svg>' +
|
||||||
|
'<svg class="icon check hidden"><use xlink:href="#gdoc_check"></use></svg>';
|
||||||
|
button.setAttribute("data-clipboard-text", codeToCopy);
|
||||||
|
button.setAttribute("data-copy-feedback", "Copied!");
|
||||||
|
button.setAttribute("role", "button");
|
||||||
|
button.setAttribute("aria-label", "Copy");
|
||||||
|
|
||||||
|
highlightDiv.classList.add("gdoc-post__codecontainer");
|
||||||
|
highlightDiv.insertBefore(button, highlightDiv.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelectorAll(".highlight")
|
||||||
|
.forEach((highlightDiv) => createCopyButton(highlightDiv));
|
|
@ -488,6 +488,45 @@ img {
|
||||||
font-size: $font-size-20;
|
font-size: $font-size-20;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__codecontainer {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover > .gdoc-post__codecopy {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__codecopy {
|
||||||
|
visibility: hidden;
|
||||||
|
position: absolute;
|
||||||
|
top: 0.5rem;
|
||||||
|
right: 0.5rem;
|
||||||
|
|
||||||
|
border: $border-2 solid var(--code-copy-border-color);
|
||||||
|
border-radius: $border-radius;
|
||||||
|
width: 2.2rem;
|
||||||
|
height: 2.2rem;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
top: 0;
|
||||||
|
width: $font-size-20;
|
||||||
|
height: $font-size-20;
|
||||||
|
color: var(--code-copy-font-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--success {
|
||||||
|
border-color: var(--code-copy-success-color);
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
color: var(--code-copy-success-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.gdoc-footer {
|
.gdoc-footer {
|
||||||
|
|
|
@ -35,6 +35,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.highlight {
|
.highlight {
|
||||||
|
overflow: auto;
|
||||||
|
max-height: var(--code-max-height);
|
||||||
|
|
||||||
pre.chroma {
|
pre.chroma {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
--code-accent-color-lite: #{darken($code-background, 2)};
|
--code-accent-color-lite: #{darken($code-background, 2)};
|
||||||
--code-font-color: #{$code-font-color};
|
--code-font-color: #{$code-font-color};
|
||||||
|
|
||||||
|
--code-copy-font-color: #{lighten($body-font-color, 24)};
|
||||||
|
--code-copy-border-color: #{lighten($body-font-color, 48)};
|
||||||
|
--code-copy-success-color: #{map.get($hint-colors, "ok")};
|
||||||
|
|
||||||
--accent-color: #{$gray-200};
|
--accent-color: #{$gray-200};
|
||||||
--accent-color-lite: #{$gray-100};
|
--accent-color-lite: #{$gray-100};
|
||||||
|
|
||||||
|
@ -61,6 +65,10 @@
|
||||||
--code-accent-color-lite: #{darken($code-background-dark, 2)};
|
--code-accent-color-lite: #{darken($code-background-dark, 2)};
|
||||||
--code-font-color: #{$code-font-color-dark};
|
--code-font-color: #{$code-font-color-dark};
|
||||||
|
|
||||||
|
--code-copy-font-color: #{lighten($body-font-color, 48)};
|
||||||
|
--code-copy-border-color: #{lighten($body-font-color, 32)};
|
||||||
|
--code-copy-success-color: #{map.get($hint-colors, "ok")};
|
||||||
|
|
||||||
--accent-color: #{darken($body-background-dark, 4)};
|
--accent-color: #{darken($body-background-dark, 4)};
|
||||||
--accent-color-lite: #{darken($body-background-dark, 2)};
|
--accent-color-lite: #{darken($body-background-dark, 2)};
|
||||||
|
|
||||||
|
@ -78,7 +86,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.gdoc-markdown {
|
.gdoc-markdown {
|
||||||
.gdoc-hint {
|
.gdoc-hint,
|
||||||
|
.gdoc-post__codecopy--success {
|
||||||
filter: saturate(2.5) brightness(0.85);
|
filter: saturate(2.5) brightness(0.85);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,8 +180,6 @@
|
||||||
display: block;
|
display: block;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: auto;
|
|
||||||
max-height: var(--code-max-height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__align {
|
&__align {
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.svg-sprite {
|
.svg-sprite {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
@use "sass:map";
|
||||||
|
|
||||||
@import "_defaults";
|
@import "_defaults";
|
||||||
@import "_color_mode";
|
@import "_color_mode";
|
||||||
@import "_chroma_base";
|
@import "_chroma_base";
|
||||||
|
|
Loading…
Reference in a new issue