├── .github
├── FUNDING.yml
└── workflows
│ └── ci.yml
├── index.d.ts
├── .gitignore
├── dist
├── copy-to-clipboard.umd.min.js
├── copy-to-clipboard.esm.js
├── copy-to-clipboard.cjs.js
├── copy-to-clipboard.umd.min.js.map
├── copy-to-clipboard.umd.js
└── copy-to-clipboard.umd.js.map
├── LICENSE
├── package.json
├── rollup.config.mjs
├── src
└── main.js
├── test
└── main.test.js
└── README.md
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | ko_fi: jaywcjlove
2 | buy_me_a_coffee: jaywcjlove
3 | custom: ["https://www.paypal.me/kennyiseeyou", "https://jaywcjlove.github.io/#/sponsor"]
4 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 |
2 | interface CopyTextToClipboard {
3 | (text: string, method?: (isCopy: boolean) => void): void
4 | }
5 |
6 | declare var copyTextToClipboard: CopyTextToClipboard
7 | export default copyTextToClipboard;
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | dist
3 | lib
4 | cjs
5 | esm
6 | node_modules
7 | coverage
8 | npm-debug.log*
9 | package-lock.json
10 |
11 | .eslintcache
12 | .DS_Store
13 | .cache
14 | .rdoc-dist
15 |
16 | *.log
17 | *.bak
18 | *.tem
19 | *.temp
20 | #.swp
21 | *.*~
22 | ~*.*
23 |
--------------------------------------------------------------------------------
/dist/copy-to-clipboard.umd.min.js:
--------------------------------------------------------------------------------
1 | /*! @uiw/copy-to-clipboard v1.0.15 | MIT © 2023 Kenny Wang https://uiwjs.github.io/copy-to-clipboard */
2 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).copyTextToClipboard=t()}(this,(function(){"use strict";return function(e,t){if("undefined"==typeof document)return;const o=document.createElement("textarea");o.value=e,o.setAttribute("readonly",""),o.style={position:"absolute",left:"-9999px"},document.body.appendChild(o);const n=document.getSelection().rangeCount>0&&document.getSelection().getRangeAt(0);o.select();let d=!1;try{d=!!document.execCommand("copy")}catch(e){d=!1}document.body.removeChild(o),n&&document.getSelection&&(document.getSelection().removeAllRanges(),document.getSelection().addRange(n)),t&&t(d)}}));
3 | //# sourceMappingURL=copy-to-clipboard.umd.min.js.map
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 uiw
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 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@uiw/copy-to-clipboard",
3 | "version": "1.0.19",
4 | "description": "Copy to clipboard.",
5 | "main": "dist/copy-to-clipboard.cjs.js",
6 | "module": "dist/copy-to-clipboard.esm.js",
7 | "browser": "dist/copy-to-clipboard.umd.js",
8 | "homepage": "https://uiwjs.github.io/copy-to-clipboard",
9 | "funding": "https://jaywcjlove.github.io/#/sponsor",
10 | "types": "index.d.ts",
11 | "scripts": {
12 | "test": "tsbb test",
13 | "coverage": "tsbb test --coverage --bail",
14 | "build": "rollup -c rollup.config.mjs",
15 | "dev": "rollup -c rollup.config.mjs -w"
16 | },
17 | "files": [
18 | "index.d.ts",
19 | "dist",
20 | "README.md"
21 | ],
22 | "keywords": [
23 | "copy",
24 | "text",
25 | "clipboard",
26 | "browser",
27 | "clipboard-copy",
28 | "clipboard.js"
29 | ],
30 | "author": {
31 | "name": "Kenny Wang",
32 | "email": "wowohoo@qq.com",
33 | "url": "https://wangchujiang.com"
34 | },
35 | "repository": {
36 | "type": "git",
37 | "url": "https://github.com/uiwjs/copy-to-clipboard.git"
38 | },
39 | "license": "MIT",
40 | "devDependencies": {
41 | "@rollup/plugin-commonjs": "^24.1.0",
42 | "@rollup/plugin-node-resolve": "^15.0.2",
43 | "@rollup/plugin-terser": "^0.4.1",
44 | "bannerjs": "^3.0.1",
45 | "rollup": "^3.20.2",
46 | "tsbb": "^4.2.3"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import { createRequire } from 'module';
2 | import commonjs from '@rollup/plugin-commonjs';
3 | import { nodeResolve } from '@rollup/plugin-node-resolve';
4 | import terser from '@rollup/plugin-terser';
5 | import * as banner from 'bannerjs';
6 |
7 | const require = createRequire(import.meta.url);
8 | const pkg = require('./package.json');
9 |
10 | export default [
11 | // browser-friendly UMD build
12 | {
13 | input: 'src/main.js',
14 | output: {
15 | name: 'copyTextToClipboard',
16 | file: pkg.browser,
17 | format: 'umd',
18 | banner: banner.multibanner(),
19 | sourcemap: true,
20 | },
21 | plugins: [
22 | nodeResolve(), // so Rollup can find `ms`
23 | commonjs() // so Rollup can convert `ms` to an ES module
24 | ]
25 | },
26 | {
27 | input: 'src/main.js',
28 | output: {
29 | file: 'dist/copy-to-clipboard.umd.min.js',
30 | format: 'umd',
31 | name: 'copyTextToClipboard',
32 | banner: banner.onebanner(),
33 | sourcemap: true
34 | },
35 | plugins: [
36 | nodeResolve(), // so Rollup can find `ms`
37 | commonjs(), // so Rollup can convert `ms` to an ES module
38 | terser(),
39 | ]
40 | },
41 | // CommonJS (for Node) and ES module (for bundlers) build.
42 | // (We could have three entries in the configuration array
43 | // instead of two, but it's quicker to generate multiple
44 | // builds from a single configuration where possible, using
45 | // an array for the `output` option, where we can specify
46 | // `file` and `format` for each target)
47 | {
48 | input: 'src/main.js',
49 | external: ['ms'],
50 | output: [
51 | // { file: pkg.main, format: 'umd', banner: banner.multibanner() },
52 | { file: pkg.main, format: 'cjs', banner: banner.multibanner(), exports: 'auto' },
53 | { file: pkg.module, format: 'es', banner: banner.multibanner(), exports: 'auto' }
54 | ]
55 | }
56 | ];
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * *** This styling is an extra step which is likely not required. ***
3 | * https://github.com/w3c/clipboard-apis/blob/master/explainer.adoc#writing-to-the-clipboard
4 | *
5 | * Why is it here? To ensure:
6 | *
7 | * 1. the element is able to have focus and selection.
8 | * 2. if element was to flash render it has minimal visual impact.
9 | * 3. less flakyness with selection and copying which **might** occur if
10 | * the textarea element is not visible.
11 | *
12 | * The likelihood is the element won't even render, not even a flash,
13 | * so some of these are just precautions. However in IE the element
14 | * is visible whilst the popup box asking the user for permission for
15 | * the web page to copy to the clipboard.
16 | *
17 | * Place in top-left corner of screen regardless of scroll position.
18 | *
19 | * @typedef CopyTextToClipboard
20 | * @property {(text: string, method?: (isCopy: boolean) => void) => void} void
21 | * @returns {void}
22 | *
23 | * @param {string} text
24 | * @param {CopyTextToClipboard} cb
25 | */
26 | export default function copyTextToClipboard(text, cb) {
27 | if (typeof document === "undefined") return;
28 | const el = document.createElement('textarea');
29 | el.value = text;
30 | el.setAttribute('readonly', '');
31 | el.style = {
32 | position: 'absolute',
33 | left: '-9999px',
34 | }
35 | document.body.appendChild(el);
36 | const selected = document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false;
37 | el.select();
38 | let isCopy = false;
39 | try {
40 | const successful = document.execCommand('copy');
41 | isCopy = !!successful;
42 | } catch (err) {
43 | isCopy = false;
44 | }
45 | document.body.removeChild(el);
46 | if (selected && document.getSelection) {
47 | document.getSelection().removeAllRanges();
48 | document.getSelection().addRange(selected);
49 | }
50 | cb && cb(isCopy);
51 | };
52 |
--------------------------------------------------------------------------------
/dist/copy-to-clipboard.esm.js:
--------------------------------------------------------------------------------
1 | /**!
2 | * @uiw/copy-to-clipboard v1.0.15
3 | * Copy to clipboard.
4 | *
5 | * Copyright (c) 2023 Kenny Wang
6 | * https://github.com/uiwjs/copy-to-clipboard.git
7 | *
8 | * @website: https://uiwjs.github.io/copy-to-clipboard
9 |
10 | * Licensed under the MIT license
11 | */
12 |
13 | /**
14 | * *** This styling is an extra step which is likely not required. ***
15 | * https://github.com/w3c/clipboard-apis/blob/master/explainer.adoc#writing-to-the-clipboard
16 | *
17 | * Why is it here? To ensure:
18 | *
19 | * 1. the element is able to have focus and selection.
20 | * 2. if element was to flash render it has minimal visual impact.
21 | * 3. less flakyness with selection and copying which **might** occur if
22 | * the textarea element is not visible.
23 | *
24 | * The likelihood is the element won't even render, not even a flash,
25 | * so some of these are just precautions. However in IE the element
26 | * is visible whilst the popup box asking the user for permission for
27 | * the web page to copy to the clipboard.
28 | *
29 | * Place in top-left corner of screen regardless of scroll position.
30 | *
31 | * @typedef CopyTextToClipboard
32 | * @property {(text: string, method?: (isCopy: boolean) => void) => void} void
33 | * @returns {void}
34 | *
35 | * @param {string} text
36 | * @param {CopyTextToClipboard} cb
37 | */
38 | function copyTextToClipboard(text, cb) {
39 | if (typeof document === "undefined") return;
40 | const el = document.createElement('textarea');
41 | el.value = text;
42 | el.setAttribute('readonly', '');
43 | el.style = {
44 | position: 'absolute',
45 | left: '-9999px',
46 | };
47 | document.body.appendChild(el);
48 | const selected = document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false;
49 | el.select();
50 | let isCopy = false;
51 | try {
52 | const successful = document.execCommand('copy');
53 | isCopy = !!successful;
54 | } catch (err) {
55 | isCopy = false;
56 | }
57 | document.body.removeChild(el);
58 | if (selected && document.getSelection) {
59 | document.getSelection().removeAllRanges();
60 | document.getSelection().addRange(selected);
61 | }
62 | cb && cb(isCopy);
63 | }
64 |
65 | export { copyTextToClipboard as default };
66 |
--------------------------------------------------------------------------------
/dist/copy-to-clipboard.cjs.js:
--------------------------------------------------------------------------------
1 | /**!
2 | * @uiw/copy-to-clipboard v1.0.15
3 | * Copy to clipboard.
4 | *
5 | * Copyright (c) 2023 Kenny Wang
6 | * https://github.com/uiwjs/copy-to-clipboard.git
7 | *
8 | * @website: https://uiwjs.github.io/copy-to-clipboard
9 |
10 | * Licensed under the MIT license
11 | */
12 |
13 | 'use strict';
14 |
15 | /**
16 | * *** This styling is an extra step which is likely not required. ***
17 | * https://github.com/w3c/clipboard-apis/blob/master/explainer.adoc#writing-to-the-clipboard
18 | *
19 | * Why is it here? To ensure:
20 | *
21 | * 1. the element is able to have focus and selection.
22 | * 2. if element was to flash render it has minimal visual impact.
23 | * 3. less flakyness with selection and copying which **might** occur if
24 | * the textarea element is not visible.
25 | *
26 | * The likelihood is the element won't even render, not even a flash,
27 | * so some of these are just precautions. However in IE the element
28 | * is visible whilst the popup box asking the user for permission for
29 | * the web page to copy to the clipboard.
30 | *
31 | * Place in top-left corner of screen regardless of scroll position.
32 | *
33 | * @typedef CopyTextToClipboard
34 | * @property {(text: string, method?: (isCopy: boolean) => void) => void} void
35 | * @returns {void}
36 | *
37 | * @param {string} text
38 | * @param {CopyTextToClipboard} cb
39 | */
40 | function copyTextToClipboard(text, cb) {
41 | if (typeof document === "undefined") return;
42 | const el = document.createElement('textarea');
43 | el.value = text;
44 | el.setAttribute('readonly', '');
45 | el.style = {
46 | position: 'absolute',
47 | left: '-9999px',
48 | };
49 | document.body.appendChild(el);
50 | const selected = document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false;
51 | el.select();
52 | let isCopy = false;
53 | try {
54 | const successful = document.execCommand('copy');
55 | isCopy = !!successful;
56 | } catch (err) {
57 | isCopy = false;
58 | }
59 | document.body.removeChild(el);
60 | if (selected && document.getSelection) {
61 | document.getSelection().removeAllRanges();
62 | document.getSelection().addRange(selected);
63 | }
64 | cb && cb(isCopy);
65 | }
66 |
67 | module.exports = copyTextToClipboard;
68 |
--------------------------------------------------------------------------------
/dist/copy-to-clipboard.umd.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"copy-to-clipboard.umd.min.js","sources":["../src/main.js"],"sourcesContent":["/**\n * *** This styling is an extra step which is likely not required. ***\n * https://github.com/w3c/clipboard-apis/blob/master/explainer.adoc#writing-to-the-clipboard\n * \n * Why is it here? To ensure:\n * \n * 1. the element is able to have focus and selection.\n * 2. if element was to flash render it has minimal visual impact.\n * 3. less flakyness with selection and copying which **might** occur if\n * the textarea element is not visible.\n *\n * The likelihood is the element won't even render, not even a flash,\n * so some of these are just precautions. However in IE the element\n * is visible whilst the popup box asking the user for permission for\n * the web page to copy to the clipboard.\n * \n * Place in top-left corner of screen regardless of scroll position.\n *\n * @typedef CopyTextToClipboard\n * @property {(text: string, method?: (isCopy: boolean) => void) => void} void\n * @returns {void}\n * \n * @param {string} text \n * @param {CopyTextToClipboard} cb \n */\nexport default function copyTextToClipboard(text, cb) {\n if (typeof document === \"undefined\") return;\n const el = document.createElement('textarea');\n el.value = text;\n el.setAttribute('readonly', '');\n el.style = {\n position: 'absolute',\n left: '-9999px',\n }\n document.body.appendChild(el);\n const selected = document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false;\n el.select();\n let isCopy = false;\n try {\n const successful = document.execCommand('copy');\n isCopy = !!successful;\n } catch (err) {\n isCopy = false;\n }\n document.body.removeChild(el);\n if (selected && document.getSelection) {\n document.getSelection().removeAllRanges();\n document.getSelection().addRange(selected);\n }\n cb && cb(isCopy);\n};\n"],"names":["text","cb","document","el","createElement","value","setAttribute","style","position","left","body","appendChild","selected","getSelection","rangeCount","getRangeAt","select","isCopy","execCommand","err","removeChild","removeAllRanges","addRange"],"mappings":";2PAyBe,SAA6BA,EAAMC,GAChD,GAAwB,oBAAbC,SAA0B,OACrC,MAAMC,EAAKD,SAASE,cAAc,YAClCD,EAAGE,MAAQL,EACXG,EAAGG,aAAa,WAAY,IAC5BH,EAAGI,MAAQ,CACTC,SAAU,WACVC,KAAM,WAERP,SAASQ,KAAKC,YAAYR,GAC1B,MAAMS,EAAWV,SAASW,eAAeC,WAAa,GAAIZ,SAASW,eAAeE,WAAW,GAC7FZ,EAAGa,SACH,IAAIC,GAAS,EACb,IAEEA,IADmBf,SAASgB,YAAY,OAEzC,CAAC,MAAOC,GACPF,GAAS,CACV,CACDf,SAASQ,KAAKU,YAAYjB,GACtBS,GAAYV,SAASW,eACvBX,SAASW,eAAeQ,kBACxBnB,SAASW,eAAeS,SAASV,IAEnCX,GAAMA,EAAGgB,EACX"}
--------------------------------------------------------------------------------
/test/main.test.js:
--------------------------------------------------------------------------------
1 | import copyTextToClipboard from '../src/main';
2 |
3 | it('should handle document not available', () => {
4 | const text = 'Hello, world!';
5 | const mockCb = jest.fn();
6 | // Create a spy to replace the document object
7 | const documentSpy = jest.spyOn(global, 'document', 'get');
8 | documentSpy.mockImplementation(() => undefined);
9 | copyTextToClipboard(text, mockCb);
10 | expect(mockCb).not.toHaveBeenCalled();
11 | // Restore the original document object
12 | documentSpy.mockRestore();
13 | });
14 |
15 | test('copyTextToClipboard should copy the text to the clipboard and return true', () => {
16 | const text = 'Hello, world!';
17 | const copied = jest.fn();
18 | document.execCommand = jest.fn(() => true);
19 | const cb = isCopy => {
20 | copied(isCopy);
21 | expect(isCopy).toBe(true);
22 | };
23 |
24 | copyTextToClipboard(text, cb);
25 |
26 | expect(copied).toHaveBeenCalledWith(true);
27 | });
28 |
29 | test('copyTextToClipboard should not copy the text to the clipboard and return false', () => {
30 | const text = 'Hello, world!';
31 | const copied = jest.fn();
32 | const cb = isCopy => {
33 | copied(isCopy);
34 | expect(isCopy).toBe(false);
35 | };
36 |
37 | // Simulating an error during `document.execCommand('copy')`
38 | document.execCommand = jest.fn(() => {
39 | throw new Error('Copy failed');
40 | });
41 |
42 | copyTextToClipboard(text, cb);
43 |
44 | expect(copied).toHaveBeenCalledWith(false);
45 | });
46 |
47 |
48 | it('should copy text to clipboard', () => {
49 | let mockCb = jest.fn();
50 | const text = 'Hello, world!';
51 | const mockRange = {
52 | commonAncestorContainer: 'mockContainer',
53 | startContainer: 'mockContainer',
54 | endContainer: 'mockContainer',
55 | startOffset: 0,
56 | endOffset: 0,
57 | };
58 |
59 | document.execCommand = jest.fn(() => true);
60 | document.getSelection = jest.fn(() => ({
61 | rangeCount: 1,
62 | getRangeAt: jest.fn(() => mockRange),
63 | removeAllRanges: jest.fn(),
64 | addRange: jest.fn(),
65 | }));
66 | document.createElement = jest.fn(() => ({
67 | value: '',
68 | setAttribute: jest.fn(),
69 | select: jest.fn(),
70 | }));
71 | document.body.appendChild = jest.fn();
72 | document.body.removeChild = jest.fn();
73 |
74 | copyTextToClipboard(text, mockCb);
75 |
76 | expect(document.createElement).toHaveBeenCalledWith('textarea');
77 | expect(document.body.appendChild).toHaveBeenCalled();
78 | expect(document.execCommand).toHaveBeenCalledWith('copy');
79 | expect(document.body.removeChild).toHaveBeenCalled();
80 | expect(mockCb).toHaveBeenCalledWith(true);
81 | });
82 |
--------------------------------------------------------------------------------
/dist/copy-to-clipboard.umd.js:
--------------------------------------------------------------------------------
1 | /**!
2 | * @uiw/copy-to-clipboard v1.0.15
3 | * Copy to clipboard.
4 | *
5 | * Copyright (c) 2023 Kenny Wang
6 | * https://github.com/uiwjs/copy-to-clipboard.git
7 | *
8 | * @website: https://uiwjs.github.io/copy-to-clipboard
9 |
10 | * Licensed under the MIT license
11 | */
12 |
13 | (function (global, factory) {
14 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
15 | typeof define === 'function' && define.amd ? define(factory) :
16 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.copyTextToClipboard = factory());
17 | })(this, (function () { 'use strict';
18 |
19 | /**
20 | * *** This styling is an extra step which is likely not required. ***
21 | * https://github.com/w3c/clipboard-apis/blob/master/explainer.adoc#writing-to-the-clipboard
22 | *
23 | * Why is it here? To ensure:
24 | *
25 | * 1. the element is able to have focus and selection.
26 | * 2. if element was to flash render it has minimal visual impact.
27 | * 3. less flakyness with selection and copying which **might** occur if
28 | * the textarea element is not visible.
29 | *
30 | * The likelihood is the element won't even render, not even a flash,
31 | * so some of these are just precautions. However in IE the element
32 | * is visible whilst the popup box asking the user for permission for
33 | * the web page to copy to the clipboard.
34 | *
35 | * Place in top-left corner of screen regardless of scroll position.
36 | *
37 | * @typedef CopyTextToClipboard
38 | * @property {(text: string, method?: (isCopy: boolean) => void) => void} void
39 | * @returns {void}
40 | *
41 | * @param {string} text
42 | * @param {CopyTextToClipboard} cb
43 | */
44 | function copyTextToClipboard(text, cb) {
45 | if (typeof document === "undefined") return;
46 | const el = document.createElement('textarea');
47 | el.value = text;
48 | el.setAttribute('readonly', '');
49 | el.style = {
50 | position: 'absolute',
51 | left: '-9999px',
52 | };
53 | document.body.appendChild(el);
54 | const selected = document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false;
55 | el.select();
56 | let isCopy = false;
57 | try {
58 | const successful = document.execCommand('copy');
59 | isCopy = !!successful;
60 | } catch (err) {
61 | isCopy = false;
62 | }
63 | document.body.removeChild(el);
64 | if (selected && document.getSelection) {
65 | document.getSelection().removeAllRanges();
66 | document.getSelection().addRange(selected);
67 | }
68 | cb && cb(isCopy);
69 | }
70 |
71 | return copyTextToClipboard;
72 |
73 | }));
74 | //# sourceMappingURL=copy-to-clipboard.umd.js.map
75 |
--------------------------------------------------------------------------------
/dist/copy-to-clipboard.umd.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"copy-to-clipboard.umd.js","sources":["../src/main.js"],"sourcesContent":["/**\n * *** This styling is an extra step which is likely not required. ***\n * https://github.com/w3c/clipboard-apis/blob/master/explainer.adoc#writing-to-the-clipboard\n * \n * Why is it here? To ensure:\n * \n * 1. the element is able to have focus and selection.\n * 2. if element was to flash render it has minimal visual impact.\n * 3. less flakyness with selection and copying which **might** occur if\n * the textarea element is not visible.\n *\n * The likelihood is the element won't even render, not even a flash,\n * so some of these are just precautions. However in IE the element\n * is visible whilst the popup box asking the user for permission for\n * the web page to copy to the clipboard.\n * \n * Place in top-left corner of screen regardless of scroll position.\n *\n * @typedef CopyTextToClipboard\n * @property {(text: string, method?: (isCopy: boolean) => void) => void} void\n * @returns {void}\n * \n * @param {string} text \n * @param {CopyTextToClipboard} cb \n */\nexport default function copyTextToClipboard(text, cb) {\n if (typeof document === \"undefined\") return;\n const el = document.createElement('textarea');\n el.value = text;\n el.setAttribute('readonly', '');\n el.style = {\n position: 'absolute',\n left: '-9999px',\n }\n document.body.appendChild(el);\n const selected = document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false;\n el.select();\n let isCopy = false;\n try {\n const successful = document.execCommand('copy');\n isCopy = !!successful;\n } catch (err) {\n isCopy = false;\n }\n document.body.removeChild(el);\n if (selected && document.getSelection) {\n document.getSelection().removeAllRanges();\n document.getSelection().addRange(selected);\n }\n cb && cb(isCopy);\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACe,SAAS,mBAAmB,CAAC,IAAI,EAAE,EAAE,EAAE;EACtD,EAAE,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,OAAO;EAC9C,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;EAChD,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC;EAClB,EAAE,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;EAClC,EAAE,EAAE,CAAC,KAAK,GAAG;EACb,IAAI,QAAQ,EAAE,UAAU;EACxB,IAAI,IAAI,EAAE,SAAS;EACnB,IAAG;EACH,EAAE,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;EAChC,EAAE,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,EAAE,CAAC,UAAU,GAAG,CAAC,GAAG,QAAQ,CAAC,YAAY,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;EAC1G,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC;EACd,EAAE,IAAI,MAAM,GAAG,KAAK,CAAC;EACrB,EAAE,IAAI;EACN,IAAI,MAAM,UAAU,GAAG,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;EACpD,IAAI,MAAM,GAAG,CAAC,CAAC,UAAU,CAAC;EAC1B,GAAG,CAAC,OAAO,GAAG,EAAE;EAChB,IAAI,MAAM,GAAG,KAAK,CAAC;EACnB,GAAG;EACH,EAAE,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;EAChC,EAAE,IAAI,QAAQ,IAAI,QAAQ,CAAC,YAAY,EAAE;EACzC,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC,eAAe,EAAE,CAAC;EAC9C,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;EAC/C,GAAG;EACH,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC;EACnB;;;;;;;;"}
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 | on:
3 | push:
4 | branches:
5 | - master
6 | jobs:
7 | build-deploy:
8 | runs-on: ubuntu-latest
9 | permissions:
10 | contents: write
11 | id-token: write
12 | steps:
13 | - uses: actions/checkout@v6
14 | - uses: actions/setup-node@v6
15 | with:
16 | node-version: 24
17 | registry-url: 'https://registry.npmjs.org'
18 |
19 | - run: npm install
20 | - run: npm run build
21 | - run: npm run coverage
22 |
23 | - name: Create idoc config.
24 | run: |
25 | cat > idoc.yml << EOF
26 | site: "Copy Text to Clipboard {{version}}"
27 | description: Copy Text to Clipboard
28 | openSource: https://github.com/jaywcjlove/copy-to-clipboard
29 | homepage: https://wangchujiang.com/copy-to-clipboard/
30 | tocs: false
31 | element:
32 | wrapper: style=max-width:720px;
33 | meta:
34 | -
35 | -
36 | -
37 | -
38 | -
39 | -
40 | -
41 | -
42 | -
43 | -
44 | menus:
45 | Home: index.html
46 | Apps:
47 | url: https://wangchujiang.com/#/app
48 | target: __blank
49 | Sponsor:
50 | url: https://jaywcjlove.github.io/#/sponsor
51 | target: __blank
52 | footer: |
53 | App •
54 | Projects •
55 | Sponsor •
56 | More Apps
57 | Generated by idoc v{{idocVersion}}
58 | EOF
59 |
60 | - run: npm install idoc@1 -g
61 | - run: idoc
62 |
63 | - name: Create Coverage Badges
64 | uses: jaywcjlove/coverage-badges-cli@main
65 | with:
66 | output: dist/badges.svg
67 |
68 | - name: Generate Contributors Images
69 | uses: jaywcjlove/github-action-contributors@main
70 | with:
71 | filter-author: (renovate\[bot\]|renovate-bot|dependabot\[bot\])
72 | output: dist/CONTRIBUTORS.svg
73 | avatarSize: 42
74 |
75 | - run: cp -rp coverage/lcov-report dist
76 |
77 | - name: Deploy
78 | uses: peaceiris/actions-gh-pages@v4
79 | with:
80 | github_token: ${{ secrets.GITHUB_TOKEN }}
81 | publish_dir: ./dist
82 |
83 | - name: Create Tag
84 | id: create_tag
85 | uses: jaywcjlove/create-tag-action@main
86 | with:
87 | token: ${{ secrets.GITHUB_TOKEN }}
88 | package-path: ./package.json
89 |
90 | - name: Generate Changelog
91 | id: changelog
92 | uses: jaywcjlove/changelog-generator@main
93 | with:
94 | token: ${{ secrets.GITHUB_TOKEN }}
95 | head-ref: ${{steps.create_tag.outputs.version}}
96 | filter-author: (jaywcjlove|小弟调调™|dependabot\[bot\]|Renovate Bot)
97 | filter: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}'
98 |
99 | - name: Create Release
100 | uses: ncipollo/release-action@v1
101 | if: steps.create_tag.outputs.successful
102 | with:
103 | allowUpdates: true
104 | token: ${{ secrets.GITHUB_TOKEN }}
105 | name: ${{ steps.create_tag.outputs.version }}
106 | tag: ${{ steps.create_tag.outputs.version }}
107 | body: |
108 | [](https://uiwjs.github.io/npm-unpkg/#/pkg/@uiw/copy-to-clipboard@${{steps.create_tag.outputs.versionNumber}}/file/README.md)
109 |
110 | ```bash
111 | npm i @uiw/copy-to-clipboard@${{steps.create_tag.outputs.versionNumber}}
112 | ```
113 |
114 | ${{ steps.changelog.outputs.compareurl }}
115 |
116 | ${{ steps.changelog.outputs.changelog }}
117 |
118 | # - run: npm install @jsdevtools/npm-publish -g
119 | # - run: npm-publish --token="${{ secrets.NPM_TOKEN }}" ./package.json
120 | - name: package.json info
121 | uses: jaywcjlove/github-action-package@main
122 | with:
123 | unset: scripts,devDependencies
124 |
125 | # npm@v11.5.0+ is required for OIDC support
126 | - name: Upgrade npm for OIDC support
127 | run: npm install -g npm@latest
128 | # node@v22.0.0+
129 | # https://gist.github.com/jaywcjlove/a178278521a6f72c74525d3f1d9c4bf9
130 | - run: NODE_AUTH_TOKEN="" npm publish --access public --provenance
131 | name: 📦 @uiw/copy-to-clipboard publish to NPM
132 | continue-on-error: true
133 |
134 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |