diff --git a/packages/saber-plugin-code-copy/LICENSE b/packages/saber-plugin-code-copy/LICENSE new file mode 100644 index 000000000..0fa9bb511 --- /dev/null +++ b/packages/saber-plugin-code-copy/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) EGOIST <0x142857@gmail.com> (https://github.com/egoist) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/packages/saber-plugin-code-copy/README.md b/packages/saber-plugin-code-copy/README.md new file mode 100644 index 000000000..080fc6398 --- /dev/null +++ b/packages/saber-plugin-code-copy/README.md @@ -0,0 +1,44 @@ +# saber-plugin-code-copy + +Copy code to clipboard. + +## Install + +```bash +yarn add saber-plugin-code-copy +``` + +## Usage + +In your `saber-config.yml`: + +```yml +plugins: + - resolve: saber-plugin-code-copy +``` + +## Options + +### statusAttribute + +- Type: `string` +- Default: `title` + +By default the `title` attribute of the button is set to `Copy`, when code is copied we update it to `Copied`, you can specify another attribute name if you want, e.g. `aria-label`. + +### buttonStyle + +- Type: `object` +- Default: `undefined` + +Assign custom style to the _Copy_ button, e.g.: + +```js +{ + backgroundColor: 'red' +} +``` + +## License + +MIT. diff --git a/packages/saber-plugin-code-copy/lib/index.js b/packages/saber-plugin-code-copy/lib/index.js new file mode 100644 index 000000000..d305f908a --- /dev/null +++ b/packages/saber-plugin-code-copy/lib/index.js @@ -0,0 +1,16 @@ +const path = require('path') + +const ID = 'copy-code' + +exports.name = ID + +exports.apply = (api, options) => { + api.browserApi.add(path.join(__dirname, 'saber-browser.js')) + api.hooks.chainWebpack.tap(ID, config => { + config.plugin('constants').tap(([constants]) => [ + Object.assign(constants, { + __CODE_COPY_OPTIONS__: JSON.stringify(options) + }) + ]) + }) +} diff --git a/packages/saber-plugin-code-copy/lib/saber-browser.js b/packages/saber-plugin-code-copy/lib/saber-browser.js new file mode 100644 index 000000000..54fa67a63 --- /dev/null +++ b/packages/saber-plugin-code-copy/lib/saber-browser.js @@ -0,0 +1,70 @@ +/* eslint-env browser */ +/* globals __CODE_COPY_OPTIONS__ */ + +export default ({ router }) => { + if (process.browser) { + const copy = require('modern-copy').default + + const forEach = (arr, fn) => Array.prototype.forEach.call(arr, fn) + const { statusAttribute = 'title', buttonStyle } = __CODE_COPY_OPTIONS__ + + let isStyleInjected = false + const injectStyle = () => { + if (isStyleInjected) { + return + } + + const style = document.createElement('style') + style.id = 'saber-plugin-code-copy-style' + style.append( + document.createTextNode(` + .saber-highlight:hover:before {display: none !important;} + .saber-highlight:hover .saber-plugin-code-copy-button { + opacity: 1; + } + .saber-plugin-code-copy-button { + opacity: 0; + position: absolute; + right: 8px; + top: 8px; + z-index: 2000; + background: #f0f0f0; + color: inherit; + padding: 3px 6px; + border-radius: 4px; + cursor: pointer; + border: 1px solid #ccc; + transition: opacity .3s ease-in-out; + } + `) + ) + document.head.append(style) + isStyleInjected = true + } + + router.afterEach(() => { + setTimeout(() => { + forEach(document.querySelectorAll('.saber-highlight'), el => { + if (el.dataset.hasCopy) return + el.dataset.hasCopy = true + const copyButton = document.createElement('button') + copyButton.className = 'saber-plugin-code-copy-button' + copyButton.innerHTML = `` + copyButton.setAttribute(statusAttribute, 'Copy') + copyButton.addEventListener('click', () => { + copy(el.querySelector('.saber-highlight-code').textContent) + copyButton.setAttribute(statusAttribute, 'Copied') + }) + copyButton.addEventListener('mouseleave', () => { + copyButton.setAttribute(statusAttribute, 'Copy') + }) + Object.assign(copyButton.style, buttonStyle) + injectStyle() + el.append(copyButton) + }) + }, 100) + }) + } +} diff --git a/packages/saber-plugin-code-copy/package.json b/packages/saber-plugin-code-copy/package.json new file mode 100644 index 000000000..9484e3bbd --- /dev/null +++ b/packages/saber-plugin-code-copy/package.json @@ -0,0 +1,17 @@ +{ + "name": "saber-plugin-copy-code", + "version": "0.1.0", + "description": "Allow to copy code into clipboard", + "license": "MIT", + "main": "lib/index.js", + "files": [ + "lib" + ], + "xo": false, + "peerDependencies": { + "saber": ">=0.7.0" + }, + "dependencies": { + "modern-copy": "^1.0.3" + } +} diff --git a/website/saber-config.js b/website/saber-config.js index ad4dcdd66..dd4d4715e 100644 --- a/website/saber-config.js +++ b/website/saber-config.js @@ -99,6 +99,14 @@ module.exports = { }, { resolve: '../packages/saber-plugin-image' + }, + { + resolve: '../packages/saber-plugin-code-copy', + options: { + buttonStyle: { + border: 'none' + } + } } ] } diff --git a/yarn.lock b/yarn.lock index 2700bcc57..960493aff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8734,6 +8734,11 @@ mkdirp@*, mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: dependencies: minimist "0.0.8" +modern-copy@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/modern-copy/-/modern-copy-1.0.3.tgz#1b0a3e8548316cae95e76f9f75cacb561d193b8e" + integrity sha512-2JGQcw7PFY74QsVupT1xzR09w7JGWjO9/XxFr42gCVuqPdeSmtRUlfcJnZTTPyRi0dbIXJ5Gotp1Pij4llMNWQ== + modify-values@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"