react-hook-toolkit 3.0.3 → 3.0.5
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/dist/chunk1516/chunk0021.d.ts +23 -3
- package/dist/chunk1516/chunk0021.js +133 -9
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/package.json +1 -1
|
@@ -11,10 +11,30 @@ export declare function useIsMounted(): boolean;
|
|
|
11
11
|
export declare function useCss(css: string): {
|
|
12
12
|
error: Error | null;
|
|
13
13
|
};
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
type VoiceStatus = 'idle' | 'playing' | 'paused' | 'ended' | 'error';
|
|
15
|
+
interface ISpeakOptions {
|
|
16
|
+
rate?: number;
|
|
17
|
+
pitch?: number;
|
|
18
|
+
volume?: number;
|
|
19
|
+
lang?: string;
|
|
20
|
+
voice?: SpeechSynthesisVoice | null;
|
|
21
|
+
onStart?: () => void;
|
|
22
|
+
onEnd?: () => void;
|
|
23
|
+
onPause?: () => void;
|
|
24
|
+
onError?: (error: Error) => void;
|
|
25
|
+
}
|
|
26
|
+
interface ISpeakResult {
|
|
27
|
+
speak: (text: string, options?: ISpeakOptions) => void;
|
|
28
|
+
stop: () => void;
|
|
29
|
+
pause: () => void;
|
|
30
|
+
resume: () => void;
|
|
31
|
+
isPlaying: boolean;
|
|
32
|
+
isPaused: boolean;
|
|
33
|
+
status: VoiceStatus;
|
|
34
|
+
isSupported: boolean;
|
|
16
35
|
error: Error | null;
|
|
17
|
-
}
|
|
36
|
+
}
|
|
37
|
+
export declare const useSpeak: () => ISpeakResult;
|
|
18
38
|
export declare function useCountUp(target: number, duration: number): {
|
|
19
39
|
count: number;
|
|
20
40
|
error: Error | null;
|
|
@@ -60,19 +60,143 @@ export function useCss(css) {
|
|
|
60
60
|
}, [css]);
|
|
61
61
|
return { error };
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
const IS_SUPPORTED = typeof window !== 'undefined' && 'speechSynthesis' in window;
|
|
64
|
+
export const useSpeak = () => {
|
|
65
|
+
const [status, setStatus] = useState('idle');
|
|
64
66
|
const [error, setError] = useState(null);
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
const isMounted = useRef(true);
|
|
68
|
+
const utteranceRef = useRef(null);
|
|
69
|
+
const callbacksRef = useRef({});
|
|
70
|
+
// ── Derived state (no extra useState) ──────────────────────
|
|
71
|
+
const isPlaying = status === 'playing';
|
|
72
|
+
const isPaused = status === 'paused';
|
|
73
|
+
// ── Mount / Unmount ────────────────────────────────────────
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
isMounted.current = true;
|
|
76
|
+
return () => {
|
|
77
|
+
var _a;
|
|
78
|
+
isMounted.current = false;
|
|
79
|
+
(_a = window.speechSynthesis) === null || _a === void 0 ? void 0 : _a.cancel();
|
|
80
|
+
utteranceRef.current = null;
|
|
81
|
+
};
|
|
82
|
+
}, []);
|
|
83
|
+
// ── Stable status setter ───────────────────────────────────
|
|
84
|
+
const safeSetStatus = useCallback((next) => {
|
|
85
|
+
if (isMounted.current)
|
|
86
|
+
setStatus(next);
|
|
87
|
+
}, []);
|
|
88
|
+
const safeSetError = useCallback((err) => {
|
|
89
|
+
if (isMounted.current)
|
|
90
|
+
setError(err);
|
|
91
|
+
}, []);
|
|
92
|
+
// ── Controls ───────────────────────────────────────────────
|
|
93
|
+
const stop = useCallback(() => {
|
|
94
|
+
var _a;
|
|
95
|
+
(_a = window.speechSynthesis) === null || _a === void 0 ? void 0 : _a.cancel();
|
|
96
|
+
safeSetStatus('idle');
|
|
97
|
+
}, [safeSetStatus]);
|
|
98
|
+
const pause = useCallback(() => {
|
|
99
|
+
var _a;
|
|
100
|
+
if (!isPlaying)
|
|
101
|
+
return;
|
|
102
|
+
(_a = window.speechSynthesis) === null || _a === void 0 ? void 0 : _a.pause();
|
|
103
|
+
safeSetStatus('paused');
|
|
104
|
+
}, [isPlaying, safeSetStatus]);
|
|
105
|
+
const resume = useCallback(() => {
|
|
106
|
+
var _a;
|
|
107
|
+
if (!isPaused)
|
|
108
|
+
return;
|
|
109
|
+
(_a = window.speechSynthesis) === null || _a === void 0 ? void 0 : _a.resume();
|
|
110
|
+
safeSetStatus('playing');
|
|
111
|
+
}, [isPaused, safeSetStatus]);
|
|
112
|
+
// ── Core speak ─────────────────────────────────────────────
|
|
113
|
+
const speak = useCallback((text, options = {}) => {
|
|
114
|
+
var _a, _b, _c, _d, _e;
|
|
115
|
+
// Guards
|
|
116
|
+
if (!IS_SUPPORTED) {
|
|
117
|
+
const err = new Error('SpeechSynthesis is not supported in this browser');
|
|
118
|
+
safeSetError(err);
|
|
119
|
+
(_a = options.onError) === null || _a === void 0 ? void 0 : _a.call(options, err);
|
|
120
|
+
return;
|
|
69
121
|
}
|
|
70
|
-
|
|
71
|
-
|
|
122
|
+
const trimmed = text === null || text === void 0 ? void 0 : text.trim();
|
|
123
|
+
if (!trimmed) {
|
|
124
|
+
const err = new Error('Text cannot be empty');
|
|
125
|
+
safeSetError(err);
|
|
126
|
+
(_b = options.onError) === null || _b === void 0 ? void 0 : _b.call(options, err);
|
|
127
|
+
return;
|
|
72
128
|
}
|
|
129
|
+
// Store latest callbacks in ref — avoids stale closure in handlers
|
|
130
|
+
callbacksRef.current = {
|
|
131
|
+
onStart: options.onStart,
|
|
132
|
+
onEnd: options.onEnd,
|
|
133
|
+
onPause: options.onPause,
|
|
134
|
+
onError: options.onError,
|
|
135
|
+
};
|
|
136
|
+
// Cancel any ongoing speech
|
|
137
|
+
window.speechSynthesis.cancel();
|
|
138
|
+
if (isMounted.current)
|
|
139
|
+
setError(null);
|
|
140
|
+
// Build utterance
|
|
141
|
+
const utterance = new SpeechSynthesisUtterance(trimmed);
|
|
142
|
+
utterance.rate = (_c = options.rate) !== null && _c !== void 0 ? _c : 1;
|
|
143
|
+
utterance.pitch = (_d = options.pitch) !== null && _d !== void 0 ? _d : 1;
|
|
144
|
+
utterance.volume = (_e = options.volume) !== null && _e !== void 0 ? _e : 1;
|
|
145
|
+
if (options.lang)
|
|
146
|
+
utterance.lang = options.lang;
|
|
147
|
+
if (options.voice)
|
|
148
|
+
utterance.voice = options.voice;
|
|
149
|
+
// ── Event handlers (read callbacks from ref — always fresh) ──
|
|
150
|
+
utterance.onstart = () => {
|
|
151
|
+
var _a, _b;
|
|
152
|
+
safeSetStatus('playing');
|
|
153
|
+
(_b = (_a = callbacksRef.current).onStart) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
154
|
+
};
|
|
155
|
+
utterance.onpause = () => {
|
|
156
|
+
var _a, _b;
|
|
157
|
+
safeSetStatus('paused');
|
|
158
|
+
(_b = (_a = callbacksRef.current).onPause) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
159
|
+
};
|
|
160
|
+
utterance.onresume = () => {
|
|
161
|
+
safeSetStatus('playing');
|
|
162
|
+
};
|
|
163
|
+
utterance.onend = () => {
|
|
164
|
+
var _a, _b;
|
|
165
|
+
safeSetStatus('ended');
|
|
166
|
+
(_b = (_a = callbacksRef.current).onEnd) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
167
|
+
utteranceRef.current = null;
|
|
168
|
+
};
|
|
169
|
+
utterance.onerror = (e) => {
|
|
170
|
+
var _a, _b;
|
|
171
|
+
// 'interrupted' fires on manual stop() — not a real error
|
|
172
|
+
if (e.error === 'interrupted' || e.error === 'canceled')
|
|
173
|
+
return;
|
|
174
|
+
const err = new Error(`Speech error: ${e.error}`);
|
|
175
|
+
safeSetStatus('error');
|
|
176
|
+
safeSetError(err);
|
|
177
|
+
(_b = (_a = callbacksRef.current).onError) === null || _b === void 0 ? void 0 : _b.call(_a, err);
|
|
178
|
+
utteranceRef.current = null;
|
|
179
|
+
};
|
|
180
|
+
// Assign ref BEFORE timeout — prevents Chrome GC dropping utterance mid-speech
|
|
181
|
+
utteranceRef.current = utterance;
|
|
182
|
+
// Chrome gets stuck in paused state after cancel() — delay lets it settle
|
|
183
|
+
setTimeout(() => {
|
|
184
|
+
if (isMounted.current)
|
|
185
|
+
window.speechSynthesis.speak(utterance);
|
|
186
|
+
}, 100);
|
|
187
|
+
}, [safeSetStatus, safeSetError]); // minimal deps — text passed at call time
|
|
188
|
+
return {
|
|
189
|
+
speak,
|
|
190
|
+
stop,
|
|
191
|
+
pause,
|
|
192
|
+
resume,
|
|
193
|
+
isPlaying,
|
|
194
|
+
isPaused,
|
|
195
|
+
status,
|
|
196
|
+
isSupported: IS_SUPPORTED,
|
|
197
|
+
error,
|
|
73
198
|
};
|
|
74
|
-
|
|
75
|
-
}
|
|
199
|
+
};
|
|
76
200
|
export function useCountUp(target, duration) {
|
|
77
201
|
const [count, setCount] = useState(0);
|
|
78
202
|
const [error, setError] = useState(null);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import 'nprogress/nprogress.css';
|
|
2
1
|
import { ReactHooksWrapper, getHook, setHook } from './chunk1415/chunk143';
|
|
3
2
|
import { useHistoryState, useIdle, useIsFirstRender, useList, useLockBodyScroll, useLongPress, useRecentSearch, useSpeech, usePermission, usePageLeave, useMotion, useHoverDirty, useBeforeUnload, useClickAway, useResponsive, useUnmountedRef } from './chunk1516/chunk726433';
|
|
4
3
|
import { useBrowser, useMenuNavigation, useNavigationState, useRouter } from './chunk1516/chunk0022';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import 'nprogress/nprogress.css';
|
|
2
1
|
import { ReactHooksWrapper, getHook, setHook } from './chunk1415/chunk143';
|
|
3
2
|
import { useHistoryState, useIdle, useIsFirstRender, useList, useLockBodyScroll, useLongPress, useRecentSearch, useSpeech, usePermission, usePageLeave, useMotion, useHoverDirty, useBeforeUnload, useClickAway, useResponsive, useUnmountedRef } from './chunk1516/chunk726433';
|
|
4
3
|
import { useBrowser, useMenuNavigation, useNavigationState, useRouter } from './chunk1516/chunk0022';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-hook-toolkit",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.5",
|
|
4
4
|
"description": "Ultimate package for React developers, offering a powerful collection of hooks and components to enhance their development experience.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|