Skip to content

Commit

Permalink
feat: use copy
Browse files Browse the repository at this point in the history
  • Loading branch information
wkylin committed Dec 30, 2024
1 parent 300a300 commit b5b06f5
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 87 deletions.
91 changes: 4 additions & 87 deletions src/components/hooks/useCopyToClipboard/index.ts
Original file line number Diff line number Diff line change
@@ -1,90 +1,7 @@
import { useState } from 'react'
import useCopyToClipboard from './use-copy-clipboard'

Check failure on line 1 in src/components/hooks/useCopyToClipboard/index.ts

View workflow job for this annotation

GitHub Actions / Qodana for JS

ESLint

ESLint: Install the 'eslint' package

const useCopyToClipboard = () => {
const [result, setResult] = useState<null | { state: 'success' } | { state: 'error'; message: string }>(null)
import useCopy from './use-copy'

const copy = async (text: string) => {
try {
await navigator.clipboard.writeText(text)
setResult({ state: 'success' })
} catch (e: any) {
setResult({ state: 'error', message: e.message })
throw e
} finally {
// 👇 Show the result feedback for 2 seconds
setTimeout(() => {
setResult(null)
}, 2000)
}
}
export default useCopy

// 👇 We want the result as a tuple
return [copy, result] as const
}
export default useCopyToClipboard

// export function Example() {
// const [inputText, setInputText] = useState('');
// const [copyToClipboard, copyResult] = useCopyToClipboard();

// const handleChangeInput = (e: React.ChangeEvent<HTMLInputElement>) => {
// setInputText(e.target.value);
// };

// const handleClickCopy = () => {
// copyToClipboard(inputText);
// };

// return (
// <div>
// <input value={inputText} onChange={handleChangeInput} />
// <button onClick={handleClickCopy}>Copy to clipboard</button>
// <div>
// {copyResult?.state === 'success' && 'Copied successfully!'}
// {copyResult?.state === 'error' && `Error: ${copyResult.message}`}
// </div>
// </div>
// );
// }

// import toast from 'react-hot-toast';

// type Props = React.HTMLAttributes<HTMLButtonElement> & {
// text: string;
// };

// function CopyToClipboard({ text, children = 'Copy', ...rest }: Props) {
// const handleClickCopy = async () => {
// try {
// await navigator.clipboard.writeText(text);
// // 👇 Using react-hot-toast to provide feedback
// toast.success('Copied!');
// } catch (e) {
// toast.error(`Error: ${e.message}`);
// throw e;
// }
// };

// return (
// <button onClick={handleClickCopy} {...rest}>
// {children}
// </button>
// );
// }

// export function Example() {
// const [inputText, setInputText] = React.useState('');

// const handleChangeInput = (e: React.ChangeEvent<HTMLInputElement>) => {
// setInputText(e.target.value);
// };

// return (
// <div>
// {/* 👇 Don't forget to add this */}
// <Toaster />
// <input value={inputText} onChange={handleChangeInput} />
// <CopyToClipboard text={inputText} />
// </div>
// );
// }
export { useCopyToClipboard }
90 changes: 90 additions & 0 deletions src/components/hooks/useCopyToClipboard/use-copy-clipboard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { useState } from 'react'

Check failure on line 1 in src/components/hooks/useCopyToClipboard/use-copy-clipboard.ts

View workflow job for this annotation

GitHub Actions / Qodana for JS

ESLint

ESLint: Install the 'eslint' package

const useCopyToClipboard = () => {
const [result, setResult] = useState<null | { state: 'success' } | { state: 'error'; message: string }>(null)

const copy = async (text: string) => {
try {
await navigator.clipboard.writeText(text)
setResult({ state: 'success' })
} catch (e: any) {
setResult({ state: 'error', message: e.message })
throw e
} finally {
// 👇 Show the result feedback for 2 seconds
setTimeout(() => {
setResult(null)
}, 2000)
}
}

// 👇 We want the result as a tuple
return [copy, result] as const
}
export default useCopyToClipboard

// export function Example() {
// const [inputText, setInputText] = useState('');
// const [copyToClipboard, copyResult] = useCopyToClipboard();

// const handleChangeInput = (e: React.ChangeEvent<HTMLInputElement>) => {
// setInputText(e.target.value);
// };

// const handleClickCopy = () => {
// copyToClipboard(inputText);
// };

// return (
// <div>
// <input value={inputText} onChange={handleChangeInput} />
// <button onClick={handleClickCopy}>Copy to clipboard</button>
// <div>
// {copyResult?.state === 'success' && 'Copied successfully!'}
// {copyResult?.state === 'error' && `Error: ${copyResult.message}`}
// </div>
// </div>
// );
// }

// import toast from 'react-hot-toast';

// type Props = React.HTMLAttributes<HTMLButtonElement> & {
// text: string;
// };

// function CopyToClipboard({ text, children = 'Copy', ...rest }: Props) {
// const handleClickCopy = async () => {
// try {
// await navigator.clipboard.writeText(text);
// // 👇 Using react-hot-toast to provide feedback
// toast.success('Copied!');
// } catch (e) {
// toast.error(`Error: ${e.message}`);
// throw e;
// }
// };

// return (
// <button onClick={handleClickCopy} {...rest}>
// {children}
// </button>
// );
// }

// export function Example() {
// const [inputText, setInputText] = React.useState('');

// const handleChangeInput = (e: React.ChangeEvent<HTMLInputElement>) => {
// setInputText(e.target.value);
// };

// return (
// <div>
// {/* 👇 Don't forget to add this */}
// <Toaster />
// <input value={inputText} onChange={handleChangeInput} />
// <CopyToClipboard text={inputText} />
// </div>
// );
// }
41 changes: 41 additions & 0 deletions src/components/hooks/useCopyToClipboard/use-copy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client'

Check failure on line 1 in src/components/hooks/useCopyToClipboard/use-copy.ts

View workflow job for this annotation

GitHub Actions / Qodana for JS

ESLint

ESLint: Install the 'eslint' package
import { useCallback, useEffect, useState } from 'react'

type CopiedValue = string | null

type CopyFn = (text: string) => Promise<boolean>

const useCopy = ({ isCopiedDelay = 2000 }: { isCopiedDelay?: number } = {}): [CopiedValue, CopyFn, boolean] => {
const [copiedText, setCopiedText] = useState<CopiedValue>(null)
const [isCopied, setIsCopied] = useState(false)

useEffect(() => {
if (!isCopied) {
return
}
setTimeout(() => {
setIsCopied(false)
}, isCopiedDelay)
}, [isCopied, isCopiedDelay])

const copy: CopyFn = useCallback(async (text) => {
if (!navigator?.clipboard) {
return false
}

// Try to save to clipboard then save it in the state if worked
try {
await navigator.clipboard.writeText(text)
setCopiedText(text)
setIsCopied(true)
return true
} catch (_error) {
setCopiedText(null)
return false
}
}, [])

return [copiedText, copy, isCopied]
}

export default useCopy
Empty file.

0 comments on commit b5b06f5

Please sign in to comment.