From 662ce55f7f70a9d900eea098a7bbef8d2c22e5ba Mon Sep 17 00:00:00 2001 From: Mikhail Kuznetcov Date: Thu, 16 Aug 2018 13:05:04 +0200 Subject: [PATCH 1/4] add draft implementation --- docs/config/README.md | 7 +++++++ lib/app/components/ClipboardCopy.vue | 13 +++++++++++++ lib/markdown/bufferCopy.js | 28 ++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 lib/app/components/ClipboardCopy.vue create mode 100644 lib/markdown/bufferCopy.js diff --git a/docs/config/README.md b/docs/config/README.md index 1899496557..9054ca31b3 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -197,6 +197,13 @@ module.exports = { } ``` +### markdown.enableCbCopy + +- Type: `boolean` +- Default: `undefined` + +Whether to show icon to copy the code snippet into the clipboard. + ## Build Pipeline ### postcss diff --git a/lib/app/components/ClipboardCopy.vue b/lib/app/components/ClipboardCopy.vue new file mode 100644 index 0000000000..bf97290d3c --- /dev/null +++ b/lib/app/components/ClipboardCopy.vue @@ -0,0 +1,13 @@ + + + + diff --git a/lib/markdown/bufferCopy.js b/lib/markdown/bufferCopy.js new file mode 100644 index 0000000000..80f46b26d3 --- /dev/null +++ b/lib/markdown/bufferCopy.js @@ -0,0 +1,28 @@ +const copyToClipboard = str => { + const el = document.createElement('textarea') + el.value = str + el.setAttribute('readonly', '') + el.style.position = 'absolute' + el.style.left = '-9999px' + document.body.appendChild(el) + const selected = + document.getSelection().rangeCount > 0 + ? document.getSelection().getRangeAt(0) + : false + el.select() + document.execCommand('copy') + document.body.removeChild(el) + if (selected) { + document.getSelection().removeAllRanges() + document.getSelection().addRange(selected) + } +} + +module.exports = md => { + const fence = md.renderer.rules.fence + md.renderer.rules.fence = (...args) => { + const rawCode = fence(...args) + return rawCode + } +} + From 8e8817029499209fc822b5b4b19be99a3e3ff647 Mon Sep 17 00:00:00 2001 From: shershen08 Date: Sun, 19 Aug 2018 13:57:42 +0200 Subject: [PATCH 2/4] add icon and style component --- lib/app/components/ClipboardCopy.vue | 32 ++++++++++++++++++---------- lib/app/components/copy.svg | 1 + 2 files changed, 22 insertions(+), 11 deletions(-) create mode 100644 lib/app/components/copy.svg diff --git a/lib/app/components/ClipboardCopy.vue b/lib/app/components/ClipboardCopy.vue index bf97290d3c..ad0a668260 100644 --- a/lib/app/components/ClipboardCopy.vue +++ b/lib/app/components/ClipboardCopy.vue @@ -1,13 +1,23 @@ - - - + + diff --git a/lib/app/components/copy.svg b/lib/app/components/copy.svg new file mode 100644 index 0000000000..7b49bda364 --- /dev/null +++ b/lib/app/components/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file From 3c8d44fc6b62bb40feb26b4a9541d428f639f90b Mon Sep 17 00:00:00 2001 From: shershen08 Date: Sun, 19 Aug 2018 13:58:09 +0200 Subject: [PATCH 3/4] add utility function to copy and iteration over code elements --- lib/app/clipboardCopy.js | 20 ++++++++++++++++++++ lib/app/util.js | 21 +++++++++++++++++++++ lib/markdown/bufferCopy.js | 28 ---------------------------- 3 files changed, 41 insertions(+), 28 deletions(-) create mode 100644 lib/app/clipboardCopy.js delete mode 100644 lib/markdown/bufferCopy.js diff --git a/lib/app/clipboardCopy.js b/lib/app/clipboardCopy.js new file mode 100644 index 0000000000..e4bbe6be25 --- /dev/null +++ b/lib/app/clipboardCopy.js @@ -0,0 +1,20 @@ + +import { copyToClipboard } from './util' + +const generateCopyButton = (parent) => { + if (parent.classList.contains('codecopy-enabled')) return + + const copyElement = document.createElement('span') + copyElement.className = 'code-copy' + copyElement.title = 'Click to copy to clipboard' + copyElement.addEventListener('click', () => { + copyToClipboard(parent.innerText) + }) + parent.appendChild(copyElement) + parent.classList.add('codecopy-enabled') +} + +export function enableCbCopy () { + const codeBlocks = document.querySelectorAll('div[class*="language-"] pre') + codeBlocks.forEach(generateCopyButton) +} diff --git a/lib/app/util.js b/lib/app/util.js index c11172240e..b9cd9ab43f 100644 --- a/lib/app/util.js +++ b/lib/app/util.js @@ -17,3 +17,24 @@ export function findPageForPath (pages, path) { frontmatter: {} } } + +export function copyToClipboard (str) { + const el = document.createElement('textarea') + el.value = str + el.setAttribute('readonly', '') + el.style.position = 'absolute' + el.style.left = '-9999px' + document.body.appendChild(el) + const selected = + document.getSelection().rangeCount > 0 + ? document.getSelection().getRangeAt(0) + : false + el.select() + document.execCommand('copy') + document.body.removeChild(el) + if (selected) { + document.getSelection().removeAllRanges() + document.getSelection().addRange(selected) + } +} + diff --git a/lib/markdown/bufferCopy.js b/lib/markdown/bufferCopy.js deleted file mode 100644 index 80f46b26d3..0000000000 --- a/lib/markdown/bufferCopy.js +++ /dev/null @@ -1,28 +0,0 @@ -const copyToClipboard = str => { - const el = document.createElement('textarea') - el.value = str - el.setAttribute('readonly', '') - el.style.position = 'absolute' - el.style.left = '-9999px' - document.body.appendChild(el) - const selected = - document.getSelection().rangeCount > 0 - ? document.getSelection().getRangeAt(0) - : false - el.select() - document.execCommand('copy') - document.body.removeChild(el) - if (selected) { - document.getSelection().removeAllRanges() - document.getSelection().addRange(selected) - } -} - -module.exports = md => { - const fence = md.renderer.rules.fence - md.renderer.rules.fence = (...args) => { - const rawCode = fence(...args) - return rawCode - } -} - From 63f31d938ff0e6f10883a50102983b5548028499 Mon Sep 17 00:00:00 2001 From: shershen08 Date: Sun, 19 Aug 2018 13:58:34 +0200 Subject: [PATCH 4/4] add style component and new route callback --- lib/app/app.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/app/app.js b/lib/app/app.js index 511abbd472..fb0108b764 100644 --- a/lib/app/app.js +++ b/lib/app/app.js @@ -6,6 +6,7 @@ import { routes } from '@temp/routes' import { siteData } from '@temp/siteData' import enhanceApp from '@temp/enhanceApp' import themeEnhanceApp from '@temp/themeEnhanceApp' +import { enableCbCopy } from './clipboardCopy' // generated from user config import('@temp/style.styl') @@ -14,6 +15,7 @@ import('@temp/style.styl') import Content from './components/Content' import OutboundLink from './components/OutboundLink.vue' import ClientOnly from './components/ClientOnly' +import ClipboardCopy from './components/ClipboardCopy.vue' // suggest dev server restart on base change if (module.hot) { @@ -38,6 +40,7 @@ Vue.component('OutboundLink', OutboundLink) Vue.component('Badge', () => import('./components/Badge.vue')) // component for client-only content Vue.component('ClientOnly', ClientOnly) +Vue.component('ClipboardCopy', ClipboardCopy) // global helper for adding base path to absolute urls Vue.prototype.$withBase = function (path) { @@ -82,6 +85,10 @@ export function createApp () { } }) + router.afterEach((to, from) => { + enableCbCopy() + }) + const options = {} themeEnhanceApp({ Vue, options, router, siteData })