@usefy/use-copy-to-clipboard 0.0.8 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +498 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,498 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/geon0529/usefy/master/assets/logo.png" alt="usefy logo" width="120" />
3
+ </p>
4
+
5
+ <h1 align="center">@usefy/use-copy-to-clipboard</h1>
6
+
7
+ <p align="center">
8
+ <strong>A robust React hook for copying text to clipboard with fallback support</strong>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/@usefy/use-copy-to-clipboard">
13
+ <img src="https://img.shields.io/npm/v/@usefy/use-copy-to-clipboard.svg?style=flat-square&color=007acc" alt="npm version" />
14
+ </a>
15
+ <a href="https://www.npmjs.com/package/@usefy/use-copy-to-clipboard">
16
+ <img src="https://img.shields.io/npm/dm/@usefy/use-copy-to-clipboard.svg?style=flat-square&color=007acc" alt="npm downloads" />
17
+ </a>
18
+ <a href="https://bundlephobia.com/package/@usefy/use-copy-to-clipboard">
19
+ <img src="https://img.shields.io/bundlephobia/minzip/@usefy/use-copy-to-clipboard?style=flat-square&color=007acc" alt="bundle size" />
20
+ </a>
21
+ <a href="https://github.com/geon0529/usefy/blob/master/LICENSE">
22
+ <img src="https://img.shields.io/npm/l/@usefy/use-copy-to-clipboard.svg?style=flat-square&color=007acc" alt="license" />
23
+ </a>
24
+ </p>
25
+
26
+ <p align="center">
27
+ <a href="#installation">Installation</a> •
28
+ <a href="#quick-start">Quick Start</a> •
29
+ <a href="#api-reference">API Reference</a> •
30
+ <a href="#examples">Examples</a> •
31
+ <a href="#license">License</a>
32
+ </p>
33
+
34
+ ---
35
+
36
+ ## Overview
37
+
38
+ `@usefy/use-copy-to-clipboard` provides a simple way to copy text to the clipboard using the modern Clipboard API with automatic fallback for older browsers. Features include auto-reset timeout, success/error callbacks, and copy state tracking.
39
+
40
+ **Part of the [@usefy](https://www.npmjs.com/org/usefy) ecosystem** — a collection of production-ready React hooks designed for modern applications.
41
+
42
+ ### Why use-copy-to-clipboard?
43
+
44
+ - **Zero Dependencies** — Pure React implementation with no external dependencies
45
+ - **TypeScript First** — Full type safety with exported interfaces
46
+ - **Modern + Fallback** — Uses Clipboard API with automatic `execCommand` fallback
47
+ - **Auto Reset** — Copied state automatically resets after configurable timeout
48
+ - **Callbacks** — `onSuccess` and `onError` callbacks for custom handling
49
+ - **Async/Await** — Returns promise with boolean success indicator
50
+ - **SSR Compatible** — Works seamlessly with Next.js, Remix, and other SSR frameworks
51
+ - **Stable References** — Memoized copy function for optimal performance
52
+ - **Well Tested** — Comprehensive test coverage with Vitest
53
+
54
+ ---
55
+
56
+ ## Installation
57
+
58
+ ```bash
59
+ # npm
60
+ npm install @usefy/use-copy-to-clipboard
61
+
62
+ # yarn
63
+ yarn add @usefy/use-copy-to-clipboard
64
+
65
+ # pnpm
66
+ pnpm add @usefy/use-copy-to-clipboard
67
+ ```
68
+
69
+ ### Peer Dependencies
70
+
71
+ This package requires React 18 or 19:
72
+
73
+ ```json
74
+ {
75
+ "peerDependencies": {
76
+ "react": "^18.0.0 || ^19.0.0"
77
+ }
78
+ }
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Quick Start
84
+
85
+ ```tsx
86
+ import { useCopyToClipboard } from '@usefy/use-copy-to-clipboard';
87
+
88
+ function CopyButton() {
89
+ const [copiedText, copy] = useCopyToClipboard();
90
+
91
+ return (
92
+ <button onClick={() => copy('Hello World!')}>
93
+ {copiedText ? 'Copied!' : 'Copy'}
94
+ </button>
95
+ );
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ## API Reference
102
+
103
+ ### `useCopyToClipboard(options?)`
104
+
105
+ A hook that provides clipboard copy functionality with state tracking.
106
+
107
+ #### Parameters
108
+
109
+ | Parameter | Type | Description |
110
+ |-----------|------|-------------|
111
+ | `options` | `UseCopyToClipboardOptions` | Configuration options |
112
+
113
+ #### Options
114
+
115
+ | Option | Type | Default | Description |
116
+ |--------|------|---------|-------------|
117
+ | `timeout` | `number` | `2000` | Time in ms before `copiedText` resets to null. Set to `0` to disable. |
118
+ | `onSuccess` | `(text: string) => void` | — | Callback called when copy succeeds |
119
+ | `onError` | `(error: Error) => void` | — | Callback called when copy fails |
120
+
121
+ #### Returns `[copiedText, copy]`
122
+
123
+ | Index | Type | Description |
124
+ |-------|------|-------------|
125
+ | `[0]` | `string \| null` | The last successfully copied text, or `null` |
126
+ | `[1]` | `(text: string) => Promise<boolean>` | Async function to copy text |
127
+
128
+ ---
129
+
130
+ ## Examples
131
+
132
+ ### Basic Copy Button
133
+
134
+ ```tsx
135
+ import { useCopyToClipboard } from '@usefy/use-copy-to-clipboard';
136
+
137
+ function CopyButton({ text }: { text: string }) {
138
+ const [copiedText, copy] = useCopyToClipboard();
139
+
140
+ return (
141
+ <button onClick={() => copy(text)}>
142
+ {copiedText === text ? 'Copied!' : 'Copy to Clipboard'}
143
+ </button>
144
+ );
145
+ }
146
+ ```
147
+
148
+ ### Copy with Visual Feedback
149
+
150
+ ```tsx
151
+ import { useCopyToClipboard } from '@usefy/use-copy-to-clipboard';
152
+
153
+ function CopyWithIcon({ text }: { text: string }) {
154
+ const [copiedText, copy] = useCopyToClipboard();
155
+ const isCopied = copiedText === text;
156
+
157
+ return (
158
+ <button
159
+ onClick={() => copy(text)}
160
+ className={isCopied ? 'copied' : ''}
161
+ >
162
+ {isCopied ? (
163
+ <CheckIcon className="icon" />
164
+ ) : (
165
+ <CopyIcon className="icon" />
166
+ )}
167
+ {isCopied ? 'Copied!' : 'Copy'}
168
+ </button>
169
+ );
170
+ }
171
+ ```
172
+
173
+ ### Code Block with Copy
174
+
175
+ ```tsx
176
+ import { useCopyToClipboard } from '@usefy/use-copy-to-clipboard';
177
+
178
+ function CodeBlock({ code, language }: { code: string; language: string }) {
179
+ const [copiedText, copy] = useCopyToClipboard();
180
+
181
+ return (
182
+ <div className="code-block">
183
+ <div className="code-header">
184
+ <span>{language}</span>
185
+ <button onClick={() => copy(code)}>
186
+ {copiedText === code ? 'Copied!' : 'Copy Code'}
187
+ </button>
188
+ </div>
189
+ <pre>
190
+ <code>{code}</code>
191
+ </pre>
192
+ </div>
193
+ );
194
+ }
195
+ ```
196
+
197
+ ### Custom Timeout
198
+
199
+ ```tsx
200
+ import { useCopyToClipboard } from '@usefy/use-copy-to-clipboard';
201
+
202
+ function LongFeedbackCopy() {
203
+ // Show "Copied!" for 5 seconds
204
+ const [copiedText, copy] = useCopyToClipboard({ timeout: 5000 });
205
+
206
+ return (
207
+ <button onClick={() => copy('Long feedback!')}>
208
+ {copiedText ? 'Copied!' : 'Copy'}
209
+ </button>
210
+ );
211
+ }
212
+ ```
213
+
214
+ ### Persistent Copied State
215
+
216
+ ```tsx
217
+ import { useCopyToClipboard } from '@usefy/use-copy-to-clipboard';
218
+
219
+ function PersistentCopy() {
220
+ // Never auto-reset the copied state
221
+ const [copiedText, copy] = useCopyToClipboard({ timeout: 0 });
222
+
223
+ return (
224
+ <div>
225
+ <button onClick={() => copy('Persistent!')}>
226
+ {copiedText ? 'Copied!' : 'Copy'}
227
+ </button>
228
+ {copiedText && <span>Copied text: {copiedText}</span>}
229
+ </div>
230
+ );
231
+ }
232
+ ```
233
+
234
+ ### With Callbacks
235
+
236
+ ```tsx
237
+ import { useCopyToClipboard } from '@usefy/use-copy-to-clipboard';
238
+ import { toast } from 'your-toast-library';
239
+
240
+ function CopyWithToast({ text }: { text: string }) {
241
+ const [, copy] = useCopyToClipboard({
242
+ onSuccess: (copiedText) => {
243
+ toast.success(`Copied: ${copiedText}`);
244
+ },
245
+ onError: (error) => {
246
+ toast.error(`Failed to copy: ${error.message}`);
247
+ },
248
+ });
249
+
250
+ return <button onClick={() => copy(text)}>Copy</button>;
251
+ }
252
+ ```
253
+
254
+ ### Async Handling
255
+
256
+ ```tsx
257
+ import { useCopyToClipboard } from '@usefy/use-copy-to-clipboard';
258
+
259
+ function AsyncCopy({ text }: { text: string }) {
260
+ const [, copy] = useCopyToClipboard();
261
+ const [status, setStatus] = useState<'idle' | 'success' | 'error'>('idle');
262
+
263
+ const handleCopy = async () => {
264
+ const success = await copy(text);
265
+ setStatus(success ? 'success' : 'error');
266
+ };
267
+
268
+ return (
269
+ <div>
270
+ <button onClick={handleCopy}>Copy</button>
271
+ {status === 'success' && <span className="success">Copied successfully!</span>}
272
+ {status === 'error' && <span className="error">Failed to copy</span>}
273
+ </div>
274
+ );
275
+ }
276
+ ```
277
+
278
+ ### Share URL Button
279
+
280
+ ```tsx
281
+ import { useCopyToClipboard } from '@usefy/use-copy-to-clipboard';
282
+
283
+ function ShareButton() {
284
+ const [copiedText, copy] = useCopyToClipboard();
285
+
286
+ const handleShare = () => {
287
+ copy(window.location.href);
288
+ };
289
+
290
+ return (
291
+ <button onClick={handleShare}>
292
+ {copiedText ? 'Link Copied!' : 'Share Link'}
293
+ </button>
294
+ );
295
+ }
296
+ ```
297
+
298
+ ### Copy Multiple Items
299
+
300
+ ```tsx
301
+ import { useCopyToClipboard } from '@usefy/use-copy-to-clipboard';
302
+
303
+ function CopyList({ items }: { items: string[] }) {
304
+ const [copiedText, copy] = useCopyToClipboard();
305
+
306
+ return (
307
+ <ul>
308
+ {items.map((item) => (
309
+ <li key={item}>
310
+ <span>{item}</span>
311
+ <button onClick={() => copy(item)}>
312
+ {copiedText === item ? 'Copied!' : 'Copy'}
313
+ </button>
314
+ </li>
315
+ ))}
316
+ </ul>
317
+ );
318
+ }
319
+ ```
320
+
321
+ ### API Key Display
322
+
323
+ ```tsx
324
+ import { useCopyToClipboard } from '@usefy/use-copy-to-clipboard';
325
+
326
+ function ApiKeyDisplay({ apiKey }: { apiKey: string }) {
327
+ const [copiedText, copy] = useCopyToClipboard();
328
+
329
+ const maskedKey = `${apiKey.slice(0, 4)}${'*'.repeat(20)}${apiKey.slice(-4)}`;
330
+
331
+ return (
332
+ <div className="api-key">
333
+ <code>{maskedKey}</code>
334
+ <button onClick={() => copy(apiKey)}>
335
+ {copiedText === apiKey ? 'Copied!' : 'Copy Key'}
336
+ </button>
337
+ </div>
338
+ );
339
+ }
340
+ ```
341
+
342
+ ---
343
+
344
+ ## TypeScript
345
+
346
+ This hook is written in TypeScript with exported types.
347
+
348
+ ```tsx
349
+ import {
350
+ useCopyToClipboard,
351
+ type UseCopyToClipboardOptions,
352
+ type UseCopyToClipboardReturn,
353
+ type CopyFn,
354
+ } from '@usefy/use-copy-to-clipboard';
355
+
356
+ // Return type
357
+ const [copiedText, copy]: UseCopyToClipboardReturn = useCopyToClipboard();
358
+
359
+ // copiedText: string | null
360
+ // copy: (text: string) => Promise<boolean>
361
+
362
+ // Options type
363
+ const options: UseCopyToClipboardOptions = {
364
+ timeout: 3000,
365
+ onSuccess: (text) => console.log('Copied:', text),
366
+ onError: (error) => console.error('Error:', error),
367
+ };
368
+ ```
369
+
370
+ ---
371
+
372
+ ## Browser Support
373
+
374
+ This hook uses the modern [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API) when available, with automatic fallback to `document.execCommand('copy')` for older browsers.
375
+
376
+ | Browser | Clipboard API | Fallback |
377
+ |---------|---------------|----------|
378
+ | Chrome 66+ | Yes | - |
379
+ | Firefox 63+ | Yes | - |
380
+ | Safari 13.1+ | Yes | - |
381
+ | Edge 79+ | Yes | - |
382
+ | IE 11 | No | Yes |
383
+ | Older browsers | No | Yes |
384
+
385
+ ---
386
+
387
+ ## Testing
388
+
389
+ This package maintains comprehensive test coverage to ensure reliability and stability.
390
+
391
+ ### Test Coverage
392
+
393
+ | Category | Tests | Coverage |
394
+ |----------|-------|----------|
395
+ | Initialization | 3 | 100% |
396
+ | Copy Functionality | 5 | 100% |
397
+ | Copy Failure | 3 | 100% |
398
+ | Timeout/Auto-Reset | 4 | 100% |
399
+ | Callbacks | 4 | 100% |
400
+ | Function Stability | 3 | 100% |
401
+ | Cleanup | 2 | 100% |
402
+ | Multiple Instances | 2 | 100% |
403
+ | Fallback Behavior | 2 | 100% |
404
+ | Edge Cases | 4 | 100% |
405
+ | **Total** | **32** | **87.87%** |
406
+
407
+ ### Test Categories
408
+
409
+ <details>
410
+ <summary><strong>Copy Functionality Tests</strong></summary>
411
+
412
+ - Copy text successfully using Clipboard API
413
+ - Update copiedText after successful copy
414
+ - Return true on successful copy
415
+ - Handle empty string
416
+ - Handle special characters
417
+
418
+ </details>
419
+
420
+ <details>
421
+ <summary><strong>Timeout Tests</strong></summary>
422
+
423
+ - Reset copiedText after default timeout (2000ms)
424
+ - Reset after custom timeout
425
+ - Not reset when timeout is 0
426
+ - Reset timer on consecutive copies
427
+
428
+ </details>
429
+
430
+ <details>
431
+ <summary><strong>Fallback Tests</strong></summary>
432
+
433
+ - Use fallback when Clipboard API is not available
434
+ - Try fallback when Clipboard API throws error
435
+
436
+ </details>
437
+
438
+ ### Running Tests
439
+
440
+ ```bash
441
+ # Run all tests
442
+ pnpm test
443
+
444
+ # Run tests in watch mode
445
+ pnpm test:watch
446
+
447
+ # Run tests with coverage report
448
+ pnpm test --coverage
449
+ ```
450
+
451
+ ---
452
+
453
+ ## Related Packages
454
+
455
+ Explore other hooks in the **@usefy** collection:
456
+
457
+ | Package | Description |
458
+ |---------|-------------|
459
+ | [@usefy/use-local-storage](https://www.npmjs.com/package/@usefy/use-local-storage) | Persistent localStorage |
460
+ | [@usefy/use-session-storage](https://www.npmjs.com/package/@usefy/use-session-storage) | Session storage persistence |
461
+ | [@usefy/use-toggle](https://www.npmjs.com/package/@usefy/use-toggle) | Boolean state management |
462
+ | [@usefy/use-counter](https://www.npmjs.com/package/@usefy/use-counter) | Counter state management |
463
+ | [@usefy/use-debounce](https://www.npmjs.com/package/@usefy/use-debounce) | Value debouncing |
464
+ | [@usefy/use-click-any-where](https://www.npmjs.com/package/@usefy/use-click-any-where) | Global click detection |
465
+
466
+ ---
467
+
468
+ ## Contributing
469
+
470
+ We welcome contributions! Please see our [Contributing Guide](https://github.com/geon0529/usefy/blob/master/CONTRIBUTING.md) for details.
471
+
472
+ ```bash
473
+ # Clone the repository
474
+ git clone https://github.com/geon0529/usefy.git
475
+
476
+ # Install dependencies
477
+ pnpm install
478
+
479
+ # Run tests
480
+ pnpm test
481
+
482
+ # Build
483
+ pnpm build
484
+ ```
485
+
486
+ ---
487
+
488
+ ## License
489
+
490
+ MIT © [mirunamu](https://github.com/geon0529)
491
+
492
+ This package is part of the [usefy](https://github.com/geon0529/usefy) monorepo.
493
+
494
+ ---
495
+
496
+ <p align="center">
497
+ <sub>Built with care by the usefy team</sub>
498
+ </p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usefy/use-copy-to-clipboard",
3
- "version": "0.0.8",
3
+ "version": "0.0.11",
4
4
  "description": "A React hook for copying text to clipboard with fallback support",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",