Skip to content

Commit

Permalink
Add copy code button to code snippets (mmistakes#945)
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
simonebortolin and mattxwang authored Dec 27, 2022
1 parent 56bda83 commit ce3f34b
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 25 deletions.
3 changes: 3 additions & 0 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ search:
# Supports true or false (default)
button: false

# For copy button on code
enable_copy_code_button: true

# To disable support for mermaid diagrams (https://mermaid-js.github.io/mermaid/),
# comment out the `mermaid` and `version` keys below
# By default, consuming the theme as a gem leaves mermaid disabled; it is opt-in
Expand Down
15 changes: 15 additions & 0 deletions _includes/icons/code_copy.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!-- Feather. MIT License: https://github.com/twbs/icons/blob/main/LICENSE.md -->
<symbol id="svg-copy" viewBox="0 0 16 16">
<title>Copy</title>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-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"/>
</svg>
</symbol>
<symbol id="svg-copied" viewBox="0 0 16 16">
<title>Copied</title>
<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">
<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"/>
<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"/>
</svg>
</symbol>
1 change: 1 addition & 0 deletions _layouts/default.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
</svg>
</symbol>
{% include icons/external_link.html %}
{% include icons/code_copy.html %}
</svg>

<div class="side-bar">
Expand Down
108 changes: 83 additions & 25 deletions _sass/code.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@

// {% raw %}

code {
padding: 0.2em 0.15em;
font-weight: 400;
background-color: $code-background-color;
border: $border $border-color;
border-radius: $border-radius;
// This instruction applies to all queues not within 'pre', avoiding 'code' generated by the highlight.
:not(pre) {
& > code {
padding: 0.2em 0.15em;
font-weight: 400;
background-color: $code-background-color;
border: $border $border-color;
border-radius: $border-radius;
}
}

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

// ```[LANG]...```

// the code may appear with 3 different types:
// container \ case: default case, code with line number, code with html rendering
// top level: div.highlighter-rouge, div.listingblock > div.content, figure.highlight
// second level: div.highlight, .table-wrapper, pre
// third level: pre.highlight, td.code, absent
// last level: code, pre, code (optionality)
// highlighter level: span, span, span
// the spacing are only in the second level for case 1, 3 and in the thirt level for case 2

// select top level container
div.highlighter-rouge,
div.listingblock > div.content {
padding: $sp-3;
div.listingblock > div.content,
figure.highlight {
margin-top: 0;
margin-bottom: $sp-3;
overflow-x: auto;
background-color: $code-background-color;
border-radius: $border-radius;
box-shadow: none;
-webkit-overflow-scrolling: touch;
position: relative;
padding: 0;

// copy button (or other button)
// the button appear only when there is a hover on the code or focus on button
> button {
width: $sp-3;
opacity: 0;
position: absolute;
top: 0;
right: 0;
border: $sp-3 solid $code-background-color;
background-color: $code-background-color;
color: $body-text-color;
box-sizing: content-box;

svg {
fill: $body-text-color;
}

&:active {
text-decoration: none;
outline: none;
opacity: 1;
}

&:focus {
opacity: 1;
}
}

// 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
&:hover {
> button {
cursor: copy;
opacity: 1;
}
}
}

// setting the spacing and scrollbar on the second level for the first case
// remove all space on the second and thirt level
div.highlighter-rouge,
div.listingblock {
div.highlight {
overflow-x: auto;
padding: $sp-3;
margin: 0;
border: 0;
}

div.highlight,
pre.highlight,
code {
padding: 0;
Expand All @@ -69,26 +131,24 @@ div.listingblock > div.content {

// {% highlight LANG %}...{% endhighlight %},
// {% highlight LANG linenos %}...{% endhighlight %}:
figure.highlight {
padding: $sp-3;
margin-top: 0;
margin-bottom: $sp-3;
overflow-x: auto;
background-color: $code-background-color;
border-radius: $border-radius;
box-shadow: none;
-webkit-overflow-scrolling: touch;

// setting the spacing and scrollbar on the second level for the thirt case
// the css rule are apply only to the last code enviroment
// setting the scroolbar
figure.highlight {
pre,
code {
padding: 0;
:not(pre) > code {
overflow-x: auto;
padding: $sp-3;
margin: 0;
border: 0;
}
}

// ```[LANG]...```, kramdown line_numbers = true,
// {% highlight LANG linenos %}...{% endhighlight %}:

// setting the spacing and scrollbar on the thirt level for the second case
.highlight .table-wrapper {
padding: 0;
margin: 0;
Expand All @@ -108,6 +168,7 @@ figure.highlight {
td.gl {
width: 1em;
padding-right: $sp-3;
padding-left: $sp-3;
}

pre {
Expand All @@ -116,10 +177,7 @@ figure.highlight {
}
}

//
// Code examples (rendered)
//

// Code examples: html render of a code
.code-example,
.listingblock > .title {
padding: $sp-3;
Expand Down
41 changes: 41 additions & 0 deletions assets/js/just-the-docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,47 @@ jtd.onReady(function(){
scrollNav();
});

// Copy button on code


{%- if site.enable_copy_code_button != false %}

jtd.onReady(function(){

var codeBlocks = document.querySelectorAll('div.highlighter-rouge, div.listingblock, figure.highlight');

var svgCopied = '<svg viewBox="0 0 24 24" class="copy-icon"><use xlink:href="#svg-copied"></use></svg>';
var svgCopy = '<svg viewBox="0 0 24 24" class="copy-icon"><use xlink:href="#svg-copy"></use></svg>';

codeBlocks.forEach(codeBlock => {
var copyButton = document.createElement('button');
var timeout = null;
copyButton.type = 'button';
copyButton.ariaLabel = 'Copy code to clipboard';
copyButton.innerHTML = svgCopy;
codeBlock.append(copyButton);

copyButton.addEventListener('click', function () {
if(timeout === null) {
var code = codeBlock.querySelector('code').innerText.trim();
window.navigator.clipboard.writeText(code);

copyButton.innerHTML = svgCopied;

var timeoutSetting = 4000;

timeout = setTimeout(function () {
copyButton.innerHTML = svgCopy;
timeout = null;
}, timeoutSetting);
}
});
});

});

{%- endif %}

})(window.jtd = window.jtd || {});

{% include js/custom.js %}
11 changes: 11 additions & 0 deletions docs/ui-components/code.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,14 @@ graph TD;
```

*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.*

## Copy button

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.

```yaml
# For copy button on code
enable_copy_code_button: true
```

Note that this feature requires JavaScript; if JavaScript is disabled in the browser, this feature will not work.

0 comments on commit ce3f34b

Please sign in to comment.