feat: add button to copy code blocks (#115)

This commit is contained in:
Robert Kaussow 2021-11-23 22:18:26 +01:00 committed by GitHub
parent 26a710b53c
commit c59f179d0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 118 additions and 11 deletions

View File

@ -54,6 +54,8 @@ This method can be used to include source code files and keep them automatically
{{</* include file="config.yaml" language="yaml" options="linenos=table,hl_lines=5-6,linenostart=100" */>}} {{</* include file="config.yaml" language="yaml" options="linenos=table,hl_lines=5-6,linenostart=100" */>}}
``` ```
**Code Include:**
<!-- prettier-ignore-start --> <!-- prettier-ignore-start -->
<!-- spellchecker-disable --> <!-- spellchecker-disable -->
{{< include file="config.yaml" language="yaml" options="linenos=table,hl_lines=5-6,linenostart=100">}} {{< include file="config.yaml" language="yaml" options="linenos=table,hl_lines=5-6,linenostart=100">}}
@ -77,9 +79,8 @@ HTML content will be filtered by the `safeHTML` filter and added to the rendered
In some situations, it can be helpful to include Markdown files that also contain shortcodes. While the [default method](#markdown-file-default) works fine to render plain Markdown, shortcodes are not parsed. The only way to get this to work is to use Hugo pages. There are several ways to structure these include pages, so whatever you do, keep in mind that Hugo needs to be able to render and serve these files as regular pages! How it works: In some situations, it can be helpful to include Markdown files that also contain shortcodes. While the [default method](#markdown-file-default) works fine to render plain Markdown, shortcodes are not parsed. The only way to get this to work is to use Hugo pages. There are several ways to structure these include pages, so whatever you do, keep in mind that Hugo needs to be able to render and serve these files as regular pages! How it works:
1. First you need to create a directory **within** your content directory. For this example site `_includes` is used. 1. First you need to create a directory **within** your content directory. For this example site `_includes` is used.
2. To prevent the theme from embedding the page in the navigation, create a file `_includes/_index.md` and add `GeekdocHidden: true` to the front matter. 2. Place your Markdown files within the `_includes` folder e.g. `/_includes/include-page.md`. Make sure to name it `*.md`.
3. Place your Markdown files within the `_includes` folder e.g. `/_includes/include-page.md`. Make sure to name it `*.md`. 3. Include the page using `{{</* include file="/_includes/include-page.md" */>}}`.
4. Include the page using `{{</* include file="/_includes/include-page.md" */>}}`.
Resulting structure should look like this: Resulting structure should look like this:

View File

@ -16,7 +16,7 @@ There are several ways to add code blocks. Most of them work out of the box, onl
To display an inline shortcode use single quotes: To display an inline shortcode use single quotes:
```plain ```Plain
`some code` `some code`
``` ```
@ -26,7 +26,7 @@ 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 ```Plain
some code some code
``` ```
@ -40,7 +40,7 @@ some code
... or if you need language specific syntax highlighting: ... or if you need language specific syntax highlighting:
````markdown ````Markdown
```Shell ```Shell
# some code # some code
echo "Hello world" echo "Hello world"
@ -80,7 +80,7 @@ pygmentsCodeFences: true
You can use it like every other shortcode: You can use it like every other shortcode:
<!-- prettier-ignore --> <!-- prettier-ignore -->
```markdown ```Markdown
{{</* highlight Shell "linenos=table" */>}} {{</* highlight Shell "linenos=table" */>}}
# some code # some code
echo "Hello World" echo "Hello World"

View File

@ -2,3 +2,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>

View File

@ -43,7 +43,7 @@
</section> </section>
{{ end }} {{ end }}
</div> </div>
{{ if (default true .Site.Params.GeekdocBackToTop) }} {{ if (default true .Site.Params.GeekblogBackToTop) }}
<div class="flex flex-25 justify-end"> <div class="flex flex-25 justify-end">
<span class="gblog-footer__item text-right"> <span class="gblog-footer__item text-right">
<a class="gblog-footer__link fake-link" href="#" aria-label="Back to top"> <a class="gblog-footer__link fake-link" href="#" aria-label="Back to top">

View File

@ -5,7 +5,7 @@
<a class="gblog-header__link" rel="me" href="{{ .Site.BaseURL }}"> <a class="gblog-header__link" rel="me" href="{{ .Site.BaseURL }}">
<span class="gblog-brand flex align-center justify-center"> <span class="gblog-brand flex align-center justify-center">
<img class="gblog-brand__img" src="{{ (default "brand.svg" .Site.Params.logo) | relURL }}" alt=""> <img class="gblog-brand__img" src="{{ (default "brand.svg" .Site.Params.logo) | relURL }}" alt="">
<span class="gdoc-brand__title">{{ .Site.Title }}</span> <span class="gblog-brand__title">{{ .Site.Title }}</span>
</span> </span>
{{ with .Site.Params.subtitle }} {{ with .Site.Params.subtitle }}
<span class="gblog-brand__subtitle flex align-center justify-center">{{ . }}</span> <span class="gblog-brand__subtitle flex align-center justify-center">{{ . }}</span>

View File

@ -4,7 +4,7 @@
{{ $language := .Get "language" }} {{ $language := .Get "language" }}
{{ $options :=.Get "options" }} {{ $options :=.Get "options" }}
<div class="gdoc-include"> <div class="gblog-include">
{{- if (.Get "language") -}} {{- if (.Get "language") -}}
{{- highlight ($file | readFile) $language (default "linenos=table" $options) -}} {{- highlight ($file | readFile) $language (default "linenos=table" $options) -}}
{{- else if eq $type "html" -}} {{- else if eq $type "html" -}}

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="-7.27 -7.27 42.55 42.55"><path d="M8.885 20.197L25.759 3.323l2.24 2.24L8.885 24.677 0 15.792l2.24-2.24z"/></svg>

After

Width:  |  Height:  |  Size: 184 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="-7.27 -7.27 42.55 42.55"><path d="M23.502 25.438V7.626H9.562v17.812h13.94zm0-20.315q1.013 0 1.787.745t.774 1.757v17.812q0 1.013-.774 1.787t-1.787.774H9.562q-1.013 0-1.787-.774t-.774-1.787V7.625q0-1.013.774-1.757t1.787-.745h13.94zM19.689 0v2.562H4.438v17.812H1.936V2.562q0-1.013.745-1.787T4.438.001h15.251z"/></svg>

After

Width:  |  Height:  |  Size: 386 B

5
src/icons/check.svg Normal file
View 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
View 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

View File

@ -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("gblog-post__codecopy--success");
trigger.querySelector(".icon.copy").classList.add("hidden");
trigger.querySelector(".icon.check").classList.remove("hidden");
setTimeout(function () {
trigger.classList.remove("gblog-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
View 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",
"gblog-post__codecopy"
);
button.type = "button";
button.innerHTML =
'<svg class="icon copy"><use xlink:href="#gblog_copy"></use></svg>' +
'<svg class="icon check hidden"><use xlink:href="#gblog_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("gblog-post__codecontainer");
highlightDiv.insertBefore(button, highlightDiv.firstChild);
}
document
.querySelectorAll(".highlight")
.forEach((highlightDiv) => createCopyButton(highlightDiv));

View File

@ -441,6 +441,46 @@ img {
font-size: 1.2em; font-size: 1.2em;
} }
} }
&__codecontainer {
position: relative;
&:hover > .gblog-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;
background: var(--code-background);
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);
}
}
}
} }
.gblog-footer { .gblog-footer {

View File

@ -66,7 +66,7 @@
} }
.hidden { .hidden {
display: none; display: none !important;
} }
.svg-sprite { .svg-sprite {