react-hook-toolkit 3.0.3 → 3.0.4

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.
@@ -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
- export declare function useSpeak(text: string): {
15
- speak: () => void;
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 function useSpeak(): ISpeakResult;
18
38
  export declare function useCountUp(target: number, duration: number): {
19
39
  count: number;
20
40
  error: Error | null;
@@ -60,18 +60,136 @@ export function useCss(css) {
60
60
  }, [css]);
61
61
  return { error };
62
62
  }
63
- export function useSpeak(text) {
63
+ const IS_SUPPORTED = typeof window !== 'undefined' && 'speechSynthesis' in window;
64
+ export function useSpeak() {
65
+ const [status, setStatus] = useState('idle');
64
66
  const [error, setError] = useState(null);
65
- const speak = () => {
66
- try {
67
- const utterance = new SpeechSynthesisUtterance(text);
68
- speechSynthesis.speak(utterance);
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
- catch (err) {
71
- setError(err instanceof Error ? err : new Error('Failed to speak'));
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
+ setError(null);
139
+ // Build utterance
140
+ const utterance = new SpeechSynthesisUtterance(trimmed);
141
+ utterance.rate = (_c = options.rate) !== null && _c !== void 0 ? _c : 1;
142
+ utterance.pitch = (_d = options.pitch) !== null && _d !== void 0 ? _d : 1;
143
+ utterance.volume = (_e = options.volume) !== null && _e !== void 0 ? _e : 1;
144
+ if (options.lang)
145
+ utterance.lang = options.lang;
146
+ if (options.voice)
147
+ utterance.voice = options.voice;
148
+ // ── Event handlers (read callbacks from ref — always fresh) ──
149
+ utterance.onstart = () => {
150
+ var _a, _b;
151
+ safeSetStatus('playing');
152
+ (_b = (_a = callbacksRef.current).onStart) === null || _b === void 0 ? void 0 : _b.call(_a);
153
+ };
154
+ utterance.onpause = () => {
155
+ var _a, _b;
156
+ safeSetStatus('paused');
157
+ (_b = (_a = callbacksRef.current).onPause) === null || _b === void 0 ? void 0 : _b.call(_a);
158
+ };
159
+ utterance.onresume = () => {
160
+ safeSetStatus('playing');
161
+ };
162
+ utterance.onend = () => {
163
+ var _a, _b;
164
+ safeSetStatus('ended');
165
+ (_b = (_a = callbacksRef.current).onEnd) === null || _b === void 0 ? void 0 : _b.call(_a);
166
+ utteranceRef.current = null;
167
+ };
168
+ utterance.onerror = (e) => {
169
+ var _a, _b;
170
+ // 'interrupted' fires on manual stop() — not a real error
171
+ if (e.error === 'interrupted' || e.error === 'canceled')
172
+ return;
173
+ const err = new Error(`Speech error: ${e.error}`);
174
+ safeSetStatus('error');
175
+ safeSetError(err);
176
+ (_b = (_a = callbacksRef.current).onError) === null || _b === void 0 ? void 0 : _b.call(_a, err);
177
+ utteranceRef.current = null;
178
+ };
179
+ utteranceRef.current = utterance;
180
+ window.speechSynthesis.speak(utterance);
181
+ }, [safeSetStatus, safeSetError]); // minimal deps — text passed at call time
182
+ return {
183
+ speak,
184
+ stop,
185
+ pause,
186
+ resume,
187
+ isPlaying,
188
+ isPaused,
189
+ status,
190
+ isSupported: IS_SUPPORTED,
191
+ error,
73
192
  };
74
- return { speak, error };
75
193
  }
76
194
  export function useCountUp(target, duration) {
77
195
  const [count, setCount] = useState(0);
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",
3
+ "version": "3.0.4",
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",