Skip to content

Commit 2d8f345

Browse files
committed
add copy/paste button to codeblocks
1 parent 510e6d2 commit 2d8f345

File tree

6 files changed

+219
-19
lines changed

6 files changed

+219
-19
lines changed

gulp.d/tasks/build.js

+15-8
Original file line numberDiff line numberDiff line change
@@ -98,18 +98,25 @@ module.exports = (src, dest, preview) => () => {
9898
.src('css/site.css', { ...opts, sourcemaps })
9999
.pipe(postcss((file) => ({ plugins: postcssPlugins, options: { file } }))),
100100
vfs.src('font/*.{ttf,woff*(2)}', opts),
101-
vfs
102-
.src('img/**/*.{gif,ico,jpg,png,svg}', opts)
103-
.pipe(
104-
imagemin(
101+
vfs.src('img/**/*.{gif,ico,jpg,png,svg}', opts).pipe(
102+
//preview
103+
// ? through()
104+
// : imagemin(
105+
imagemin(
105106
[
106107
imagemin.gifsicle(),
107108
imagemin.jpegtran(),
108109
imagemin.optipng(),
109-
imagemin.svgo({ plugins: [{ removeViewBox: false }] }),
110-
].reduce((accum, it) => (it ? accum.concat(it) : accum), [])
111-
)
112-
),
110+
imagemin.svgo({
111+
plugins: [
112+
// { cleanupIDs: { preservePrefixes: ['symbol-', 'view-'] } },
113+
{ removeViewBox: false },
114+
// { removeDesc: false },
115+
],
116+
}),
117+
// ].reduce((accum, it) => (it ? accum.concat(it) : accum), [])
118+
])
119+
),
113120
vfs.src('helpers/*.js', opts),
114121
vfs.src('layouts/*.hbs', opts),
115122
vfs.src('partials/*.hbs', opts)

preview-src/index.adoc

+10
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ vfs
6969
<2> Wrap each streaming file in a buffer so the files can be processed by uglify.
7070
Uglify can only work with buffers, not streams.
7171

72+
Execute these commands to validate and build your site:
73+
74+
$ podman run -v $PWD:/antora:Z --rm -t antora/antora \
75+
version
76+
3.0.0
77+
$ podman run -v $PWD:/antora:Z --rm -it antora/antora \
78+
--clean \
79+
antora-playbook.yml
80+
81+
7282
Cum dicat #putant# ne.
7383
Est in <<inline,reque>> homero principes, meis deleniti mediocrem ad has.
7484
Altera atomorum his ex, has cu elitr melius propriae.

src/css/doc.css

+80-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
.doc {
2-
color: var(--doc-font-color);
31
font-size: var(--doc-font-size);
42
hyphens: auto;
53
line-height: var(--doc-line-height);
@@ -522,26 +520,97 @@
522520
padding: 0.75rem;
523521
}
524522

525-
/* NOTE assume pre.highlight contains code[data-lang] */
526523
.doc pre.highlight {
527524
position: relative;
528525
}
529526

530-
.doc .listingblock code[data-lang]::before {
531-
content: attr(data-lang);
532-
display: none;
527+
.doc .language-console .hljs-meta {
528+
user-select: none;
529+
}
530+
531+
.doc .source-toolbox {
532+
display: flex;
533+
visibility: hidden;
534+
position: absolute;
535+
top: 0.25rem;
536+
right: 0.5rem;
533537
color: var(--pre-annotation-font-color);
538+
font-family: var(--body-font-family);
534539
font-size: calc(13.5 / var(--rem-base) * 1rem);
535-
letter-spacing: 0.05em;
536540
line-height: 1;
541+
}
542+
543+
.doc .listingblock:hover .source-toolbox {
544+
visibility: visible;
545+
}
546+
547+
.doc .source-toolbox .source-lang {
537548
text-transform: uppercase;
549+
letter-spacing: 0.075em;
550+
font-size: 0.96em;
551+
line-height: 1.0425;
552+
}
553+
554+
.doc .source-toolbox > :not(:last-child)::after {
555+
content: "|";
556+
letter-spacing: 0;
557+
padding: 0 1ch;
558+
}
559+
560+
.doc .source-toolbox .copy-button {
561+
display: flex;
562+
flex-direction: column;
563+
align-items: center;
564+
background: transparent;
565+
border: none;
566+
color: inherit;
567+
outline: none;
568+
padding: 0;
569+
font-size: inherit;
570+
line-height: inherit;
571+
width: 1em;
572+
height: 1em;
573+
}
574+
575+
.source-toolbox .copy-button * {
576+
flex: none;
577+
}
578+
579+
.source-toolbox .copy-button svg {
580+
fill: currentColor;
581+
width: inherit;
582+
height: inherit;
583+
}
584+
585+
.source-toolbox .copy-toast {
586+
position: relative;
587+
display: inline-flex;
588+
justify-content: center;
589+
margin-top: 1em;
590+
background-color: var(--doc-font-color);
591+
border-radius: 0.25em;
592+
padding: 0.5em;
593+
color: var(--color-white);
594+
cursor: auto;
595+
opacity: 0;
596+
transition: opacity 0.5s ease 0.75s;
597+
}
598+
599+
.source-toolbox .copy-toast::after {
600+
content: "";
538601
position: absolute;
539-
top: 0.25rem;
540-
right: 0.25rem;
602+
top: 0;
603+
width: 1em;
604+
height: 1em;
605+
border: 0.55em solid transparent;
606+
border-left-color: var(--doc-font-color);
607+
transform: rotate(-90deg) translateX(50%) translateY(50%);
608+
transform-origin: left;
541609
}
542610

543-
.doc .listingblock:hover code[data-lang]::before {
544-
display: block;
611+
.source-toolbox .copy-button.clicked .copy-toast {
612+
opacity: 1;
613+
transition: none;
545614
}
546615

547616
.doc .dlist dt {

src/img/octicons-16.svg

+36
Loading

src/js/07-copy-to-clipboard.js

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
;(function () {
2+
'use strict'
3+
4+
var CMD_RX = /^\$ (\S[^\\\n]*(\\\n(?!\$ )[^\\\n]*)*)(?=\n|$)/gm
5+
var LINE_CONTINUATION_RX = /( ) *\\\n *|\\\n( ?) */g
6+
var TRAILING_SPACE_RX = / +$/gm
7+
var config = (document.getElementById('site-script') || { dataset: {} }).dataset
8+
9+
;[].slice.call(document.querySelectorAll('.doc pre.highlight, .doc .literalblock pre')).forEach(function (pre) {
10+
var code, language, lang, copy, toast, toolbox
11+
if (pre.classList.contains('highlight')) {
12+
code = pre.querySelector('code')
13+
if ((language = code.dataset.lang) && language !== 'console') {
14+
;(lang = document.createElement('span')).className = 'source-lang'
15+
lang.appendChild(document.createTextNode(language))
16+
}
17+
} else if (pre.innerText.startsWith('$ ')) {
18+
var block = pre.parentNode.parentNode
19+
block.classList.remove('literalblock')
20+
block.classList.add('listingblock')
21+
pre.classList.add('highlightjs', 'highlight')
22+
;(code = document.createElement('code')).className = 'language-console hljs'
23+
code.dataset.lang = 'console'
24+
code.appendChild(pre.firstChild)
25+
pre.appendChild(code)
26+
} else {
27+
return
28+
}
29+
;(toolbox = document.createElement('div')).className = 'source-toolbox'
30+
if (lang) toolbox.appendChild(lang)
31+
if (window.navigator.clipboard) {
32+
;(copy = document.createElement('button')).className = 'copy-button'
33+
copy.setAttribute('title', 'Copy to clipboard')
34+
if (config.svgAs === 'svg') {
35+
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
36+
svg.setAttribute('class', 'copy-icon')
37+
var use = document.createElementNS('http://www.w3.org/2000/svg', 'use')
38+
use.setAttribute('href', window.uiRootPath + '/img/octicons-16.svg#icon-clippy')
39+
svg.appendChild(use)
40+
copy.appendChild(svg)
41+
} else {
42+
var img = document.createElement('img')
43+
img.src = window.uiRootPath + '/img/octicons-16.svg#view-clippy'
44+
img.alt = 'copy icon'
45+
img.className = 'copy-icon'
46+
copy.appendChild(img)
47+
}
48+
;(toast = document.createElement('span')).className = 'copy-toast'
49+
toast.appendChild(document.createTextNode('Copied!'))
50+
copy.appendChild(toast)
51+
toolbox.appendChild(copy)
52+
}
53+
pre.appendChild(toolbox)
54+
if (copy) copy.addEventListener('click', writeToClipboard.bind(copy, code))
55+
})
56+
57+
function extractCommands (text) {
58+
var cmds = []
59+
var m
60+
while ((m = CMD_RX.exec(text))) cmds.push(m[1].replace(LINE_CONTINUATION_RX, '$1$2'))
61+
return cmds.join(' && ')
62+
}
63+
64+
function writeToClipboard (code) {
65+
var text = code.innerText.replace(TRAILING_SPACE_RX, '')
66+
if (code.dataset.lang === 'console' && text.startsWith('$ ')) text = extractCommands(text)
67+
window.navigator.clipboard.writeText(text).then(
68+
function () {
69+
this.classList.add('clicked')
70+
this.offsetHeight // eslint-disable-line no-unused-expressions
71+
this.classList.remove('clicked')
72+
}.bind(this),
73+
function () {}
74+
)
75+
}
76+
})()
77+

src/partials/footer-scripts.hbs

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
<script>window.uiRootPath = '{{{uiRootPath}}}'</script>
12
<script src="{{{uiRootPath}}}/js/site.js"></script>
23
<script async src="{{{uiRootPath}}}/js/vendor/highlight.js"></script>

0 commit comments

Comments
 (0)