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/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 })
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/components/ClipboardCopy.vue b/lib/app/components/ClipboardCopy.vue
new file mode 100644
index 0000000000..ad0a668260
--- /dev/null
+++ b/lib/app/components/ClipboardCopy.vue
@@ -0,0 +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
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)
+ }
+}
+