From bacddc78219e08e269f3b888c86c559ca5f33cc9 Mon Sep 17 00:00:00 2001 From: George <31376482+george-gca@users.noreply.github.com> Date: Sun, 19 Mar 2023 18:17:34 -0300 Subject: [PATCH] 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. --- _includes/scripts/misc.html | 1 + _posts/2015-07-15-code.md | 32 +++- _sass/_base.scss | 304 ++++++++++++++++++++++++++---------- assets/js/copy_code.js | 36 +++++ 4 files changed, 286 insertions(+), 87 deletions(-) create mode 100644 assets/js/copy_code.js diff --git a/_includes/scripts/misc.html b/_includes/scripts/misc.html index 08ba49f03517..90360808d291 100644 --- a/_includes/scripts/misc.html +++ b/_includes/scripts/misc.html @@ -12,3 +12,4 @@ + diff --git a/_posts/2015-07-15-code.md b/_posts/2015-07-15-code.md index 675543df5eda..a86fa077c536 100644 --- a/_posts/2015-07-15-code.md +++ b/_posts/2015-07-15-code.md @@ -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 %}
code code code
{% endhighlight %} diff --git a/_sass/_base.scss b/_sass/_base.scss index 8954b6c80287..ec6a768fa713 100644 --- a/_sass/_base.scss +++ b/_sass/_base.scss @@ -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; + } +} diff --git a/assets/js/copy_code.js b/assets/js/copy_code.js new file mode 100644 index 000000000000..b12af5342b07 --- /dev/null +++ b/assets/js/copy_code.js @@ -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 = ''; + 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 = ''; + var waitFor = 3000; + + setTimeout(function () { + copyButton.innerText = 'Copy'; + copyButton.innerHTML = ''; + }, waitFor); + }); + } +});