use-clipboard-pro 1.0.0
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.
- package/LICENSE +11 -0
- package/README.md +75 -0
- package/dist/index.cjs +157 -0
- package/dist/index.d.cts +19 -0
- package/dist/index.d.mts +19 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.mjs +155 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 UdhayaKumar
|
|
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
|
+
...
|
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# ✂️ use-clipboard-pro
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/use-clipboard-pro)
|
|
4
|
+
[](https://bundlephobia.com/package/use-clipboard-pro)
|
|
5
|
+
[](https://github.com/udhayavirat18/use-clipboard-pro/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
A bulletproof React clipboard hook engineered for production. It features global state sync, accessibility (ARIA) announcements, and legacy iOS fallbacks.
|
|
8
|
+
|
|
9
|
+
Most clipboard hooks (`use-clipboard-copy`, `@mantine/hooks`) are simple `useState` wrappers that fail silently on older devices, ignore screen readers, and lose history between components. `use-clipboard-pro` fixes all of that in **< 1.5kB**.
|
|
10
|
+
|
|
11
|
+
## ✨ Why this is different
|
|
12
|
+
* **Global History Sync:** Powered by an `@xstate/store` finite state machine. If you copy in a Modal, your Navbar instantly knows about it.
|
|
13
|
+
* **100% Accessible:** Automatically injects an `aria-live` region to announce "Copied!" to VoiceOver/NVDA screen readers.
|
|
14
|
+
* **Bulletproof Fallbacks:** Uses an invisible `<textarea>` injection for older iOS Safari versions and local HTTP dev environments that block the modern `navigator.clipboard` API.
|
|
15
|
+
* **SSR Ready:** Safely checks for `window` before executing.
|
|
16
|
+
|
|
17
|
+
## 📦 Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install use-clipboard-pro
|
|
21
|
+
# or
|
|
22
|
+
yarn add use-clipboard-pro
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
🚀 Quick Start
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
TypeScript
|
|
29
|
+
import { useClipboardPro } from 'use-clipboard-pro';
|
|
30
|
+
|
|
31
|
+
export function App() {
|
|
32
|
+
const { copy, copied, status, history, clearHistory } = useClipboardPro({
|
|
33
|
+
clearAfter: 2500, // Reset the 'copied' boolean after 2.5s
|
|
34
|
+
announce: true, // Enable ARIA screen-reader announcements
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div>
|
|
39
|
+
<button onClick={() => copy('npm install use-clipboard-pro')}>
|
|
40
|
+
{copied ? '✅ Copied!' : '📋 Copy Install Command'}
|
|
41
|
+
</button>
|
|
42
|
+
|
|
43
|
+
{status === 'error' && <p>Permission denied!</p>}
|
|
44
|
+
|
|
45
|
+
<h3>Your Global Copy History:</h3>
|
|
46
|
+
<ul>
|
|
47
|
+
{history.map((item, i) => (
|
|
48
|
+
<li key={i}>{item}</li>
|
|
49
|
+
))}
|
|
50
|
+
</ul>
|
|
51
|
+
<button onClick={clearHistory}>Clear History</button>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
# 🧠 API Reference
|
|
57
|
+
|
|
58
|
+
`useClipboardPro(options?: UseClipboardOptions)`
|
|
59
|
+
|
|
60
|
+
# Props
|
|
61
|
+
|
|
62
|
+
| Option | Type | Default | Description |
|
|
63
|
+
|--------|------|---------|-------------|
|
|
64
|
+
| `clearAfter` | `number` | `2000` | Milliseconds before the copied state resets to `false`. |
|
|
65
|
+
| `announce` | `boolean` | `true` | Whether to announce the copy event to screen readers. |
|
|
66
|
+
|
|
67
|
+
# Returns
|
|
68
|
+
|
|
69
|
+
| Property | Type | Description |
|
|
70
|
+
|----------|------|-------------|
|
|
71
|
+
| `copy(text: string)` | `Promise<boolean>` | Triggers the copy action. Returns success status. |
|
|
72
|
+
| `copied` | `boolean` | True if successfully copied. Resets after `clearAfter` ms. |
|
|
73
|
+
| `status` | `'idle' \| 'copied' \| 'error'` | The strict current state of the clipboard machine. |
|
|
74
|
+
| `history` | `string[]` | Array of the last 5 unique copied strings. Synced globally. |
|
|
75
|
+
| `clearHistory()` | `void` | Clears the global copy history. |
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const react = require('react');
|
|
4
|
+
const store = require('@xstate/store');
|
|
5
|
+
|
|
6
|
+
const clipboardStore = store.createStore({
|
|
7
|
+
context: {
|
|
8
|
+
status: "idle",
|
|
9
|
+
history: [],
|
|
10
|
+
error: null
|
|
11
|
+
},
|
|
12
|
+
on: {
|
|
13
|
+
COPY_SUCCESS: (context, event) => {
|
|
14
|
+
const newHistory = [
|
|
15
|
+
event.text,
|
|
16
|
+
...context.history.filter((item) => item !== event.text)
|
|
17
|
+
].slice(0, 5);
|
|
18
|
+
return {
|
|
19
|
+
...context,
|
|
20
|
+
status: "copied",
|
|
21
|
+
// <-- "as const" prevents TS from changing this to a generic string
|
|
22
|
+
history: newHistory,
|
|
23
|
+
error: null
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
COPY_ERROR: (context, event) => {
|
|
27
|
+
return {
|
|
28
|
+
...context,
|
|
29
|
+
status: "error",
|
|
30
|
+
error: event.error
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
RESET: (context) => {
|
|
34
|
+
return {
|
|
35
|
+
...context,
|
|
36
|
+
status: "idle",
|
|
37
|
+
error: null
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
CLEAR_HISTORY: (context) => {
|
|
41
|
+
return {
|
|
42
|
+
...context,
|
|
43
|
+
history: []
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const fallbackCopyTextToClipboard = (text) => {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const textArea = document.createElement("textarea");
|
|
52
|
+
textArea.value = text;
|
|
53
|
+
textArea.style.top = "0";
|
|
54
|
+
textArea.style.left = "0";
|
|
55
|
+
textArea.style.position = "fixed";
|
|
56
|
+
textArea.style.fontSize = "16px";
|
|
57
|
+
textArea.style.width = "1px";
|
|
58
|
+
textArea.style.height = "1px";
|
|
59
|
+
textArea.style.padding = "0";
|
|
60
|
+
textArea.style.border = "none";
|
|
61
|
+
textArea.style.outline = "none";
|
|
62
|
+
textArea.style.boxShadow = "none";
|
|
63
|
+
textArea.style.background = "transparent";
|
|
64
|
+
textArea.setAttribute("aria-hidden", "true");
|
|
65
|
+
document.body.appendChild(textArea);
|
|
66
|
+
textArea.focus();
|
|
67
|
+
textArea.select();
|
|
68
|
+
textArea.setSelectionRange(0, 999999);
|
|
69
|
+
try {
|
|
70
|
+
const successful = document.execCommand("copy");
|
|
71
|
+
document.body.removeChild(textArea);
|
|
72
|
+
if (successful) {
|
|
73
|
+
resolve(true);
|
|
74
|
+
} else {
|
|
75
|
+
reject(new Error("Fallback copy failed"));
|
|
76
|
+
}
|
|
77
|
+
} catch (err) {
|
|
78
|
+
document.body.removeChild(textArea);
|
|
79
|
+
reject(err);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
function useClipboardPro(options = {}) {
|
|
85
|
+
const { clearAfter = 2e3, announce = true } = options;
|
|
86
|
+
const state = react.useSyncExternalStore(
|
|
87
|
+
(callback) => {
|
|
88
|
+
const { unsubscribe } = clipboardStore.subscribe(callback);
|
|
89
|
+
return unsubscribe;
|
|
90
|
+
},
|
|
91
|
+
() => clipboardStore.getSnapshot()
|
|
92
|
+
);
|
|
93
|
+
const timeoutRef = react.useRef(null);
|
|
94
|
+
const copy = react.useCallback(async (text) => {
|
|
95
|
+
if (!text) return false;
|
|
96
|
+
try {
|
|
97
|
+
if (navigator?.clipboard?.writeText) {
|
|
98
|
+
await navigator.clipboard.writeText(text);
|
|
99
|
+
} else {
|
|
100
|
+
await fallbackCopyTextToClipboard(text);
|
|
101
|
+
}
|
|
102
|
+
clipboardStore.send({ type: "COPY_SUCCESS", text });
|
|
103
|
+
if (announce) {
|
|
104
|
+
announceToScreenReader(`Copied to clipboard: ${text}`);
|
|
105
|
+
}
|
|
106
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
107
|
+
timeoutRef.current = setTimeout(() => {
|
|
108
|
+
clipboardStore.send({ type: "RESET" });
|
|
109
|
+
}, clearAfter);
|
|
110
|
+
return true;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
clipboardStore.send({ type: "COPY_ERROR", error: String(error) });
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}, [clearAfter, announce]);
|
|
116
|
+
const clearHistory = react.useCallback(() => {
|
|
117
|
+
clipboardStore.send({ type: "CLEAR_HISTORY" });
|
|
118
|
+
}, []);
|
|
119
|
+
react.useEffect(() => {
|
|
120
|
+
return () => {
|
|
121
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
122
|
+
};
|
|
123
|
+
}, []);
|
|
124
|
+
return {
|
|
125
|
+
copy,
|
|
126
|
+
copied: state.context.status === "copied",
|
|
127
|
+
status: state.context.status,
|
|
128
|
+
history: state.context.history,
|
|
129
|
+
error: state.context.error,
|
|
130
|
+
clearHistory
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function announceToScreenReader(message) {
|
|
134
|
+
let announcer = document.getElementById("clipboard-pro-announcer");
|
|
135
|
+
if (!announcer) {
|
|
136
|
+
announcer = document.createElement("div");
|
|
137
|
+
announcer.id = "clipboard-pro-announcer";
|
|
138
|
+
announcer.setAttribute("aria-live", "polite");
|
|
139
|
+
announcer.setAttribute("aria-atomic", "true");
|
|
140
|
+
announcer.style.position = "absolute";
|
|
141
|
+
announcer.style.width = "1px";
|
|
142
|
+
announcer.style.height = "1px";
|
|
143
|
+
announcer.style.margin = "-1px";
|
|
144
|
+
announcer.style.padding = "0";
|
|
145
|
+
announcer.style.overflow = "hidden";
|
|
146
|
+
announcer.style.clip = "rect(0, 0, 0, 0)";
|
|
147
|
+
announcer.style.whiteSpace = "nowrap";
|
|
148
|
+
announcer.style.border = "0";
|
|
149
|
+
document.body.appendChild(announcer);
|
|
150
|
+
}
|
|
151
|
+
announcer.textContent = "";
|
|
152
|
+
setTimeout(() => {
|
|
153
|
+
if (announcer) announcer.textContent = message;
|
|
154
|
+
}, 50);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
exports.useClipboardPro = useClipboardPro;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
type ClipboardStatus = 'idle' | 'copied' | 'error';
|
|
2
|
+
|
|
3
|
+
interface UseClipboardOptions {
|
|
4
|
+
/** How long to wait before resetting the 'copied' state back to 'idle' (ms) */
|
|
5
|
+
clearAfter?: number;
|
|
6
|
+
/** Whether to announce the copy event to screen readers via aria-live */
|
|
7
|
+
announce?: boolean;
|
|
8
|
+
}
|
|
9
|
+
declare function useClipboardPro(options?: UseClipboardOptions): {
|
|
10
|
+
copy: (text: string) => Promise<boolean>;
|
|
11
|
+
copied: boolean;
|
|
12
|
+
status: ClipboardStatus;
|
|
13
|
+
history: string[];
|
|
14
|
+
error: string | null;
|
|
15
|
+
clearHistory: () => void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export { useClipboardPro };
|
|
19
|
+
export type { UseClipboardOptions };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
type ClipboardStatus = 'idle' | 'copied' | 'error';
|
|
2
|
+
|
|
3
|
+
interface UseClipboardOptions {
|
|
4
|
+
/** How long to wait before resetting the 'copied' state back to 'idle' (ms) */
|
|
5
|
+
clearAfter?: number;
|
|
6
|
+
/** Whether to announce the copy event to screen readers via aria-live */
|
|
7
|
+
announce?: boolean;
|
|
8
|
+
}
|
|
9
|
+
declare function useClipboardPro(options?: UseClipboardOptions): {
|
|
10
|
+
copy: (text: string) => Promise<boolean>;
|
|
11
|
+
copied: boolean;
|
|
12
|
+
status: ClipboardStatus;
|
|
13
|
+
history: string[];
|
|
14
|
+
error: string | null;
|
|
15
|
+
clearHistory: () => void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export { useClipboardPro };
|
|
19
|
+
export type { UseClipboardOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
type ClipboardStatus = 'idle' | 'copied' | 'error';
|
|
2
|
+
|
|
3
|
+
interface UseClipboardOptions {
|
|
4
|
+
/** How long to wait before resetting the 'copied' state back to 'idle' (ms) */
|
|
5
|
+
clearAfter?: number;
|
|
6
|
+
/** Whether to announce the copy event to screen readers via aria-live */
|
|
7
|
+
announce?: boolean;
|
|
8
|
+
}
|
|
9
|
+
declare function useClipboardPro(options?: UseClipboardOptions): {
|
|
10
|
+
copy: (text: string) => Promise<boolean>;
|
|
11
|
+
copied: boolean;
|
|
12
|
+
status: ClipboardStatus;
|
|
13
|
+
history: string[];
|
|
14
|
+
error: string | null;
|
|
15
|
+
clearHistory: () => void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export { useClipboardPro };
|
|
19
|
+
export type { UseClipboardOptions };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { useSyncExternalStore, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
import { createStore } from '@xstate/store';
|
|
3
|
+
|
|
4
|
+
const clipboardStore = createStore({
|
|
5
|
+
context: {
|
|
6
|
+
status: "idle",
|
|
7
|
+
history: [],
|
|
8
|
+
error: null
|
|
9
|
+
},
|
|
10
|
+
on: {
|
|
11
|
+
COPY_SUCCESS: (context, event) => {
|
|
12
|
+
const newHistory = [
|
|
13
|
+
event.text,
|
|
14
|
+
...context.history.filter((item) => item !== event.text)
|
|
15
|
+
].slice(0, 5);
|
|
16
|
+
return {
|
|
17
|
+
...context,
|
|
18
|
+
status: "copied",
|
|
19
|
+
// <-- "as const" prevents TS from changing this to a generic string
|
|
20
|
+
history: newHistory,
|
|
21
|
+
error: null
|
|
22
|
+
};
|
|
23
|
+
},
|
|
24
|
+
COPY_ERROR: (context, event) => {
|
|
25
|
+
return {
|
|
26
|
+
...context,
|
|
27
|
+
status: "error",
|
|
28
|
+
error: event.error
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
RESET: (context) => {
|
|
32
|
+
return {
|
|
33
|
+
...context,
|
|
34
|
+
status: "idle",
|
|
35
|
+
error: null
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
CLEAR_HISTORY: (context) => {
|
|
39
|
+
return {
|
|
40
|
+
...context,
|
|
41
|
+
history: []
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const fallbackCopyTextToClipboard = (text) => {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
const textArea = document.createElement("textarea");
|
|
50
|
+
textArea.value = text;
|
|
51
|
+
textArea.style.top = "0";
|
|
52
|
+
textArea.style.left = "0";
|
|
53
|
+
textArea.style.position = "fixed";
|
|
54
|
+
textArea.style.fontSize = "16px";
|
|
55
|
+
textArea.style.width = "1px";
|
|
56
|
+
textArea.style.height = "1px";
|
|
57
|
+
textArea.style.padding = "0";
|
|
58
|
+
textArea.style.border = "none";
|
|
59
|
+
textArea.style.outline = "none";
|
|
60
|
+
textArea.style.boxShadow = "none";
|
|
61
|
+
textArea.style.background = "transparent";
|
|
62
|
+
textArea.setAttribute("aria-hidden", "true");
|
|
63
|
+
document.body.appendChild(textArea);
|
|
64
|
+
textArea.focus();
|
|
65
|
+
textArea.select();
|
|
66
|
+
textArea.setSelectionRange(0, 999999);
|
|
67
|
+
try {
|
|
68
|
+
const successful = document.execCommand("copy");
|
|
69
|
+
document.body.removeChild(textArea);
|
|
70
|
+
if (successful) {
|
|
71
|
+
resolve(true);
|
|
72
|
+
} else {
|
|
73
|
+
reject(new Error("Fallback copy failed"));
|
|
74
|
+
}
|
|
75
|
+
} catch (err) {
|
|
76
|
+
document.body.removeChild(textArea);
|
|
77
|
+
reject(err);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
function useClipboardPro(options = {}) {
|
|
83
|
+
const { clearAfter = 2e3, announce = true } = options;
|
|
84
|
+
const state = useSyncExternalStore(
|
|
85
|
+
(callback) => {
|
|
86
|
+
const { unsubscribe } = clipboardStore.subscribe(callback);
|
|
87
|
+
return unsubscribe;
|
|
88
|
+
},
|
|
89
|
+
() => clipboardStore.getSnapshot()
|
|
90
|
+
);
|
|
91
|
+
const timeoutRef = useRef(null);
|
|
92
|
+
const copy = useCallback(async (text) => {
|
|
93
|
+
if (!text) return false;
|
|
94
|
+
try {
|
|
95
|
+
if (navigator?.clipboard?.writeText) {
|
|
96
|
+
await navigator.clipboard.writeText(text);
|
|
97
|
+
} else {
|
|
98
|
+
await fallbackCopyTextToClipboard(text);
|
|
99
|
+
}
|
|
100
|
+
clipboardStore.send({ type: "COPY_SUCCESS", text });
|
|
101
|
+
if (announce) {
|
|
102
|
+
announceToScreenReader(`Copied to clipboard: ${text}`);
|
|
103
|
+
}
|
|
104
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
105
|
+
timeoutRef.current = setTimeout(() => {
|
|
106
|
+
clipboardStore.send({ type: "RESET" });
|
|
107
|
+
}, clearAfter);
|
|
108
|
+
return true;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
clipboardStore.send({ type: "COPY_ERROR", error: String(error) });
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}, [clearAfter, announce]);
|
|
114
|
+
const clearHistory = useCallback(() => {
|
|
115
|
+
clipboardStore.send({ type: "CLEAR_HISTORY" });
|
|
116
|
+
}, []);
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
return () => {
|
|
119
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
120
|
+
};
|
|
121
|
+
}, []);
|
|
122
|
+
return {
|
|
123
|
+
copy,
|
|
124
|
+
copied: state.context.status === "copied",
|
|
125
|
+
status: state.context.status,
|
|
126
|
+
history: state.context.history,
|
|
127
|
+
error: state.context.error,
|
|
128
|
+
clearHistory
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function announceToScreenReader(message) {
|
|
132
|
+
let announcer = document.getElementById("clipboard-pro-announcer");
|
|
133
|
+
if (!announcer) {
|
|
134
|
+
announcer = document.createElement("div");
|
|
135
|
+
announcer.id = "clipboard-pro-announcer";
|
|
136
|
+
announcer.setAttribute("aria-live", "polite");
|
|
137
|
+
announcer.setAttribute("aria-atomic", "true");
|
|
138
|
+
announcer.style.position = "absolute";
|
|
139
|
+
announcer.style.width = "1px";
|
|
140
|
+
announcer.style.height = "1px";
|
|
141
|
+
announcer.style.margin = "-1px";
|
|
142
|
+
announcer.style.padding = "0";
|
|
143
|
+
announcer.style.overflow = "hidden";
|
|
144
|
+
announcer.style.clip = "rect(0, 0, 0, 0)";
|
|
145
|
+
announcer.style.whiteSpace = "nowrap";
|
|
146
|
+
announcer.style.border = "0";
|
|
147
|
+
document.body.appendChild(announcer);
|
|
148
|
+
}
|
|
149
|
+
announcer.textContent = "";
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
if (announcer) announcer.textContent = message;
|
|
152
|
+
}, 50);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export { useClipboardPro };
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "use-clipboard-pro",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "A bulletproof React clipboard hook with global history, ARIA support, and iOS fallbacks.",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"package.json"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "unbuild",
|
|
24
|
+
"dev": "unbuild --stub"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/udhayavirat18/use-clipboard-pro.git"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"react",
|
|
32
|
+
"clipboard",
|
|
33
|
+
"hook",
|
|
34
|
+
"use-clipboard",
|
|
35
|
+
"copy",
|
|
36
|
+
"xstate",
|
|
37
|
+
"history",
|
|
38
|
+
"a11y",
|
|
39
|
+
"accessibility",
|
|
40
|
+
"ios-fallback"
|
|
41
|
+
],
|
|
42
|
+
"author": "UdhayaKumar",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/udhayavirat18/use-clipboard-pro/issues"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/udhayavirat18/use-clipboard-pro#readme",
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@xstate/store": "^3.16.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/react": "^19.2.14",
|
|
53
|
+
"typescript": "^5.9.3",
|
|
54
|
+
"unbuild": "^3.6.1"
|
|
55
|
+
},
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"react": "^19.2.4"
|
|
58
|
+
}
|
|
59
|
+
}
|