Implemented copy code button feature (#1267)

Implemented support for copy code button in code blocks (#1262), also updated blog post about code to reflect it.
This commit is contained in:
George 2023-03-19 18:17:34 -03:00 committed by GitHub
parent fa9e1c30c1
commit bacddc7821
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 286 additions and 87 deletions

View File

@ -12,3 +12,4 @@
<!-- Load Common JS -->
<script defer src="{{ '/assets/js/common.js' | relative_url }}"></script>
<script defer src="{{ '/assets/js/copy_code.js' | relative_url }}" type="text/javascript"></script>

View File

@ -9,7 +9,37 @@ categories: sample-posts
This theme implements a built-in Jekyll feature, the use of Rouge, for syntax highlighting.
It supports more than 100 languages.
This example is in C++.
All you have to do is wrap your code in a liquid tag:
All you have to do is wrap your code in markdown code tags:
````markdown
```c++
code code code
```
````
```c++
int main(int argc, char const \*argv[])
{
string myString;
cout << "input a string: ";
getline(cin, myString);
int length = myString.length();
char charArray = new char * [length];
charArray = myString;
for(int i = 0; i < length; ++i){
cout << charArray[i] << " ";
}
return 0;
}
```
By default, it does not display line numbers. If you want to display line numbers for every code block, you can set `kramdown.syntax_highlighter_opts.block.line_numbers` to true in your `_config.yml` file.
If you want to display line numbers for a specific code block, all you have to do is wrap your code in a liquid tag:
{% raw %}
{% highlight c++ linenos %} <br/> code code code <br/> {% endhighlight %}

View File

@ -4,7 +4,18 @@
// Typography
p, h1, h2, h3, h4, h5, h6, em, div, li, span, strong {
p,
h1,
h2,
h3,
h4,
h5,
h6,
em,
div,
li,
span,
strong {
color: var(--global-text-color);
}
@ -13,26 +24,33 @@ hr {
}
table {
td, th {
td,
th {
color: var(--global-text-color);
}
td {
font-size: 1rem;
}
}
a, table.table a {
a,
table.table a {
color: var(--global-theme-color);
&:hover {
color: var(--global-theme-color);
text-decoration: underline;
}
&:hover:after :not(.nav-item.dropdown) {
width: 100%;
}
}
figure, img {
figure,
img {
max-width: 90vw;
}
@ -86,7 +104,8 @@ blockquote {
// Citation
.citation, .citation-number {
.citation,
.citation-number {
color: var(--global-theme-color);
}
@ -99,24 +118,30 @@ blockquote {
margin-bottom: 5px;
margin-top: 5px;
font-family: monospace;
p {
display: inline-block;
margin: 0;
}
}
}
.profile.float-right{
.profile.float-right {
margin-left: 1rem;
}
.profile.float-left{
.profile.float-left {
margin-right: 1rem;
}
@media (min-width: 576px) {
.profile {
width: 30%;
.address {
p { display: block; }
p {
display: block;
}
}
}
}
@ -124,8 +149,10 @@ blockquote {
.post-description {
margin-bottom: 2rem;
font-size: 0.875rem;
a {
color: inherit;
&:hover {
color: var(--global-theme-color);
text-decoration: none;
@ -142,58 +169,73 @@ blockquote {
background-color: var(--global-bg-color);
opacity: 0.95;
}
.navbar .dropdown-menu {
background-color: var(--global-bg-color);
border: 1px solid var(--global-divider-color);
a:not(.active) {
color: var(--global-text-color);
}
a:hover {
color: var(--global-hover-color);
}
.dropdown-divider {
border-top: 1px solid var(--global-divider-color) !important;
}
}
.dropdown-item {
color: var(--global-text-color);
&:hover {
color: var(--global-hover-color);
background-color: var(--global-bg-color);
}
&:hover {
color: var(--global-hover-color);
background-color: var(--global-bg-color);
}
}
.navbar.navbar-light {
a {
&:hover {
text-decoration: none;
}
}
.navbar-brand {
color: var(--global-text-color);
}
.navbar-nav .nav-item .nav-link {
color: var(--global-text-color);
&:hover {
color: var(--global-hover-color);
}
}
.navbar-nav .nav-item.active>.nav-link {
background-color: inherit;
font-weight: bolder;
color: var(--global-theme-color);
&:hover {
color: var(--global-hover-color);
}
background-color: inherit;
font-weight: bolder;
color: var(--global-theme-color);
&:hover {
color: var(--global-hover-color);
}
}
.navbar-brand.social {
padding-bottom: 0;
padding-top: 0;
font-size: 1.7rem;
a {
i::before {
color: var(--global-text-color);
transition-property: all 0.2s ease-in-out;
}
&:hover {
i::before {
color: var(--global-theme-color);
@ -213,13 +255,16 @@ blockquote {
margin-bottom: 4px;
transition: all 0.2s;
}
.top-bar {
transform: rotate(45deg);
transform-origin: 10% 10%;
}
.middle-bar {
opacity: 0;
}
.bottom-bar {
transform: rotate(-45deg);
transform-origin: 10% 90%;
@ -230,9 +275,11 @@ blockquote {
.top-bar {
transform: rotate(0);
}
.middle-bar {
opacity: 1;
}
.bottom-bar {
transform: rotate(0);
}
@ -243,6 +290,7 @@ blockquote {
border: 0;
background-color: inherit;
color: var(--global-text-color);
&:hover {
color: var(--global-hover-color);
}
@ -252,13 +300,16 @@ blockquote {
.social {
text-align: center;
.contact-icons {
font-size: 4rem;
a {
i::before {
color: var(--global-text-color);
transition-property: all 0.2s ease-in-out;
}
&:hover {
i::before {
color: var(--global-theme-color);
@ -266,6 +317,7 @@ blockquote {
}
}
}
.contact-note {
font-size: 0.8rem;
}
@ -276,13 +328,16 @@ blockquote {
footer.fixed-bottom {
background-color: var(--global-footer-bg-color);
font-size: 0.75rem;
.container {
color: var(--global-footer-text-color);
padding-top: 9px;
padding-bottom: 8px;
}
a {
color: var(--global-footer-link-color);
&:hover {
color: var(--global-theme-color);
text-decoration: none;
@ -301,11 +356,11 @@ footer.sticky-bottom {
.cv {
margin-bottom: 40px;
.card {
background-color: var(--global-card-bg-color);
border: 1px solid var(--global-divider-color);
.list-group-item {
background-color: inherit;
border-color: var(--global-divider-color);
@ -333,6 +388,7 @@ footer.sticky-bottom {
text-align: center;
padding-top: 2rem;
padding-bottom: 3rem;
h1 {
color: var(--global-theme-color);
font-size: 5rem;
@ -348,7 +404,8 @@ footer.sticky-bottom {
justify-content: center;
display: flow-root;
p, li {
p,
li {
list-style: none;
display: inline-block;
padding: 1rem 0.5rem;
@ -361,25 +418,30 @@ footer.sticky-bottom {
margin: 0;
margin-bottom: 40px;
padding: 0;
li {
border-bottom: 1px solid var(--global-divider-color);
list-style: none;
padding-top: 2rem;
padding-bottom: 2rem;
.post-meta {
color: var(--global-text-color-light);
font-size: 0.875rem;
margin-bottom: 0;
}
.post-tags {
color: var(--global-text-color-light);
font-size: 0.875rem;
padding-top: 0.25rem;
padding-bottom: 0;
}
a {
color: var(--global-text-color);
text-decoration: none;
&:hover {
color: var(--global-theme-color);
}
@ -391,13 +453,16 @@ footer.sticky-bottom {
.page-item {
.page-link {
color: var(--global-text-color);
&:hover {
color: $black-color;
}
}
&.active .page-link {
color: $white-color;
background-color: var(--global-theme-color);
&:hover {
background-color: var(--global-theme-color);
}
@ -445,7 +510,8 @@ footer.sticky-bottom {
}
}
.grid-sizer, .grid-item {
.grid-sizer,
.grid-item {
width: 250px;
margin-bottom: 10px;
}
@ -465,6 +531,7 @@ footer.sticky-bottom {
.publications {
margin-top: 2rem;
h1 {
color: var(--global-theme-color);
font-size: 2rem;
@ -472,12 +539,15 @@ footer.sticky-bottom {
margin-top: 1em;
margin-bottom: 1em;
}
h2 {
margin-bottom: 1rem;
span {
font-size: 1.5rem;
}
}
h2.year {
color: var(--global-divider-color);
border-top: 1px solid var(--global-divider-color);
@ -486,6 +556,7 @@ footer.sticky-bottom {
margin-bottom: -2rem;
text-align: right;
}
ol.bibliography {
list-style: none;
padding: 0;
@ -493,56 +564,69 @@ footer.sticky-bottom {
li {
margin-bottom: 1rem;
.preview {
width: 100%;
min-width: 80px;
max-width: 200px;
}
.abbr {
height: 2rem;
margin-bottom: 0.5rem;
abbr {
display: inline-block;
background-color: var(--global-theme-color);
padding-left: 1rem;
padding-right: 1rem;
a {
color: white;
&:hover {
text-decoration: none;
}
}
}
.award {
color: var(--global-theme-color) !important;
border: 1px solid var(--global-theme-color);
}
}
.title {
font-weight: bolder;
}
.author {
a {
border-bottom: 1px dashed var(--global-theme-color);
&:hover {
border-bottom-style: solid;
text-decoration: none;
border-bottom-style: solid;
text-decoration: none;
}
}
> em {
>em {
border-bottom: 1px solid;
font-style: normal;
}
> span.more-authors {
>span.more-authors {
color: var(--global-text-color-light);
border-bottom: 1px dashed var(--global-text-color-light);
cursor: pointer;
&:hover {
color: var(--global-text-color);
border-bottom: 1px dashed var(--global-text-color);
color: var(--global-text-color);
border-bottom: 1px dashed var(--global-text-color);
}
}
}
.links {
a.btn {
color: var(--global-text-color);
@ -551,12 +635,14 @@ footer.sticky-bottom {
padding-right: 1rem;
padding-top: 0.25rem;
padding-bottom: 0.25rem;
&:hover {
color: var(--global-theme-color);
border-color: var(--global-theme-color);
}
}
}
.badges {
span {
display: inline-block;
@ -564,11 +650,13 @@ footer.sticky-bottom {
height: 100%;
padding-left: 0.5rem;
vertical-align: middle;
&:hover {
text-decoration: underline;
}
}
}
.hidden {
font-size: 0.875rem;
max-height: 0px;
@ -584,12 +672,14 @@ footer.sticky-bottom {
line-height: 1.4em;
margin: 10px;
}
pre {
font-size: 1em;
line-height: 1.4em;
padding: 10px;
}
}
.hidden.open {
max-height: 100em;
transition-property: 0.15s ease;
@ -598,9 +688,11 @@ footer.sticky-bottom {
-o-transition: 0.15s ease;
transition: all 0.15s ease;
}
div.abstract.hidden {
border: dashed 1px var(--global-bg-color);
}
div.abstract.hidden.open {
border-color: var(--global-text-color);
}
@ -618,9 +710,12 @@ pre {
background-color: var(--global-code-bg-color);
border-radius: 6px;
padding: 6px 12px;
pre, code {
pre,
code {
background-color: transparent;
border-radius: 0;
margin-bottom: 0;
padding: 0;
}
}
@ -643,26 +738,30 @@ html.transition *:after {
}
// Extra Markdown style (post Customization)
.post{
.post-meta{
.post {
.post-meta {
color: var(--global-text-color-light);
font-size: 0.875rem;
margin-bottom: 0;
}
.post-tags{
.post-tags {
color: var(--global-text-color-light);
font-size: 0.875rem;
padding-top: 0.25rem;
padding-bottom: 1rem;
a {
color: var(--global-text-color-light);
text-decoration: none;
&:hover {
color: var(--global-theme-color);
}
}
}
.post-content{
.post-content {
blockquote {
border-left: 5px solid var(--global-theme-color);
padding: 8px;
@ -671,56 +770,89 @@ html.transition *:after {
}
progress {
/* Positioning */
position: fixed;
left: 0;
top: 56px;
z-index: 10;
/* Dimensions */
width: 100%;
height: 1px;
/* Reset the appearance */
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
/* Get rid of the default border in Firefox/Opera. */
border: none;
/* Progress bar container for Firefox/IE10 */
background-color: transparent;
/* Progress bar value for IE10 */
color: var(--global-theme-color);
}
progress::-webkit-progress-bar {
background-color: transparent;
}
progress::-webkit-progress-value {
background-color: var(--global-theme-color);
}
progress::-moz-progress-bar {
background-color: var(--global-theme-color);
}
.progress-container {
width: 100%;
background-color: transparent;
position: fixed;
top: 56px;
left: 0;
height: 5px;
display: block;
}
.progress-bar {
background-color: var(--global-theme-color);
width: 0%;
display: block;
height: inherit;
}
/* Positioning */
position: fixed;
left: 0;
top: 56px;
z-index: 10;
/* Dimensions */
width: 100%;
height: 1px;
/* Reset the appearance */
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
/* Get rid of the default border in Firefox/Opera. */
border: none;
/* Progress bar container for Firefox/IE10 */
background-color: transparent;
/* Progress bar value for IE10 */
color: var(--global-theme-color);
}
progress::-webkit-progress-bar {
background-color: transparent;
}
progress::-webkit-progress-value {
background-color: var(--global-theme-color);
}
progress::-moz-progress-bar {
background-color: var(--global-theme-color);
}
.progress-container {
width: 100%;
background-color: transparent;
position: fixed;
top: 56px;
left: 0;
height: 5px;
display: block;
}
.progress-bar {
background-color: var(--global-theme-color);
width: 0%;
display: block;
height: inherit;
}
pre {
padding: 8px 12px;
position: relative;
/* Copy code to clipboard button */
.copy {
background: var(--global-card-bg-color);
border-color: var(--global-bg-color);
border-radius: .3rem;
border-style: solid;
color: var(--global-text-color);
font-size: medium;
opacity: 0;
position: absolute;
right: .25rem;
top: .25rem;
&:active,
&:focus,
&:hover {
color: var(--global-hover-color);
opacity: 1;
}
}
&:active .copy,
&:focus .copy,
&:hover .copy {
color: var(--global-hover-color);
opacity: 1;
}
}

36
assets/js/copy_code.js Normal file
View File

@ -0,0 +1,36 @@
// create element for copy button in code blocks
var codeBlocks = document.querySelectorAll('pre');
codeBlocks.forEach(function (codeBlock) {
if (codeBlock.querySelector('pre:not(.lineno)') || codeBlock.querySelector('code')) {
var copyButton = document.createElement('button');
copyButton.className = 'copy';
copyButton.type = 'button';
copyButton.ariaLabel = 'Copy code to clipboard';
copyButton.innerText = 'Copy';
copyButton.innerHTML = '<i class="fas fa-clipboard"></i>';
codeBlock.append(copyButton);
// get code from code block and copy to clipboard
copyButton.addEventListener('click', function () {
// check if code block has line numbers
// i.e. `kramdown.syntax_highlighter_opts.block.line_numbers` set to true in _config.yml
// or using `jekyll highlight` liquid tag with `linenos` option
if (codeBlock.querySelector('pre:not(.lineno)')) {
// get code from code block ignoring line numbers
var code = codeBlock.querySelector('pre:not(.lineno)').innerText.trim();
} else { // if (codeBlock.querySelector('code')) {
// get code from code block when line numbers are not displayed
var code = codeBlock.querySelector('code').innerText.trim();
}
window.navigator.clipboard.writeText(code);
copyButton.innerText = 'Copied';
copyButton.innerHTML = '<i class="fas fa-clipboard-check"></i>';
var waitFor = 3000;
setTimeout(function () {
copyButton.innerText = 'Copy';
copyButton.innerHTML = '<i class="fas fa-clipboard"></i>';
}, waitFor);
});
}
});