├── .babelrc ├── .editorconfig ├── .eslintrc.js ├── .github └── workflows │ ├── build.yml │ └── publish.yml ├── .gitignore ├── .prettierrc.js ├── LICENSE ├── README.md ├── api-extractor.json ├── example └── vue3-clipboard-example │ ├── .gitignore │ ├── index.html │ ├── package.json │ ├── public │ └── favicon.ico │ └── src │ ├── App.vue │ ├── assets │ └── logo.png │ ├── components │ └── HelloWorld.vue │ ├── index.css │ └── main.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── @types │ └── index.ts ├── VueClipboard.ts └── index.ts ├── tsconfig.json └── vite.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "env": { 4 | "production": { 5 | "presets": ["minify"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | ecmaVersion: 2020, 5 | sourceType: 'module', 6 | }, 7 | extends: [ 8 | 'plugin:@typescript-eslint/recommended', 9 | 'prettier/@typescript-eslint', 10 | 'plugin:prettier/recommended', 11 | ], 12 | rules: {}, 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions/setup-node@v1 11 | with: 12 | node-version: 12 13 | registry-url: https://registry.npmjs.org/ 14 | - run: npm install 15 | - run: npm run-script build 16 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v1 13 | with: 14 | node-version: 12 15 | registry-url: https://registry.npmjs.org/ 16 | - run: npm install 17 | - run: npm run build 18 | - run: npm run build:dts 19 | - run: npm publish --access public 20 | env: 21 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules 3 | coverage 4 | npm-debug.log 5 | .nyc_output 6 | dist 7 | package-lock.json 8 | .DS_Store 9 | demo_dist 10 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | trailingComma: 'all', 4 | tabWidth: 2, 5 | semi: false, 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Soren Martius 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue3-clipboard 2 | 3 | **Note:** I don't maintain this repository anymore. Instead I recommend using [vueuse](https://vueuse.org/core/useClipboard/#useclipboard). 4 | 5 | 6 | ![Build](https://github.com/soerenmartius/vue3-clipboard/workflows/Build/badge.svg) 7 | 8 | [Clipboard.js](https://clipboardjs.com/) bindings for Vue 3. 9 | 10 | This repository is a port of Inndy's 11 | [vue-clipboard2](https://github.com/Inndy/vue-clipboard2) plugin for Vue3. 12 | 13 | ## Install 14 | 15 | `npm install --save @soerenmartius/vue3-clipboard` 16 | 17 | ## Usage 18 | 19 | ### Import the `v-clipboard` directive globally 20 | 21 | **`src/main.ts`** 22 | 23 | ```typescript 24 | import { createApp } from 'vue' 25 | import App from './App.vue' 26 | import { VueClipboard } from '@soerenmartius/vue3-clipboard' 27 | 28 | createApp(App).use(VueClipboard).mount('#app') 29 | 30 | ``` 31 | 32 | ### Import the `v-clipboard` directive for a specific component 33 | 34 | ## Examples 35 | 36 | ### Apply the v-clipboard directive to an element 37 | 38 | ```typescript 39 | 43 | 44 | 55 | ``` 56 | 57 | ### Copy value of an input, and handle events separately. 58 | 59 | ```typescript 60 | 70 | 71 | 90 | ``` 91 | 92 | ### Standalone usage of the toClipboard function 93 | 94 | ```typescript 95 | 99 | 100 | 113 | ``` 114 | 115 | ## Contributing 116 | 117 | Contributions are always encouraged and welcome! 118 | For the process of accepting changes, we use 119 | [Pull Requests](https://github.com/soerenmartius/vue3-clipboard/pulls). 120 | For feature requests please fill an 121 | [issue](https://github.com/soerenmartius/vue3-clipboard/issues/new). 122 | 123 | ## License 124 | 125 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 126 | -------------------------------------------------------------------------------- /api-extractor.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", 3 | "mainEntryPointFilePath": "./dist/src/index.d.ts", 4 | "apiReport": { 5 | "enabled": true, 6 | "reportFolder": "/temp/" 7 | }, 8 | "docModel": { 9 | "enabled": true 10 | }, 11 | "dtsRollup": { 12 | "enabled": true, 13 | "untrimmedFilePath": "./dist/.d.ts" 14 | }, 15 | "tsdocMetadata": { 16 | "enabled": false 17 | }, 18 | "messages": { 19 | "compilerMessageReporting": { 20 | "default": { 21 | "logLevel": "warning" 22 | } 23 | }, 24 | "extractorMessageReporting": { 25 | "default": { 26 | "logLevel": "warning", 27 | "addToApiReportFile": true 28 | }, 29 | "ae-missing-release-tag": { 30 | "logLevel": "none" 31 | } 32 | }, 33 | "tsdocMessageReporting": { 34 | "default": { 35 | "logLevel": "warning" 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /example/vue3-clipboard-example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local -------------------------------------------------------------------------------- /example/vue3-clipboard-example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /example/vue3-clipboard-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-clipboard-example", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build" 7 | }, 8 | "dependencies": { 9 | "vue": "^3.0.4", 10 | "vue3-clipboard": "^1.0.0" 11 | }, 12 | "devDependencies": { 13 | "@vue/compiler-sfc": "^3.0.4", 14 | "vite": "^1.0.0-rc.13" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example/vue3-clipboard-example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soerenmartius/vue3-clipboard/157b4ed54529c10eb21351afaa535894a3123934/example/vue3-clipboard-example/public/favicon.ico -------------------------------------------------------------------------------- /example/vue3-clipboard-example/src/App.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 16 | -------------------------------------------------------------------------------- /example/vue3-clipboard-example/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/soerenmartius/vue3-clipboard/157b4ed54529c10eb21351afaa535894a3123934/example/vue3-clipboard-example/src/assets/logo.png -------------------------------------------------------------------------------- /example/vue3-clipboard-example/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 15 | -------------------------------------------------------------------------------- /example/vue3-clipboard-example/src/index.css: -------------------------------------------------------------------------------- 1 | #app { 2 | font-family: Avenir, Helvetica, Arial, sans-serif; 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; 5 | text-align: center; 6 | color: #2c3e50; 7 | margin-top: 60px; 8 | } 9 | -------------------------------------------------------------------------------- /example/vue3-clipboard-example/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import VueClipboard from '@soerenmartius/vue3-clipboard' 4 | import './index.css' 5 | 6 | createApp(App).use(VueClipboard).mount('#app') 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@soerenmartius/vue3-clipboard", 3 | "version": "0.1.2", 4 | "description": "A Vue 3 binding for clipboard.js", 5 | "main": "dist/vue3-clipboard.cjs.js", 6 | "browser": "dist/vue3-clipboard.esm.js", 7 | "unpkg": "dist/vue3-clipboard.global.js", 8 | "jsdelivr": "dist/vue3-clipboard.global.js", 9 | "module": "dist/vue3-clipboard.esm-bundler.js", 10 | "types": "dist/vue3-clipboard.d.ts", 11 | "sideEffects": false, 12 | "files": [ 13 | "dist/*.js", 14 | "dist/vue3-clipboard.d.ts", 15 | "LICENSE", 16 | "README.md" 17 | ], 18 | "scripts": { 19 | "build": "rollup -c rollup.config.js", 20 | "dev": "vite serve", 21 | "start": "vite serve --mode production", 22 | "demo:build": "vite build", 23 | "release": "bash scripts/release.sh", 24 | "build:dts": "api-extractor run --local --verbose", 25 | "lint": "eslint './{src,tests,examples}/**/*.{js,ts,tsx,vue,md}'", 26 | "lint:fix": "yarn run lint --fix", 27 | "test:types": "tsc --build tsconfig.json" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/soerenmartius/vue3-clipboard.git" 32 | }, 33 | "keywords": [ 34 | "vue", 35 | "vue3", 36 | "clipboard", 37 | "clipboard.js" 38 | ], 39 | "author": { 40 | "name": "Soeren Martius", 41 | "email": "soeren.martius@gmail.com" 42 | }, 43 | "license": "MIT", 44 | "bugs": { 45 | "url": "https://github.com/soerenmartius/vue3-clipboard/issues" 46 | }, 47 | "homepage": "https://github.com/soerenmartius/vue3-clipboard#readme", 48 | "husky": { 49 | "hooks": { 50 | "pre-commit": "lint-staged" 51 | } 52 | }, 53 | "lint-staged": { 54 | "*.js": [ 55 | "prettier --write" 56 | ], 57 | "*.ts?(x)": [ 58 | "prettier --parser=typescript --write" 59 | ] 60 | }, 61 | "peerDependecies": { 62 | "vue": "^3.0.0" 63 | }, 64 | "dependencies": { 65 | "clipboard": "^2.0.0" 66 | }, 67 | "devDependencies": { 68 | "@microsoft/api-extractor": "^7.12.0", 69 | "@rollup/plugin-alias": "^3.1.1", 70 | "@rollup/plugin-commonjs": "^16.0.0", 71 | "@rollup/plugin-node-resolve": "^10.0.0", 72 | "@rollup/plugin-replace": "^2.3.3", 73 | "@size-limit/preset-small-lib": "^4.9.0", 74 | "@types/clipboard": "^2.0.1", 75 | "@types/jest": "^26.0.19", 76 | "@typescript-eslint/eslint-plugin": "^4.11.0", 77 | "@typescript-eslint/parser": "^4.11.0", 78 | "chalk": "^4.1.0", 79 | "eslint": "^7.16.0", 80 | "eslint-config-prettier": "^7.1.0", 81 | "eslint-config-standard": "^16.0.2", 82 | "eslint-plugin-import": ">=2.22.0", 83 | "eslint-plugin-node": ">=11.1.0", 84 | "eslint-plugin-prettier": "^3.3.0", 85 | "eslint-plugin-promise": ">=4.2.0", 86 | "eslint-plugin-standard": "^4.0.2", 87 | "husky": "^4.3.6", 88 | "lint-staged": "^10.5.3", 89 | "prettier": "^2.2.1", 90 | "rollup": "^2.35.1", 91 | "rollup-plugin-terser": "^7.0.2", 92 | "rollup-plugin-typescript2": "^0.29.0", 93 | "typescript": "^4.1.2", 94 | "vite": "^1.0.0-rc.13", 95 | "vue": "^3.0.3", 96 | "vue-jest": "^3.0.7" 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import ts from 'rollup-plugin-typescript2' 3 | import replace from '@rollup/plugin-replace' 4 | import resolve from '@rollup/plugin-node-resolve' 5 | import commonjs from '@rollup/plugin-commonjs' 6 | import pascalcase from 'pascalcase' 7 | 8 | const pkg = require('./package.json') 9 | const name = pkg.name 10 | 11 | function getAuthors(pkg) { 12 | const { contributors, author } = pkg 13 | 14 | const authors = new Set() 15 | if (contributors && contributors) 16 | contributors.forEach((contributor) => { 17 | authors.add(contributor.name) 18 | }) 19 | if (author) authors.add(author.name) 20 | 21 | return Array.from(authors).join(', ') 22 | } 23 | 24 | const banner = `/*! 25 | * ${pkg.name} v${pkg.version} 26 | * (c) ${new Date().getFullYear()} ${getAuthors(pkg)} 27 | * @license MIT 28 | */` 29 | 30 | // ensure TS checks only once for each build 31 | let hasTSChecked = false 32 | 33 | const outputConfigs = { 34 | // each file name has the format: `dist/${name}.${format}.js` 35 | // format being a key of this object 36 | 'esm-bundler': { 37 | file: pkg.module, 38 | format: `es`, 39 | }, 40 | cjs: { 41 | file: pkg.main, 42 | format: `cjs`, 43 | }, 44 | global: { 45 | file: pkg.unpkg, 46 | format: `iife`, 47 | }, 48 | esm: { 49 | file: pkg.browser, 50 | format: `es`, 51 | }, 52 | } 53 | 54 | const allFormats = Object.keys(outputConfigs) 55 | const packageFormats = allFormats 56 | const packageConfigs = packageFormats.map((format) => 57 | createConfig(format, outputConfigs[format]), 58 | ) 59 | 60 | // only add the production ready if we are bundling the options 61 | packageFormats.forEach((format) => { 62 | if (format === 'cjs') { 63 | packageConfigs.push(createProductionConfig(format)) 64 | } else if (format === 'global') { 65 | packageConfigs.push(createMinifiedConfig(format)) 66 | } 67 | }) 68 | 69 | export default packageConfigs 70 | 71 | function createConfig(format, output, plugins = []) { 72 | if (!output) { 73 | console.log(require('chalk').yellow(`invalid format: "${format}"`)) 74 | process.exit(1) 75 | } 76 | 77 | output.sourcemap = !!process.env.SOURCE_MAP 78 | output.banner = banner 79 | output.externalLiveBindings = false 80 | output.globals = { vue: 'Vue' } 81 | 82 | const isProductionBuild = /\.prod\.js$/.test(output.file) 83 | const isGlobalBuild = format === 'global' 84 | const isRawESMBuild = format === 'esm' 85 | const isNodeBuild = format === 'cjs' 86 | const isBundlerESMBuild = /esm-bundler/.test(format) 87 | 88 | if (isGlobalBuild) output.name = pascalcase(pkg.name) 89 | 90 | const shouldEmitDeclarations = !hasTSChecked 91 | 92 | const tsPlugin = ts({ 93 | check: !hasTSChecked, 94 | tsconfig: path.resolve(__dirname, 'tsconfig.json'), 95 | cacheRoot: path.resolve(__dirname, 'node_modules/.rts2_cache'), 96 | tsconfigOverride: { 97 | compilerOptions: { 98 | sourceMap: output.sourcemap, 99 | declaration: shouldEmitDeclarations, 100 | declarationMap: shouldEmitDeclarations, 101 | }, 102 | exclude: ['__tests__', 'test-dts'], 103 | }, 104 | }) 105 | // we only need to check TS and generate declarations once for each build. 106 | // it also seems to run into weird issues when checking multiple times 107 | // during a single build. 108 | hasTSChecked = true 109 | 110 | const external = ['vue'] 111 | 112 | const nodePlugins = [resolve(), commonjs()] 113 | 114 | return { 115 | input: `src/index.ts`, 116 | // Global and Browser ESM builds inlines everything so that they can be 117 | // used alone. 118 | external, 119 | plugins: [ 120 | tsPlugin, 121 | createReplacePlugin( 122 | isProductionBuild, 123 | isBundlerESMBuild, 124 | // isBrowserBuild? 125 | isGlobalBuild || isRawESMBuild || isBundlerESMBuild, 126 | isGlobalBuild, 127 | isNodeBuild, 128 | ), 129 | ...nodePlugins, 130 | ...plugins, 131 | ], 132 | output, 133 | // onwarn: (msg, warn) => { 134 | // if (!/Circular/.test(msg)) { 135 | // warn(msg) 136 | // } 137 | // }, 138 | } 139 | } 140 | 141 | function createReplacePlugin( 142 | isProduction, 143 | isBundlerESMBuild, 144 | isBrowserBuild, 145 | isGlobalBuild, 146 | isNodeBuild, 147 | ) { 148 | const replacements = { 149 | __COMMIT__: `"${process.env.COMMIT}"`, 150 | __VERSION__: `"${pkg.version}"`, 151 | __DEV__: isBundlerESMBuild 152 | ? // preserve to be handled by bundlers 153 | `(process.env.NODE_ENV !== 'production')` 154 | : // hard coded dev/prod builds 155 | !isProduction, 156 | // this is only used during tests 157 | __TEST__: isBundlerESMBuild ? `(process.env.NODE_ENV === 'test')` : false, 158 | // If the build is expected to run directly in the browser (global / esm builds) 159 | __BROWSER__: isBrowserBuild, 160 | // is targeting bundlers? 161 | __BUNDLER__: isBundlerESMBuild, 162 | __GLOBAL__: isGlobalBuild, 163 | // is targeting Node (SSR)? 164 | __NODE_JS__: isNodeBuild, 165 | } 166 | // allow inline overrides like 167 | //__RUNTIME_COMPILE__=true yarn build 168 | Object.keys(replacements).forEach((key) => { 169 | if (key in process.env) { 170 | replacements[key] = process.env[key] 171 | } 172 | }) 173 | return replace(replacements) 174 | } 175 | 176 | function createProductionConfig(format) { 177 | return createConfig(format, { 178 | file: `dist/${name}.${format}.prod.js`, 179 | format: outputConfigs[format].format, 180 | }) 181 | } 182 | 183 | function createMinifiedConfig(format) { 184 | const { terser } = require('rollup-plugin-terser') 185 | return createConfig( 186 | format, 187 | { 188 | file: `dist/${name}.${format}.prod.js`, 189 | format: outputConfigs[format].format, 190 | }, 191 | [ 192 | terser({ 193 | module: /^esm/.test(format), 194 | compress: { 195 | ecma: 2015, 196 | pure_getters: true, 197 | }, 198 | }), 199 | ], 200 | ) 201 | } 202 | -------------------------------------------------------------------------------- /src/@types/index.ts: -------------------------------------------------------------------------------- 1 | export type Actions = 'copy' | 'cut' 2 | 3 | export interface ClipboardConfig { 4 | autoSetContainer: boolean 5 | appendToBody: boolean 6 | } 7 | -------------------------------------------------------------------------------- /src/VueClipboard.ts: -------------------------------------------------------------------------------- 1 | import { App } from 'vue' 2 | 3 | import { Actions, ClipboardConfig } from './@types' 4 | import ClipboardJS from 'clipboard' 5 | 6 | export interface IVueClipboard { 7 | /** 8 | * Set global config 9 | */ 10 | config(config: ClipboardConfig): void 11 | 12 | /** 13 | * Copy string to clipboard 14 | */ 15 | toClipboard(text: string, action: Actions): Promise 16 | 17 | install(app: App): void 18 | } 19 | 20 | const defaultConfig = { 21 | autoSetContainer: false, 22 | appendToBody: true, 23 | } 24 | 25 | export const VueClipboard: IVueClipboard = { 26 | config: (options: ClipboardConfig) => { 27 | const { autoSetContainer, appendToBody } = options 28 | 29 | defaultConfig.autoSetContainer = autoSetContainer ? autoSetContainer : false 30 | defaultConfig.appendToBody = appendToBody ? appendToBody : true 31 | }, 32 | install: (app: App) => { 33 | app.config.globalProperties.$vclipboard = toClipboard 34 | 35 | app.directive('clipboard', { 36 | beforeMount(el, binding) { 37 | if (binding.arg === 'success') { 38 | el._vClipboard_success = binding.value 39 | } else if (binding.arg === 'error') { 40 | el._vClipboard_error = binding.value 41 | } else { 42 | const clipboard = new ClipboardJS(el, { 43 | text: () => binding.value, 44 | action: () => { 45 | return binding.arg === 'cut' ? 'cut' : 'copy' 46 | }, 47 | container: defaultConfig.autoSetContainer ? el : undefined, 48 | }) 49 | clipboard.on('success', (e) => { 50 | const callback = el._vClipboard_success 51 | callback && callback(e) 52 | }) 53 | clipboard.on('error', (e) => { 54 | const callback = el._vClipboard_error 55 | callback && callback(e) 56 | }) 57 | el._vClipboard = clipboard 58 | } 59 | }, 60 | updated(el, binding) { 61 | if (binding.arg === 'success') { 62 | el._vClipboard_success = binding.value 63 | } else if (binding.arg === 'error') { 64 | el._vClipboard_error = binding.value 65 | } else { 66 | el._vClipboard.text = () => { 67 | return binding.value 68 | } 69 | el._vClipboard.action = () => { 70 | return binding.arg === 'cut' ? 'cut' : 'copy' 71 | } 72 | } 73 | }, 74 | unmounted(el, binding) { 75 | if (binding.arg === 'success') { 76 | delete el._vClipboard_success 77 | } else if (binding.arg === 'error') { 78 | delete el._vClipboard_error 79 | } else { 80 | el._vClipboard.destroy() 81 | delete el._vClipboard 82 | } 83 | }, 84 | }) 85 | }, 86 | toClipboard: (text: string, action: Actions) => toClipboard(text, action), 87 | } 88 | 89 | export const toClipboard = ( 90 | text: string, 91 | action: Actions = 'copy', 92 | ): Promise => { 93 | return new Promise((resolve, reject) => { 94 | const fakeElement = document.createElement('button') 95 | const clipboard = new ClipboardJS(fakeElement, { 96 | text: () => text, 97 | action: () => action, 98 | }) 99 | 100 | clipboard.on('success', (e) => { 101 | clipboard.destroy() 102 | resolve(e) 103 | }) 104 | 105 | clipboard.on('error', (e) => { 106 | clipboard.destroy() 107 | reject(e) 108 | }) 109 | 110 | if (defaultConfig.appendToBody) { 111 | document.body.appendChild(fakeElement) 112 | } 113 | 114 | fakeElement.click() 115 | if (defaultConfig.appendToBody) { 116 | document.body.removeChild(fakeElement) 117 | } 118 | }) 119 | } 120 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import type { IVueClipboard } from './VueClipboard' 2 | declare module '@vue/runtime-core' { 3 | interface ComponentCustomProperties { 4 | $vclipboard: IVueClipboard 5 | } 6 | } 7 | 8 | export { VueClipboard, toClipboard } from './VueClipboard' 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src/**/*.ts", "__tests__/**/*.ts"], 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "rootDir": ".", 6 | "outDir": "dist", 7 | "sourceMap": false, 8 | "noEmit": true, 9 | 10 | "target": "esnext", 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "allowJs": false, 14 | 15 | "noUnusedLocals": true, 16 | "strictNullChecks": true, 17 | "noImplicitAny": true, 18 | "noImplicitThis": true, 19 | "noImplicitReturns": true, 20 | "strict": true, 21 | "isolatedModules": false, 22 | "allowSyntheticDefaultImports": true, 23 | "experimentalDecorators": true, 24 | "resolveJsonModule": true, 25 | "esModuleInterop": true, 26 | "removeComments": false, 27 | "jsx": "preserve", 28 | "lib": ["esnext", "dom"], 29 | "types": ["jest", "clipboard", "node"] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | root: 'demo', 5 | outDir: 'demo_dist', 6 | alias: { 7 | '/@/': path.resolve(__dirname, 'src'), 8 | }, 9 | optimizeDeps: { 10 | // include: [''], 11 | }, 12 | } 13 | --------------------------------------------------------------------------------