From 6dc9110ab098fe0f9c44934c4da2aa1a69d17156 Mon Sep 17 00:00:00 2001 From: scottcampbell Date: Thu, 4 Apr 2019 14:49:02 -0400 Subject: [PATCH 1/8] added md story and ts useCopyToClipboard files. --- docs/useCopyToClipboard.md | 0 src/__stories__/useCopyToClipboard.tsx | 0 src/index.ts | 2 ++ src/useCopyToClipboard.ts | 35 ++++++++++++++++++++++++++ 4 files changed, 37 insertions(+) create mode 100644 docs/useCopyToClipboard.md create mode 100644 src/__stories__/useCopyToClipboard.tsx create mode 100644 src/useCopyToClipboard.ts diff --git a/docs/useCopyToClipboard.md b/docs/useCopyToClipboard.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/__stories__/useCopyToClipboard.tsx b/src/__stories__/useCopyToClipboard.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/index.ts b/src/index.ts index 9b8f364024..af714b87ba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ import useAsyncRetry from './useAsyncRetry'; import useAudio from './useAudio'; import useBattery from './useBattery'; import useBoolean from './useBoolean'; +import useCopyToClipboard from './useCopyToClipboard'; import useDrop from './useDrop'; import useDropArea from './useDropArea'; import useCounter from './useCounter'; @@ -72,6 +73,7 @@ export { useAudio, useBattery, useBoolean, + useCopyToClipboard, useDrop, useDropArea, useClickAway, diff --git a/src/useCopyToClipboard.ts b/src/useCopyToClipboard.ts new file mode 100644 index 0000000000..649c1dc07e --- /dev/null +++ b/src/useCopyToClipboard.ts @@ -0,0 +1,35 @@ +import {useState, useEffect} from 'react'; + +const useCopyToClipboard = (timeout = null) => { + + const [success, setSuccess] = useState(false); + + const copyToClipboard = (text) => { + + if (typeof text == "string" || typeof text == "number" ) { + const element = document.createElement('textarea'); + element.value = text; + document.body.appendChild(element); + element.select(); + document.execCommand('copy'); + document.body.removeChild(element); + setSuccess(true); + } + + else { + setSuccess(false); + console.error(`Cannot copy typeof ${typeof text} to clipboard, must be a valid string or number.`); + } + } + + useEffect( () => { + // if timeout given, set success to false + if (timeout) { + if (success) setTimeout( () => setSuccess(false), timeout); + } + }, [success]) + + return [success, copyToClipboard]; +} + +export default useCopyToClipboard; From dc476e5b94bf229c680668fa04a345e948d31612 Mon Sep 17 00:00:00 2001 From: scottcampbell Date: Fri, 5 Apr 2019 09:43:08 -0400 Subject: [PATCH 2/8] added useCopyToClipboard --- docs/useCopyToClipboard.md | 54 ++++++++++++++++++++ package.json | 3 +- src/__stories__/useCopyToClipboard.story.tsx | 23 +++++++++ src/__stories__/useCopyToClipboard.tsx | 0 src/useCopyToClipboard.ts | 22 +++++--- yarn.lock | 5 ++ 6 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 src/__stories__/useCopyToClipboard.story.tsx delete mode 100644 src/__stories__/useCopyToClipboard.tsx diff --git a/docs/useCopyToClipboard.md b/docs/useCopyToClipboard.md index e69de29bb2..b0df80901a 100644 --- a/docs/useCopyToClipboard.md +++ b/docs/useCopyToClipboard.md @@ -0,0 +1,54 @@ +# `useCopyToClipboard` + +copy text to a users clipboard. + +## Usage + +### basic usage: + +```jsx +import {useCopyToClipboard} from 'react-use'; + +const myComp = (props) => { + const [success, copyToClipboard] = useCopyToClipboard(); + const myText = 'text to be copied'; + return ( + copyToClipboard(myText) }>{myText} + ) +} +``` + +### with timeout: + +```jsx +import {useCopyToClipboard} from 'react-use'; + +const myComp = (props) => { + const [success, copyToClipboard] = useCopyToClipboard(2000); + const myText = 'text to be copied'; + return ( +
+ copyToClipboard(myText) }>{myText} + { success && copied to clipboard} +
+ ) +} +``` + +### with custom polyfill: + +```jsx +import {useCopyToClipboard} from 'react-use'; +import * as clipboard from "clipboard-polyfill" + +const myComp = (props) => { + const [success, copyToClipboard] = useCopyToClipboard(null, clipboard.writeText); + const myText = 'text to be copied'; + return ( +
+ copyToClipboard(myText) }>{myText} + { success && copied to clipboard} +
+ ) +} +``` diff --git a/package.json b/package.json index 048a73c415..8228553131 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ }, "homepage": "https://github.com/streamich/react-use#readme", "dependencies": { + "clipboard-polyfill": "^2.7.0", "nano-css": "^5.1.0", "react-wait": "^0.3.0", "screenfull": "^4.1.0", @@ -41,9 +42,9 @@ "ts-easing": "^0.2.0" }, "peerDependencies": { + "keyboardjs": "*", "react": "^16.8.0", "react-dom": "^16.8.0", - "keyboardjs": "*", "rebound": "*" }, "devDependencies": { diff --git a/src/__stories__/useCopyToClipboard.story.tsx b/src/__stories__/useCopyToClipboard.story.tsx new file mode 100644 index 0000000000..d97437e53e --- /dev/null +++ b/src/__stories__/useCopyToClipboard.story.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import {storiesOf} from '@storybook/react'; +import ShowDocs from './util/ShowDocs'; +import {useCopyToClipboard} from '..'; + +const Demo = () => { + const [success, copyToClipboard] = useCopyToClipboard(2000); + const textToCopy = "text to be copied" + return ( +
+ {textToCopy} + + { success && text copied!} +
+ +
+
+ ) +} + +storiesOf('UI|useCopyToClipboard', module) + .add('Docs', () => ) + .add('Demo', () => ) diff --git a/src/__stories__/useCopyToClipboard.tsx b/src/__stories__/useCopyToClipboard.tsx deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/useCopyToClipboard.ts b/src/useCopyToClipboard.ts index 649c1dc07e..30258061dc 100644 --- a/src/useCopyToClipboard.ts +++ b/src/useCopyToClipboard.ts @@ -1,21 +1,27 @@ import {useState, useEffect} from 'react'; +import * as clipboard from "clipboard-polyfill" -const useCopyToClipboard = (timeout = null) => { +const copyDefault = (text) => { + console.log('run'); + const element = document.createElement('textarea'); // create textarea HTML element + element.value = text; // add the text to be copied to the element + document.body.appendChild(element); // add element to DOM + element.select(); // select the text + document.execCommand('copy'); // execute copy command + document.body.removeChild(element); // remove element from DOM +}; + + +const useCopyToClipboard = (timeout = null, copy = copyDefault) => { const [success, setSuccess] = useState(false); const copyToClipboard = (text) => { if (typeof text == "string" || typeof text == "number" ) { - const element = document.createElement('textarea'); - element.value = text; - document.body.appendChild(element); - element.select(); - document.execCommand('copy'); - document.body.removeChild(element); + copy(text); setSuccess(true); } - else { setSuccess(false); console.error(`Cannot copy typeof ${typeof text} to clipboard, must be a valid string or number.`); diff --git a/yarn.lock b/yarn.lock index 2025b08bdb..f042241f28 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3638,6 +3638,11 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= +clipboard-polyfill@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/clipboard-polyfill/-/clipboard-polyfill-2.7.0.tgz#4f4344bb3405f88609ccf8ee2d445018da0e318f" + integrity sha512-JUQ9Q32xetbWG3e3tdYbP4uB13YeMz79AWg4SiPLVbu4s6pv2q70R8uXDtA8UD3f2BKQeTDD5ApoqRCs29RDlw== + clipboard@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d" From a1d65574947b3257050142fca3aca3bf9acf8595 Mon Sep 17 00:00:00 2001 From: scottcampbell Date: Fri, 5 Apr 2019 09:52:10 -0400 Subject: [PATCH 3/8] fixed typescript error. --- docs/useCopyToClipboard.md | 2 +- src/useCopyToClipboard.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/useCopyToClipboard.md b/docs/useCopyToClipboard.md index b0df80901a..ce59f7dc4e 100644 --- a/docs/useCopyToClipboard.md +++ b/docs/useCopyToClipboard.md @@ -42,7 +42,7 @@ import {useCopyToClipboard} from 'react-use'; import * as clipboard from "clipboard-polyfill" const myComp = (props) => { - const [success, copyToClipboard] = useCopyToClipboard(null, clipboard.writeText); + const [success, copyToClipboard] = useCopyToClipboard(undefined, clipboard.writeText); const myText = 'text to be copied'; return (
diff --git a/src/useCopyToClipboard.ts b/src/useCopyToClipboard.ts index 30258061dc..01d7457b37 100644 --- a/src/useCopyToClipboard.ts +++ b/src/useCopyToClipboard.ts @@ -12,7 +12,7 @@ const copyDefault = (text) => { }; -const useCopyToClipboard = (timeout = null, copy = copyDefault) => { +const useCopyToClipboard = (timeout = undefined, copy = copyDefault) => { const [success, setSuccess] = useState(false); From 6578cfc27bed4c3c5d6b57279e9284c6254e6e88 Mon Sep 17 00:00:00 2001 From: scottcampbell Date: Fri, 5 Apr 2019 09:53:07 -0400 Subject: [PATCH 4/8] removed clipboard-polyfilll dependancy. --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 8228553131..81df383b10 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,6 @@ }, "homepage": "https://github.com/streamich/react-use#readme", "dependencies": { - "clipboard-polyfill": "^2.7.0", "nano-css": "^5.1.0", "react-wait": "^0.3.0", "screenfull": "^4.1.0", From cf847037b20fcb167395ebad2cd9b633a349125d Mon Sep 17 00:00:00 2001 From: scottcampbell Date: Fri, 5 Apr 2019 10:00:38 -0400 Subject: [PATCH 5/8] reverted yarn.lock to original. --- yarn.lock | 5 ----- 1 file changed, 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index f042241f28..2025b08bdb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3638,11 +3638,6 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= -clipboard-polyfill@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/clipboard-polyfill/-/clipboard-polyfill-2.7.0.tgz#4f4344bb3405f88609ccf8ee2d445018da0e318f" - integrity sha512-JUQ9Q32xetbWG3e3tdYbP4uB13YeMz79AWg4SiPLVbu4s6pv2q70R8uXDtA8UD3f2BKQeTDD5ApoqRCs29RDlw== - clipboard@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d" From 0a6d7731ddf26445c02db4c1c5e5fb18a63bc985 Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 7 Apr 2019 22:28:52 +0200 Subject: [PATCH 6/8] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20make=20useCopyToClip?= =?UTF-8?q?board=20hook=20interface=20more=20idiomatic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + docs/useCopyToClipboard.md | 57 +++++++------------- src/__stories__/useCopyToClipboard.story.tsx | 13 ++--- src/useCopyToClipboard.ts | 50 +++++++---------- 4 files changed, 45 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index 74449a3cc6..7b4f10b2bb 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ - [**Side-effects**](./docs/Side-effects.md) - [`useAsync`](./docs/useAsync.md) — resolves an `async` function. - [`useAsyncRetry`](./docs/useAsyncRetry.md) — `useAsync` with `retry()` method. + - [`useCopyToClipboard`](./docs/useCopyToClipboard.md) — copies text to clipboard. - [`useDebounce`](./docs/useDebounce.md) — debounces a function. [![][img-demo]](https://streamich.github.io/react-use/?path=/story/side-effects-usedebounce--demo) - [`useFavicon`](./docs/useFavicon.md) — sets favicon of the page. - [`useLocalStorage`](./docs/useLocalStorage.md) — manages a value in `localStorage`. diff --git a/docs/useCopyToClipboard.md b/docs/useCopyToClipboard.md index ce59f7dc4e..79b83f7b4a 100644 --- a/docs/useCopyToClipboard.md +++ b/docs/useCopyToClipboard.md @@ -1,54 +1,35 @@ # `useCopyToClipboard` -copy text to a users clipboard. +Copy text to a user's clipboard. -## Usage - -### basic usage: - -```jsx -import {useCopyToClipboard} from 'react-use'; -const myComp = (props) => { - const [success, copyToClipboard] = useCopyToClipboard(); - const myText = 'text to be copied'; - return ( - copyToClipboard(myText) }>{myText} - ) -} -``` +## Usage -### with timeout: +Basic usage ```jsx -import {useCopyToClipboard} from 'react-use'; +const Demo = () => { + const [text, setText] = React.useState(''); + const [copied, copyToClipboard] = useCopyToClipboard(text); -const myComp = (props) => { - const [success, copyToClipboard] = useCopyToClipboard(2000); - const myText = 'text to be copied'; return (
- copyToClipboard(myText) }>{myText} - { success && copied to clipboard} + setText(e.target.value)} /> + +
Copied: {copied ? 'Yes' : 'No'}
- ) + ) } ``` -### with custom polyfill: +## Reference -```jsx -import {useCopyToClipboard} from 'react-use'; -import * as clipboard from "clipboard-polyfill" - -const myComp = (props) => { - const [success, copyToClipboard] = useCopyToClipboard(undefined, clipboard.writeText); - const myText = 'text to be copied'; - return ( -
- copyToClipboard(myText) }>{myText} - { success && copied to clipboard} -
- ) -} +```js +const [copied, copyToClipboard] = useCopyToClipboard(text); +const [copied, copyToClipboard] = useCopyToClipboard(text, copyFunction); ``` + +, where + +- `copyFunction` — function that receives a single string argument, which + it copies to user's clipboard. diff --git a/src/__stories__/useCopyToClipboard.story.tsx b/src/__stories__/useCopyToClipboard.story.tsx index d97437e53e..fa346a017f 100644 --- a/src/__stories__/useCopyToClipboard.story.tsx +++ b/src/__stories__/useCopyToClipboard.story.tsx @@ -4,13 +4,14 @@ import ShowDocs from './util/ShowDocs'; import {useCopyToClipboard} from '..'; const Demo = () => { - const [success, copyToClipboard] = useCopyToClipboard(2000); - const textToCopy = "text to be copied" + const [text, setText] = React.useState(''); + const [copied, copyToClipboard] = useCopyToClipboard(text); + return (
- {textToCopy} - - { success && text copied!} + setText(e.target.value)} /> + +
Copied: {copied ? 'Yes' : 'No'}
@@ -18,6 +19,6 @@ const Demo = () => { ) } -storiesOf('UI|useCopyToClipboard', module) +storiesOf('Side-effects|useCopyToClipboard', module) .add('Docs', () => ) .add('Demo', () => ) diff --git a/src/useCopyToClipboard.ts b/src/useCopyToClipboard.ts index 01d7457b37..0515f9270f 100644 --- a/src/useCopyToClipboard.ts +++ b/src/useCopyToClipboard.ts @@ -1,41 +1,27 @@ -import {useState, useEffect} from 'react'; -import * as clipboard from "clipboard-polyfill" +import useUpdateEffect from './useUpdateEffect'; +import {useState, useCallback} from 'react'; const copyDefault = (text) => { - console.log('run'); - const element = document.createElement('textarea'); // create textarea HTML element - element.value = text; // add the text to be copied to the element - document.body.appendChild(element); // add element to DOM - element.select(); // select the text - document.execCommand('copy'); // execute copy command - document.body.removeChild(element); // remove element from DOM + const element = document.createElement('textarea'); + element.value = text; + document.body.appendChild(element); + element.select(); + document.execCommand('copy'); + document.body.removeChild(element); }; +const useCopyToClipboard = (text: string = '', copy = copyDefault): [boolean, () => void] => { + const [copied, setCopied] = useState(false); + const copyToClipboard = useCallback(() => { + copy(text); + setCopied(true); + }, [text]); -const useCopyToClipboard = (timeout = undefined, copy = copyDefault) => { + useUpdateEffect(() => { + setCopied(false); + }, [text]); - const [success, setSuccess] = useState(false); - - const copyToClipboard = (text) => { - - if (typeof text == "string" || typeof text == "number" ) { - copy(text); - setSuccess(true); - } - else { - setSuccess(false); - console.error(`Cannot copy typeof ${typeof text} to clipboard, must be a valid string or number.`); - } - } - - useEffect( () => { - // if timeout given, set success to false - if (timeout) { - if (success) setTimeout( () => setSuccess(false), timeout); - } - }, [success]) - - return [success, copyToClipboard]; + return [copied, copyToClipboard]; } export default useCopyToClipboard; From f185044236046f1533b95853ae194564d2b44343 Mon Sep 17 00:00:00 2001 From: streamich Date: Mon, 8 Apr 2019 00:16:44 +0200 Subject: [PATCH 7/8] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20improve=20useCopyToC?= =?UTF-8?q?lipboard()=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/useCopyToClipboard.md | 4 ++-- src/useCopyToClipboard.ts | 48 +++++++++++++++++++++++++++++++------- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/docs/useCopyToClipboard.md b/docs/useCopyToClipboard.md index 79b83f7b4a..cc56dae1eb 100644 --- a/docs/useCopyToClipboard.md +++ b/docs/useCopyToClipboard.md @@ -26,10 +26,10 @@ const Demo = () => { ```js const [copied, copyToClipboard] = useCopyToClipboard(text); -const [copied, copyToClipboard] = useCopyToClipboard(text, copyFunction); +const [copied, copyToClipboard] = useCopyToClipboard(text, writeText); ``` , where -- `copyFunction` — function that receives a single string argument, which +- `writeText` — function that receives a single string argument, which it copies to user's clipboard. diff --git a/src/useCopyToClipboard.ts b/src/useCopyToClipboard.ts index 0515f9270f..62024190c6 100644 --- a/src/useCopyToClipboard.ts +++ b/src/useCopyToClipboard.ts @@ -1,24 +1,54 @@ import useUpdateEffect from './useUpdateEffect'; -import {useState, useCallback} from 'react'; +import useRefMounted from './useRefMounted'; +import {useState, useCallback, useRef} from 'react'; -const copyDefault = (text) => { +export type WriteText = (text: string) => Promise; // https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText +export type UseCopyToClipboard = (text?: string, writeText?: WriteText) => [boolean, () => void]; + +const writeTextDefault = async (text) => { const element = document.createElement('textarea'); element.value = text; document.body.appendChild(element); - element.select(); - document.execCommand('copy'); - document.body.removeChild(element); + try { + element.select(); + document.execCommand('copy'); + } finally { + document.body.removeChild(element); + } }; -const useCopyToClipboard = (text: string = '', copy = copyDefault): [boolean, () => void] => { +const useCopyToClipboard: UseCopyToClipboard = (text = '', writeText = writeTextDefault) => { + if (process.env.NODE_ENV !== 'production') { + if (typeof text !== 'string') { + console.warn('useCopyToClipboard hook expects first argument to be string.'); + } + } + + const mounted = useRefMounted(); + const latestText = useRef(text); const [copied, setCopied] = useState(false); - const copyToClipboard = useCallback(() => { - copy(text); - setCopied(true); + const copyToClipboard = useCallback(async () => { + if (latestText.current !== text) { + if (process.env.NODE_ENV !== 'production') { + console.warn('Trying to copy stale text.'); + } + return; + } + + try { + await writeText(text); + if (!mounted.current) return; + setCopied(true); + } catch (error) { + if (!mounted.current) return; + console.error(error); + setCopied(false); + } }, [text]); useUpdateEffect(() => { setCopied(false); + latestText.current = text; }, [text]); return [copied, copyToClipboard]; From 25532254701cbf36e65e91db44b27db6111d8bd8 Mon Sep 17 00:00:00 2001 From: streamich Date: Mon, 8 Apr 2019 00:42:07 +0200 Subject: [PATCH 8/8] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20onCopy=20and?= =?UTF-8?q?=20onError=20events=20and=20use=20NPM=20copy=20library?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/__stories__/useCopyToClipboard.story.tsx | 5 +++- src/useCopyToClipboard.ts | 28 +++++++++----------- yarn.lock | 9 ++++++- 4 files changed, 26 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 81df383b10..993b6eb357 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ }, "homepage": "https://github.com/streamich/react-use#readme", "dependencies": { + "copy-to-clipboard": "^3.1.0", "nano-css": "^5.1.0", "react-wait": "^0.3.0", "screenfull": "^4.1.0", diff --git a/src/__stories__/useCopyToClipboard.story.tsx b/src/__stories__/useCopyToClipboard.story.tsx index fa346a017f..7fcd5a7f3e 100644 --- a/src/__stories__/useCopyToClipboard.story.tsx +++ b/src/__stories__/useCopyToClipboard.story.tsx @@ -5,7 +5,10 @@ import {useCopyToClipboard} from '..'; const Demo = () => { const [text, setText] = React.useState(''); - const [copied, copyToClipboard] = useCopyToClipboard(text); + const [copied, copyToClipboard] = useCopyToClipboard(text, { + onCopy: txt => alert('success: ' + txt), + onError: err => alert(err), + }); return (
diff --git a/src/useCopyToClipboard.ts b/src/useCopyToClipboard.ts index 62024190c6..244bcc1bfb 100644 --- a/src/useCopyToClipboard.ts +++ b/src/useCopyToClipboard.ts @@ -1,23 +1,19 @@ +import {useState, useCallback, useRef} from 'react'; import useUpdateEffect from './useUpdateEffect'; import useRefMounted from './useRefMounted'; -import {useState, useCallback, useRef} from 'react'; +const writeTextDefault = require('copy-to-clipboard'); export type WriteText = (text: string) => Promise; // https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText -export type UseCopyToClipboard = (text?: string, writeText?: WriteText) => [boolean, () => void]; - -const writeTextDefault = async (text) => { - const element = document.createElement('textarea'); - element.value = text; - document.body.appendChild(element); - try { - element.select(); - document.execCommand('copy'); - } finally { - document.body.removeChild(element); - } -}; +export interface UseCopyToClipboardOptions { + writeText?: WriteText; + onCopy?: (text: string) => void; + onError?: (error: any, text: string) => void; +} +export type UseCopyToClipboard = (text?: string, options?: UseCopyToClipboardOptions) => [boolean, () => void]; + +const useCopyToClipboard: UseCopyToClipboard = (text = '', options) => { + const {writeText = writeTextDefault, onCopy, onError} = (options || {}) as UseCopyToClipboardOptions; -const useCopyToClipboard: UseCopyToClipboard = (text = '', writeText = writeTextDefault) => { if (process.env.NODE_ENV !== 'production') { if (typeof text !== 'string') { console.warn('useCopyToClipboard hook expects first argument to be string.'); @@ -39,10 +35,12 @@ const useCopyToClipboard: UseCopyToClipboard = (text = '', writeText = writeText await writeText(text); if (!mounted.current) return; setCopied(true); + onCopy && onCopy(text); } catch (error) { if (!mounted.current) return; console.error(error); setCopied(false); + onError && onError(error, text); } }, [text]); diff --git a/yarn.lock b/yarn.lock index 2025b08bdb..41d861ec33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3932,6 +3932,13 @@ copy-to-clipboard@^3.0.8: dependencies: toggle-selection "^1.0.3" +copy-to-clipboard@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.1.0.tgz#0a28141899e6bd217b9dc13fd1689b3b38820b44" + integrity sha512-+RNyDq266tv5aGhfRsL6lxgj8Y6sCvTrVJnFUVvuxuqkcSMaLISt1wd4JkdQSphbcLTIQ9kEpTULNnoCXAFdng== + dependencies: + toggle-selection "^1.0.6" + core-js-compat@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.0.0.tgz#cd9810b8000742535a4a43773866185e310bd4f7" @@ -11010,7 +11017,7 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -toggle-selection@^1.0.3: +toggle-selection@^1.0.3, toggle-selection@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI=