Skip to content

Commit ce3f34b

Browse files
simonebortolinmattxwang
andauthoredDec 27, 2022
Add copy code button to code snippets (mmistakes#945)
Hello everyone, this is my implementation for the copy button on the snippet (requested in mmistakes#924) The implementation is made 100% javascript as with or without a jekyll template modification you still have to execute some javascript code, and I consider it the best choice. the button only appears if the mouse is over it, to allow the entire line to be read the important CSS changes were made to make the copy button work even in the long code situation: ![image](https://user-images.githubusercontent.com/26844016/187731472-d4bf7828-2356-4d94-9c2d-9db863228f5f.png) to avoid this: ![image](https://user-images.githubusercontent.com/26844016/183292313-d7f73d7d-58c0-4c7b-b5ba-e08bd285514b.png) Co-authored-by: Matt Wang <[email protected]>
1 parent 56bda83 commit ce3f34b

File tree

6 files changed

+154
-25
lines changed

6 files changed

+154
-25
lines changed
 

‎_config.yml

+3
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ search:
7373
# Supports true or false (default)
7474
button: false
7575

76+
# For copy button on code
77+
enable_copy_code_button: true
78+
7679
# To disable support for mermaid diagrams (https://mermaid-js.github.io/mermaid/),
7780
# comment out the `mermaid` and `version` keys below
7881
# By default, consuming the theme as a gem leaves mermaid disabled; it is opt-in

‎_includes/icons/code_copy.html

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!-- Feather. MIT License: https://github.com/twbs/icons/blob/main/LICENSE.md -->
2+
<symbol id="svg-copy" viewBox="0 0 16 16">
3+
<title>Copy</title>
4+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard" viewBox="0 0 16 16">
5+
<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"/>
6+
<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"/>
7+
</svg>
8+
</symbol>
9+
<symbol id="svg-copied" viewBox="0 0 16 16">
10+
<title>Copied</title>
11+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard-check-fill" viewBox="0 0 16 16">
12+
<path d="M6.5 0A1.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-3Zm3 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-.5h3Z"/>
13+
<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-1v1A2.5 2.5 0 0 1 9.5 5h-3A2.5 2.5 0 0 1 4 2.5v-1Zm6.854 7.354-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 0 1 .708-.708L7.5 10.793l2.646-2.647a.5.5 0 0 1 .708.708Z"/>
14+
</svg>
15+
</symbol>

‎_layouts/default.html

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
</svg>
4141
</symbol>
4242
{% include icons/external_link.html %}
43+
{% include icons/code_copy.html %}
4344
</svg>
4445

4546
<div class="side-bar">

‎_sass/code.scss

+83-25
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33

44
// {% raw %}
55

6-
code {
7-
padding: 0.2em 0.15em;
8-
font-weight: 400;
9-
background-color: $code-background-color;
10-
border: $border $border-color;
11-
border-radius: $border-radius;
6+
// This instruction applies to all queues not within 'pre', avoiding 'code' generated by the highlight.
7+
:not(pre) {
8+
& > code {
9+
padding: 0.2em 0.15em;
10+
font-weight: 400;
11+
background-color: $code-background-color;
12+
border: $border $border-color;
13+
border-radius: $border-radius;
14+
}
1215
}
1316

1417
// Avoid appearance of dark border around visited code links in Safari
@@ -47,18 +50,77 @@ a:visited code {
4750
// Kramdown line_numbers = true: fences have a wider gutter than with Liquid?
4851

4952
// ```[LANG]...```
53+
54+
// the code may appear with 3 different types:
55+
// container \ case: default case, code with line number, code with html rendering
56+
// top level: div.highlighter-rouge, div.listingblock > div.content, figure.highlight
57+
// second level: div.highlight, .table-wrapper, pre
58+
// third level: pre.highlight, td.code, absent
59+
// last level: code, pre, code (optionality)
60+
// highlighter level: span, span, span
61+
// the spacing are only in the second level for case 1, 3 and in the thirt level for case 2
62+
63+
// select top level container
5064
div.highlighter-rouge,
51-
div.listingblock > div.content {
52-
padding: $sp-3;
65+
div.listingblock > div.content,
66+
figure.highlight {
5367
margin-top: 0;
5468
margin-bottom: $sp-3;
55-
overflow-x: auto;
5669
background-color: $code-background-color;
5770
border-radius: $border-radius;
5871
box-shadow: none;
5972
-webkit-overflow-scrolling: touch;
73+
position: relative;
74+
padding: 0;
75+
76+
// copy button (or other button)
77+
// the button appear only when there is a hover on the code or focus on button
78+
> button {
79+
width: $sp-3;
80+
opacity: 0;
81+
position: absolute;
82+
top: 0;
83+
right: 0;
84+
border: $sp-3 solid $code-background-color;
85+
background-color: $code-background-color;
86+
color: $body-text-color;
87+
box-sizing: content-box;
88+
89+
svg {
90+
fill: $body-text-color;
91+
}
92+
93+
&:active {
94+
text-decoration: none;
95+
outline: none;
96+
opacity: 1;
97+
}
98+
99+
&:focus {
100+
opacity: 1;
101+
}
102+
}
103+
104+
// the button can be seen by doing a simple hover in the code, there is no need to go over the location of the button
105+
&:hover {
106+
> button {
107+
cursor: copy;
108+
opacity: 1;
109+
}
110+
}
111+
}
112+
113+
// setting the spacing and scrollbar on the second level for the first case
114+
// remove all space on the second and thirt level
115+
div.highlighter-rouge,
116+
div.listingblock {
117+
div.highlight {
118+
overflow-x: auto;
119+
padding: $sp-3;
120+
margin: 0;
121+
border: 0;
122+
}
60123

61-
div.highlight,
62124
pre.highlight,
63125
code {
64126
padding: 0;
@@ -69,26 +131,24 @@ div.listingblock > div.content {
69131

70132
// {% highlight LANG %}...{% endhighlight %},
71133
// {% highlight LANG linenos %}...{% endhighlight %}:
72-
figure.highlight {
73-
padding: $sp-3;
74-
margin-top: 0;
75-
margin-bottom: $sp-3;
76-
overflow-x: auto;
77-
background-color: $code-background-color;
78-
border-radius: $border-radius;
79-
box-shadow: none;
80-
-webkit-overflow-scrolling: touch;
81134

135+
// setting the spacing and scrollbar on the second level for the thirt case
136+
// the css rule are apply only to the last code enviroment
137+
// setting the scroolbar
138+
figure.highlight {
82139
pre,
83-
code {
84-
padding: 0;
140+
:not(pre) > code {
141+
overflow-x: auto;
142+
padding: $sp-3;
85143
margin: 0;
86144
border: 0;
87145
}
88146
}
89147

90148
// ```[LANG]...```, kramdown line_numbers = true,
91149
// {% highlight LANG linenos %}...{% endhighlight %}:
150+
151+
// setting the spacing and scrollbar on the thirt level for the second case
92152
.highlight .table-wrapper {
93153
padding: 0;
94154
margin: 0;
@@ -108,6 +168,7 @@ figure.highlight {
108168
td.gl {
109169
width: 1em;
110170
padding-right: $sp-3;
171+
padding-left: $sp-3;
111172
}
112173

113174
pre {
@@ -116,10 +177,7 @@ figure.highlight {
116177
}
117178
}
118179

119-
//
120-
// Code examples (rendered)
121-
//
122-
180+
// Code examples: html render of a code
123181
.code-example,
124182
.listingblock > .title {
125183
padding: $sp-3;

‎assets/js/just-the-docs.js

+41
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,47 @@ jtd.onReady(function(){
479479
scrollNav();
480480
});
481481

482+
// Copy button on code
483+
484+
485+
{%- if site.enable_copy_code_button != false %}
486+
487+
jtd.onReady(function(){
488+
489+
var codeBlocks = document.querySelectorAll('div.highlighter-rouge, div.listingblock, figure.highlight');
490+
491+
var svgCopied = '<svg viewBox="0 0 24 24" class="copy-icon"><use xlink:href="#svg-copied"></use></svg>';
492+
var svgCopy = '<svg viewBox="0 0 24 24" class="copy-icon"><use xlink:href="#svg-copy"></use></svg>';
493+
494+
codeBlocks.forEach(codeBlock => {
495+
var copyButton = document.createElement('button');
496+
var timeout = null;
497+
copyButton.type = 'button';
498+
copyButton.ariaLabel = 'Copy code to clipboard';
499+
copyButton.innerHTML = svgCopy;
500+
codeBlock.append(copyButton);
501+
502+
copyButton.addEventListener('click', function () {
503+
if(timeout === null) {
504+
var code = codeBlock.querySelector('code').innerText.trim();
505+
window.navigator.clipboard.writeText(code);
506+
507+
copyButton.innerHTML = svgCopied;
508+
509+
var timeoutSetting = 4000;
510+
511+
timeout = setTimeout(function () {
512+
copyButton.innerHTML = svgCopy;
513+
timeout = null;
514+
}, timeoutSetting);
515+
}
516+
});
517+
});
518+
519+
});
520+
521+
{%- endif %}
522+
482523
})(window.jtd = window.jtd || {});
483524

484525
{% include js/custom.js %}

‎docs/ui-components/code.md

+11
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,14 @@ graph TD;
143143
```
144144

145145
*Note: for demonstration purposes, we've enabled mermaid on this site. It is still disabled by default, and users need to opt-in to use it.*
146+
147+
## Copy button
148+
149+
The copy button for code blocks can be enabled or disabled via the `enable_copy_code_button` key in `_config.yml`. By default, the value of this key is `false`; users need to opt-in.
150+
151+
```yaml
152+
# For copy button on code
153+
enable_copy_code_button: true
154+
```
155+
156+
Note that this feature requires JavaScript; if JavaScript is disabled in the browser, this feature will not work.

0 commit comments

Comments
 (0)
Please sign in to comment.