mirror of
https://github.com/streamich/react-use.git
synced 2026-02-01 14:37:31 +00:00
feat: add useCopyToClipboard() hook
This commit is contained in:
commit
4d8e276607
@ -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`.
|
||||
|
||||
35
docs/useCopyToClipboard.md
Normal file
35
docs/useCopyToClipboard.md
Normal file
@ -0,0 +1,35 @@
|
||||
# `useCopyToClipboard`
|
||||
|
||||
Copy text to a user's clipboard.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Basic usage
|
||||
|
||||
```jsx
|
||||
const Demo = () => {
|
||||
const [text, setText] = React.useState('');
|
||||
const [copied, copyToClipboard] = useCopyToClipboard(text);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input value={text} onChange={e => setText(e.target.value)} />
|
||||
<button type="button" onClick={copyToClipboard}>copy text</button>
|
||||
<div>Copied: {copied ? 'Yes' : 'No'}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
```js
|
||||
const [copied, copyToClipboard] = useCopyToClipboard(text);
|
||||
const [copied, copyToClipboard] = useCopyToClipboard(text, writeText);
|
||||
```
|
||||
|
||||
, where
|
||||
|
||||
- `writeText` — function that receives a single string argument, which
|
||||
it copies to user's clipboard.
|
||||
@ -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-fast-compare": "^2.0.4",
|
||||
"react-wait": "^0.3.0",
|
||||
|
||||
27
src/__stories__/useCopyToClipboard.story.tsx
Normal file
27
src/__stories__/useCopyToClipboard.story.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import * as React from 'react';
|
||||
import {storiesOf} from '@storybook/react';
|
||||
import ShowDocs from './util/ShowDocs';
|
||||
import {useCopyToClipboard} from '..';
|
||||
|
||||
const Demo = () => {
|
||||
const [text, setText] = React.useState('');
|
||||
const [copied, copyToClipboard] = useCopyToClipboard(text, {
|
||||
onCopy: txt => alert('success: ' + txt),
|
||||
onError: err => alert(err),
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input value={text} onChange={e => setText(e.target.value)} />
|
||||
<button type="button" onClick={copyToClipboard}>copy text</button>
|
||||
<div>Copied: {copied ? 'Yes' : 'No'}</div>
|
||||
<div style={{margin: 10}}>
|
||||
<input type="text" placeholder="now paste it in here"/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
storiesOf('Side-effects|useCopyToClipboard', module)
|
||||
.add('Docs', () => <ShowDocs md={require('../../docs/useCopyToClipboard.md')} />)
|
||||
.add('Demo', () => <Demo/>)
|
||||
@ -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';
|
||||
@ -73,6 +74,7 @@ export {
|
||||
useAudio,
|
||||
useBattery,
|
||||
useBoolean,
|
||||
useCopyToClipboard,
|
||||
useDrop,
|
||||
useDropArea,
|
||||
useClickAway,
|
||||
|
||||
55
src/useCopyToClipboard.ts
Normal file
55
src/useCopyToClipboard.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import {useState, useCallback, useRef} from 'react';
|
||||
import useUpdateEffect from './useUpdateEffect';
|
||||
import useRefMounted from './useRefMounted';
|
||||
const writeTextDefault = require('copy-to-clipboard');
|
||||
|
||||
export type WriteText = (text: string) => Promise<void>; // https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText
|
||||
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;
|
||||
|
||||
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(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);
|
||||
onCopy && onCopy(text);
|
||||
} catch (error) {
|
||||
if (!mounted.current) return;
|
||||
console.error(error);
|
||||
setCopied(false);
|
||||
onError && onError(error, text);
|
||||
}
|
||||
}, [text]);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
setCopied(false);
|
||||
latestText.current = text;
|
||||
}, [text]);
|
||||
|
||||
return [copied, copyToClipboard];
|
||||
}
|
||||
|
||||
export default useCopyToClipboard;
|
||||
@ -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=
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user