@grazziotin/react-components-next 2.0.0 → 2.2.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.
@@ -1,121 +1,85 @@
1
1
  import react__default from 'react';
2
2
 
3
3
  type SayFeedbackType = "info" | "success" | "warning" | "error" | "default";
4
- interface SayNotifyOptions {
5
- type?: SayFeedbackType;
6
- autoClose?: number;
7
- }
8
4
  interface SayCallOptions {
5
+ /** Tipo do toast. */
6
+ type: SayFeedbackType;
9
7
  /**
10
- * Tipo de feedback. desativa notificação.
11
- * Padrão: (via do hook).
12
- */
13
- type?: SayFeedbackType | false;
14
- /**
15
- * Exibe notificação via .
16
- * Padrão: .
8
+ * Exibe notificação toast integrada.
9
+ * Padrão: `true`.
17
10
  */
18
11
  notify?: boolean;
19
12
  /**
20
- * Dispara vibração conforme .
21
- * Padrão: .
13
+ * Dispara vibração conforme `shouldVibrate`.
14
+ * Padrão: `true`.
22
15
  */
23
16
  vibrate?: boolean;
24
- /** Duração da notificação em ms (repasse para ). */
17
+ /** Duração da notificação em ms. Padrão: `notifyAutoClose` do hook. */
25
18
  autoClose?: number;
26
19
  }
27
20
  interface UseSayOptions {
28
- /**
29
- * Callback de notificação visual (ex.: toast).
30
- * Omitir para não exibir notificação.
31
- */
32
- onNotify?: (text: string, options: SayNotifyOptions) => void;
33
- /**
34
- * Tipo padrão da notificação.
35
- * Padrão: .
36
- */
37
- defaultNotifyType?: SayFeedbackType;
38
21
  /**
39
22
  * Auto-close padrão da notificação em ms.
40
- * Padrão: .
23
+ * Padrão: `4500`.
41
24
  */
42
- defaultNotifyAutoClose?: number;
25
+ notifyAutoClose?: number;
43
26
  /**
44
27
  * Duração padrão da vibração em ms.
45
- * Padrão: .
28
+ * Padrão: `500`.
46
29
  */
47
30
  vibrateDuration?: number;
48
31
  /**
49
- * Define se deve vibrar para o informado.
50
- * Padrão: ( quando é omitido ou ).
32
+ * Define se deve vibrar para o `type` informado.
33
+ * Padrão: vibra quando `type` é `"warning"`.
51
34
  */
52
- shouldVibrate?: (type?: SayFeedbackType | false) => boolean;
35
+ shouldVibrate?: (type: SayFeedbackType) => boolean;
53
36
  }
54
37
  interface UseSayReturn {
55
- say: (text: string, options?: SayCallOptions) => void;
38
+ /**
39
+ * Dispara fala, toast (quando `notify !== false`) e vibração.
40
+ * @param text - Texto falado.
41
+ * @param options - Controle de notificação, vibração e tipo do toast.
42
+ */
56
43
  isSpeaking: boolean;
57
44
  textSpeaking: string;
58
45
  setIsSpeaking: React.Dispatch<React.SetStateAction<boolean>>;
46
+ handleSay: (text: string, options: SayCallOptions) => void;
59
47
  }
60
48
  interface SayProps {
61
49
  isSpeaking: boolean;
62
50
  text: string;
63
51
  setIsSpeaking: React.Dispatch<React.SetStateAction<boolean>>;
64
52
  /**
65
- * Nome da voz do .
66
- * Padrão: .
53
+ * Nome da voz do `speechSynthesis`.
54
+ * Padrão: `"Google português do Brasil"`.
67
55
  */
68
56
  voice?: string;
69
57
  /**
70
58
  * Velocidade da fala.
71
- * Padrão: .
59
+ * Padrão: `1.4`.
72
60
  */
73
61
  rate?: number;
74
62
  /**
75
63
  * Tom da voz.
76
- * Padrão: .
64
+ * Padrão: `0.8`.
77
65
  */
78
66
  pitch?: number;
79
67
  /**
80
68
  * Volume da fala.
81
- * Padrão: .
69
+ * Padrão: `1`.
82
70
  */
83
71
  volume?: number;
84
72
  onEnd?: () => void;
85
73
  onStart?: () => void;
86
74
  }
87
75
 
88
- /**
89
- * Hook para disparar fala, notificação e vibração de forma configurável.
90
- *
91
- * @example
92
- * ```tsx
93
- * const { say, isSpeaking, textSpeaking, setIsSpeaking } = useSay({
94
- * onNotify: (text, { type }) => toast(text, { type }),
95
- * });
96
- *
97
- * <Say
98
- * isSpeaking={isSpeaking}
99
- * text={textSpeaking}
100
- * setIsSpeaking={setIsSpeaking}
101
- * />
102
- *
103
- * <button type="button" onClick={() => say("Operação concluída")}>
104
- * Falar
105
- * </button>
106
- * ```
107
- */
108
- declare function useSay({ onNotify, defaultNotifyType, vibrateDuration, defaultNotifyAutoClose, shouldVibrate, }?: UseSayOptions): UseSayReturn;
109
-
110
76
  /**
111
77
  * Sintetiza texto em fala via Web Speech API (`react-say`).
112
- * Use com {@link useSay} para controlar `isSpeaking`, `text` e `setIsSpeaking`.
78
+ * Inclui toast integrado para notificações do {@link useSay}.
113
79
  *
114
80
  * @example
115
81
  * ```tsx
116
- * const { say, isSpeaking, textSpeaking, setIsSpeaking } = useSay({
117
- * onNotify: (text, { type }) => toast(text, { type }),
118
- * });
82
+ * const { handleSay, isSpeaking, textSpeaking, setIsSpeaking } = useSay();
119
83
  *
120
84
  * return (
121
85
  * <>
@@ -123,14 +87,34 @@ declare function useSay({ onNotify, defaultNotifyType, vibrateDuration, defaultN
123
87
  * isSpeaking={isSpeaking}
124
88
  * text={textSpeaking}
125
89
  * setIsSpeaking={setIsSpeaking}
126
- * voice="Google português do Brasil"
127
- * rate={1.4}
128
90
  * />
129
- * <button type="button" onClick={() => say("Pronto")}>Falar</button>
91
+ * <button type="button" onClick={() => handleSay("Pronto", { type: "success" })}>
92
+ * Falar
93
+ * </button>
130
94
  * </>
131
95
  * );
132
96
  * ```
133
97
  */
134
- declare function Say({ text, onEnd, onStart, isSpeaking, setIsSpeaking, rate, voice, pitch, volume, }: Readonly<SayProps>): react__default.ReactElement | null;
98
+ declare function Say({ text, onEnd, onStart, isSpeaking, setIsSpeaking, rate, voice, pitch, volume, }: Readonly<SayProps>): react__default.ReactElement;
99
+
100
+ /**
101
+ * Hook para disparar fala, toast integrado e vibração.
102
+ *
103
+ * @example
104
+ * ```tsx
105
+ * const { handleSay, isSpeaking, textSpeaking, setIsSpeaking } = useSay();
106
+ *
107
+ * <Say
108
+ * isSpeaking={isSpeaking}
109
+ * text={textSpeaking}
110
+ * setIsSpeaking={setIsSpeaking}
111
+ * />
112
+ *
113
+ * handleSay("Operação concluída", { type: "success" });
114
+ * handleSay("Atenção", { type: "warning" });
115
+ * handleSay("Apenas fala", { type: "default", notify: false });
116
+ * ```
117
+ */
118
+ declare function useSay({ notifyAutoClose, vibrateDuration, shouldVibrate, }?: UseSayOptions): UseSayReturn;
135
119
 
136
- export { Say, type SayCallOptions, type SayFeedbackType, type SayNotifyOptions, type SayProps, type UseSayOptions, type UseSayReturn, useSay };
120
+ export { Say, type SayCallOptions, type SayFeedbackType, type SayProps, type UseSayOptions, type UseSayReturn, useSay };
@@ -1,121 +1,85 @@
1
1
  import react__default from 'react';
2
2
 
3
3
  type SayFeedbackType = "info" | "success" | "warning" | "error" | "default";
4
- interface SayNotifyOptions {
5
- type?: SayFeedbackType;
6
- autoClose?: number;
7
- }
8
4
  interface SayCallOptions {
5
+ /** Tipo do toast. */
6
+ type: SayFeedbackType;
9
7
  /**
10
- * Tipo de feedback. desativa notificação.
11
- * Padrão: (via do hook).
12
- */
13
- type?: SayFeedbackType | false;
14
- /**
15
- * Exibe notificação via .
16
- * Padrão: .
8
+ * Exibe notificação toast integrada.
9
+ * Padrão: `true`.
17
10
  */
18
11
  notify?: boolean;
19
12
  /**
20
- * Dispara vibração conforme .
21
- * Padrão: .
13
+ * Dispara vibração conforme `shouldVibrate`.
14
+ * Padrão: `true`.
22
15
  */
23
16
  vibrate?: boolean;
24
- /** Duração da notificação em ms (repasse para ). */
17
+ /** Duração da notificação em ms. Padrão: `notifyAutoClose` do hook. */
25
18
  autoClose?: number;
26
19
  }
27
20
  interface UseSayOptions {
28
- /**
29
- * Callback de notificação visual (ex.: toast).
30
- * Omitir para não exibir notificação.
31
- */
32
- onNotify?: (text: string, options: SayNotifyOptions) => void;
33
- /**
34
- * Tipo padrão da notificação.
35
- * Padrão: .
36
- */
37
- defaultNotifyType?: SayFeedbackType;
38
21
  /**
39
22
  * Auto-close padrão da notificação em ms.
40
- * Padrão: .
23
+ * Padrão: `4500`.
41
24
  */
42
- defaultNotifyAutoClose?: number;
25
+ notifyAutoClose?: number;
43
26
  /**
44
27
  * Duração padrão da vibração em ms.
45
- * Padrão: .
28
+ * Padrão: `500`.
46
29
  */
47
30
  vibrateDuration?: number;
48
31
  /**
49
- * Define se deve vibrar para o informado.
50
- * Padrão: ( quando é omitido ou ).
32
+ * Define se deve vibrar para o `type` informado.
33
+ * Padrão: vibra quando `type` é `"warning"`.
51
34
  */
52
- shouldVibrate?: (type?: SayFeedbackType | false) => boolean;
35
+ shouldVibrate?: (type: SayFeedbackType) => boolean;
53
36
  }
54
37
  interface UseSayReturn {
55
- say: (text: string, options?: SayCallOptions) => void;
38
+ /**
39
+ * Dispara fala, toast (quando `notify !== false`) e vibração.
40
+ * @param text - Texto falado.
41
+ * @param options - Controle de notificação, vibração e tipo do toast.
42
+ */
56
43
  isSpeaking: boolean;
57
44
  textSpeaking: string;
58
45
  setIsSpeaking: React.Dispatch<React.SetStateAction<boolean>>;
46
+ handleSay: (text: string, options: SayCallOptions) => void;
59
47
  }
60
48
  interface SayProps {
61
49
  isSpeaking: boolean;
62
50
  text: string;
63
51
  setIsSpeaking: React.Dispatch<React.SetStateAction<boolean>>;
64
52
  /**
65
- * Nome da voz do .
66
- * Padrão: .
53
+ * Nome da voz do `speechSynthesis`.
54
+ * Padrão: `"Google português do Brasil"`.
67
55
  */
68
56
  voice?: string;
69
57
  /**
70
58
  * Velocidade da fala.
71
- * Padrão: .
59
+ * Padrão: `1.4`.
72
60
  */
73
61
  rate?: number;
74
62
  /**
75
63
  * Tom da voz.
76
- * Padrão: .
64
+ * Padrão: `0.8`.
77
65
  */
78
66
  pitch?: number;
79
67
  /**
80
68
  * Volume da fala.
81
- * Padrão: .
69
+ * Padrão: `1`.
82
70
  */
83
71
  volume?: number;
84
72
  onEnd?: () => void;
85
73
  onStart?: () => void;
86
74
  }
87
75
 
88
- /**
89
- * Hook para disparar fala, notificação e vibração de forma configurável.
90
- *
91
- * @example
92
- * ```tsx
93
- * const { say, isSpeaking, textSpeaking, setIsSpeaking } = useSay({
94
- * onNotify: (text, { type }) => toast(text, { type }),
95
- * });
96
- *
97
- * <Say
98
- * isSpeaking={isSpeaking}
99
- * text={textSpeaking}
100
- * setIsSpeaking={setIsSpeaking}
101
- * />
102
- *
103
- * <button type="button" onClick={() => say("Operação concluída")}>
104
- * Falar
105
- * </button>
106
- * ```
107
- */
108
- declare function useSay({ onNotify, defaultNotifyType, vibrateDuration, defaultNotifyAutoClose, shouldVibrate, }?: UseSayOptions): UseSayReturn;
109
-
110
76
  /**
111
77
  * Sintetiza texto em fala via Web Speech API (`react-say`).
112
- * Use com {@link useSay} para controlar `isSpeaking`, `text` e `setIsSpeaking`.
78
+ * Inclui toast integrado para notificações do {@link useSay}.
113
79
  *
114
80
  * @example
115
81
  * ```tsx
116
- * const { say, isSpeaking, textSpeaking, setIsSpeaking } = useSay({
117
- * onNotify: (text, { type }) => toast(text, { type }),
118
- * });
82
+ * const { handleSay, isSpeaking, textSpeaking, setIsSpeaking } = useSay();
119
83
  *
120
84
  * return (
121
85
  * <>
@@ -123,14 +87,34 @@ declare function useSay({ onNotify, defaultNotifyType, vibrateDuration, defaultN
123
87
  * isSpeaking={isSpeaking}
124
88
  * text={textSpeaking}
125
89
  * setIsSpeaking={setIsSpeaking}
126
- * voice="Google português do Brasil"
127
- * rate={1.4}
128
90
  * />
129
- * <button type="button" onClick={() => say("Pronto")}>Falar</button>
91
+ * <button type="button" onClick={() => handleSay("Pronto", { type: "success" })}>
92
+ * Falar
93
+ * </button>
130
94
  * </>
131
95
  * );
132
96
  * ```
133
97
  */
134
- declare function Say({ text, onEnd, onStart, isSpeaking, setIsSpeaking, rate, voice, pitch, volume, }: Readonly<SayProps>): react__default.ReactElement | null;
98
+ declare function Say({ text, onEnd, onStart, isSpeaking, setIsSpeaking, rate, voice, pitch, volume, }: Readonly<SayProps>): react__default.ReactElement;
99
+
100
+ /**
101
+ * Hook para disparar fala, toast integrado e vibração.
102
+ *
103
+ * @example
104
+ * ```tsx
105
+ * const { handleSay, isSpeaking, textSpeaking, setIsSpeaking } = useSay();
106
+ *
107
+ * <Say
108
+ * isSpeaking={isSpeaking}
109
+ * text={textSpeaking}
110
+ * setIsSpeaking={setIsSpeaking}
111
+ * />
112
+ *
113
+ * handleSay("Operação concluída", { type: "success" });
114
+ * handleSay("Atenção", { type: "warning" });
115
+ * handleSay("Apenas fala", { type: "default", notify: false });
116
+ * ```
117
+ */
118
+ declare function useSay({ notifyAutoClose, vibrateDuration, shouldVibrate, }?: UseSayOptions): UseSayReturn;
135
119
 
136
- export { Say, type SayCallOptions, type SayFeedbackType, type SayNotifyOptions, type SayProps, type UseSayOptions, type UseSayReturn, useSay };
120
+ export { Say, type SayCallOptions, type SayFeedbackType, type SayProps, type UseSayOptions, type UseSayReturn, useSay };
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ require('react-toastify/dist/ReactToastify.css');
4
+ var reactToastify = require('react-toastify');
3
5
  var SayEngine = require('react-say');
4
6
  var react = require('react');
5
7
  var jsxRuntime = require('react/jsx-runtime');
@@ -8,7 +10,12 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
10
 
9
11
  var SayEngine__default = /*#__PURE__*/_interopDefault(SayEngine);
10
12
 
11
- // src/components/accessibility/say/utils/constants.ts
13
+ // src/components/accessibility/say/index.tsx
14
+ var DEFAULT_SAY_RATE = 1.4;
15
+ var DEFAULT_SAY_VOLUME = 1;
16
+ var DEFAULT_SAY_PITCH = 0.8;
17
+ var DEFAULT_SAY_VIBRATE_DURATION = 500;
18
+ var DEFAULT_SAY_VOICE = "Google portugu\xEAs do Brasil";
12
19
  var SPEECH_APIS = ["speechSynthesis", "SpeechSynthesisUtterance"];
13
20
  function hasSpeechSupport() {
14
21
  return SPEECH_APIS.every((api) => api in globalThis);
@@ -29,55 +36,11 @@ function getSpeechSupport() {
29
36
  SpeechSynthesisUtterance: globalThis.SpeechSynthesisUtterance
30
37
  };
31
38
  }
32
- var DEFAULT_SAY_VOICE = "Google portugu\xEAs do Brasil";
33
- var DEFAULT_SAY_RATE = 1.4;
34
- var DEFAULT_SAY_PITCH = 0.8;
35
- var DEFAULT_SAY_VOLUME = 1;
36
- var DEFAULT_SAY_NOTIFY_AUTO_CLOSE = 1500;
37
- var DEFAULT_SAY_VIBRATE_DURATION = 500;
38
39
  function defaultShouldVibrate(type) {
39
- return !type || type === "warning";
40
+ return type === "warning";
40
41
  }
41
-
42
- // src/functions/nvl/nvl.ts
43
- function nvl(value, defaultValue) {
44
- return value != null ? value : defaultValue;
45
- }
46
- function useSay({
47
- onNotify,
48
- defaultNotifyType = "success",
49
- vibrateDuration = DEFAULT_SAY_VIBRATE_DURATION,
50
- defaultNotifyAutoClose = DEFAULT_SAY_NOTIFY_AUTO_CLOSE,
51
- shouldVibrate = defaultShouldVibrate
52
- } = {}) {
53
- const [isSpeaking, setIsSpeaking] = react.useState(false);
54
- const [textSpeaking, setTextSpeaking] = react.useState("");
55
- const say = react.useCallback(
56
- (text, options) => {
57
- const type = options == null ? void 0 : options.type;
58
- const notify = nvl(options == null ? void 0 : options.notify, true);
59
- const vibrate = nvl(options == null ? void 0 : options.vibrate, true);
60
- setIsSpeaking(true);
61
- setTextSpeaking(text);
62
- if (notify && type !== false && onNotify) {
63
- onNotify(text, {
64
- type: nvl(type, defaultNotifyType),
65
- autoClose: nvl(options == null ? void 0 : options.autoClose, defaultNotifyAutoClose)
66
- });
67
- }
68
- if (vibrate && shouldVibrate(type) && navigator.vibrate) {
69
- navigator.vibrate(vibrateDuration);
70
- }
71
- },
72
- [
73
- onNotify,
74
- shouldVibrate,
75
- vibrateDuration,
76
- defaultNotifyType,
77
- defaultNotifyAutoClose
78
- ]
79
- );
80
- return { say, isSpeaking, textSpeaking, setIsSpeaking };
42
+ function showSayNotify(text, { type = "success", autoClose = 4500 }) {
43
+ reactToastify.toast(text, { type, autoClose });
81
44
  }
82
45
  function useSpeechPonyfill() {
83
46
  const ponyfill = react.useMemo(() => getSpeechSupport(), []);
@@ -111,27 +74,77 @@ function Say({
111
74
  volume = DEFAULT_SAY_VOLUME
112
75
  }) {
113
76
  const { voices, ponyfill } = useSpeechPonyfill();
77
+ const canSpeak = isSpeaking && voices.length > 0 && ponyfill;
114
78
  const handleEnd = react.useCallback(() => {
115
79
  setIsSpeaking(false);
116
80
  onEnd == null ? void 0 : onEnd();
117
81
  }, [setIsSpeaking, onEnd]);
118
- if (!isSpeaking || voices.length === 0 || !ponyfill) return null;
119
- return /* @__PURE__ */ jsxRuntime.jsx(
120
- SayEngine__default.default,
121
- {
122
- text,
123
- rate,
124
- pitch,
125
- volume,
126
- onEnd: handleEnd,
127
- onStart,
128
- ponyfill,
129
- voice: voices.find((v) => v.name === voice)
130
- }
131
- );
82
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
83
+ /* @__PURE__ */ jsxRuntime.jsx(
84
+ reactToastify.ToastContainer,
85
+ {
86
+ limit: 3,
87
+ draggable: true,
88
+ newestOnTop: true,
89
+ pauseOnHover: true,
90
+ closeOnClick: true,
91
+ autoClose: 4500,
92
+ pauseOnFocusLoss: true,
93
+ closeButton: false,
94
+ position: "top-right",
95
+ style: { fontSize: "14px" }
96
+ }
97
+ ),
98
+ canSpeak && /* @__PURE__ */ jsxRuntime.jsx(
99
+ SayEngine__default.default,
100
+ {
101
+ text,
102
+ rate,
103
+ pitch,
104
+ volume,
105
+ onEnd: handleEnd,
106
+ onStart,
107
+ ponyfill,
108
+ voice: voices.find((v) => v.name === voice)
109
+ }
110
+ )
111
+ ] });
132
112
  }
133
113
  var say_default = Say;
134
114
 
115
+ // src/functions/nvl/nvl.ts
116
+ function nvl(value, defaultValue) {
117
+ return value != null ? value : defaultValue;
118
+ }
119
+ function useSay({
120
+ notifyAutoClose = 4500,
121
+ vibrateDuration = DEFAULT_SAY_VIBRATE_DURATION,
122
+ shouldVibrate = defaultShouldVibrate
123
+ } = {}) {
124
+ const [isSpeaking, setIsSpeaking] = react.useState(false);
125
+ const [textSpeaking, setTextSpeaking] = react.useState("");
126
+ const handleSay = react.useCallback(
127
+ (text, options) => {
128
+ const notify = nvl(options.notify, true);
129
+ const vibrate = nvl(options.vibrate, true);
130
+ const { type } = options;
131
+ setIsSpeaking(true);
132
+ setTextSpeaking(text);
133
+ if (notify) {
134
+ showSayNotify(text, {
135
+ type,
136
+ autoClose: nvl(options.autoClose, notifyAutoClose)
137
+ });
138
+ }
139
+ if (vibrate && shouldVibrate(type) && navigator.vibrate) {
140
+ navigator.vibrate(vibrateDuration);
141
+ }
142
+ },
143
+ [shouldVibrate, vibrateDuration, notifyAutoClose]
144
+ );
145
+ return { handleSay, isSpeaking, textSpeaking, setIsSpeaking };
146
+ }
147
+
135
148
  exports.Say = say_default;
136
149
  exports.useSay = useSay;
137
150
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/accessibility/say/utils/constants.ts","../../src/functions/nvl/nvl.ts","../../src/components/accessibility/say/use-say.ts","../../src/components/accessibility/say/use-speech-ponyfill.ts","../../src/components/accessibility/say/index.tsx"],"names":["useState","useCallback","useMemo","useEffect","jsx","SayEngine"],"mappings":"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","file":"index.js","sourcesContent":["import type { SpeechPonyfill } from \"./interface\";\r\n\r\nexport const SPEECH_APIS = [\"speechSynthesis\", \"SpeechSynthesisUtterance\"];\r\n\r\nexport function hasSpeechSupport(): boolean {\r\n return SPEECH_APIS.every((api) => api in globalThis);\r\n}\r\n\r\nexport function voicesAreEqual(\r\n current: SpeechSynthesisVoice[],\r\n next: SpeechSynthesisVoice[],\r\n): boolean {\r\n if (current.length !== next.length) return false;\r\n\r\n return current.every(\r\n (v, i) => v.name === next[i]?.name && v.lang === next[i]?.lang,\r\n );\r\n}\r\n\r\nexport function getSpeechSupport(): SpeechPonyfill | null {\r\n if (!hasSpeechSupport()) return null;\r\n\r\n return {\r\n speechSynthesis: globalThis.speechSynthesis,\r\n SpeechSynthesisUtterance: globalThis.SpeechSynthesisUtterance,\r\n };\r\n}\r\n\r\nexport const DEFAULT_SAY_VOICE = \"Google português do Brasil\";\r\nexport const DEFAULT_SAY_RATE = 1.4;\r\nexport const DEFAULT_SAY_PITCH = 0.8;\r\nexport const DEFAULT_SAY_VOLUME = 1;\r\nexport const DEFAULT_SAY_NOTIFY_AUTO_CLOSE = 1500;\r\nexport const DEFAULT_SAY_VIBRATE_DURATION = 500;\r\n\r\nexport function defaultShouldVibrate(\r\n type?: import(\"./interface\").SayFeedbackType | false,\r\n): boolean {\r\n return !type || type === \"warning\";\r\n}\r\n\r\nconst sayConstants = `\r\nSintetiza texto em fala via Web Speech API (\\`react-say\\`), com hook \\`useSay\\` para notificação e vibração.\r\n\r\n**Importação:**\r\n\\`\\`\\`tsx\r\nimport { Say, useSay } from \"@grazziotin/react-components-next/accessibility\";\r\n\\`\\`\\`\r\n\r\n**Uso básico:**\r\n\\`\\`\\`tsx\r\nconst { say, isSpeaking, textSpeaking, setIsSpeaking } = useSay({\r\n onNotify: (text, { type }) => toast(text, { type }),\r\n});\r\n\r\n<>\r\n <Say\r\n isSpeaking={isSpeaking}\r\n text={textSpeaking}\r\n setIsSpeaking={setIsSpeaking}\r\n />\r\n <button type=\"button\" onClick={() => say(\"Operação concluída\")}>\r\n Falar\r\n </button>\r\n</>\r\n\\`\\`\\`\r\n\r\nRequer suporte a \\`speechSynthesis\\` no navegador. A voz padrão é \\`Google português do Brasil\\`.\r\n`;\r\n\r\nexport default sayConstants;\r\n","/**\r\n * Função NVL (Null Value Logic) - retorna valor padrão se o valor for null/undefined\r\n * @param {T | null | undefined} value - Valor a ser verificado\r\n * @param {T} defaultValue - Valor padrão a ser retornado se value for null/undefined\r\n * @returns {T} Value se não for null/undefined, senão defaultValue\r\n * @template T - Tipo do valor\r\n * @example\r\n * nvl(null, 'padrão') // Retorna: 'padrão'\r\n * nvl('valor', 'padrão') // Retorna: 'valor'\r\n */\r\nexport function nvl<T>(value: T | null | undefined, defaultValue: T): T {\r\n return value ?? defaultValue;\r\n}\r\n","import {\r\n defaultShouldVibrate,\r\n DEFAULT_SAY_NOTIFY_AUTO_CLOSE,\r\n DEFAULT_SAY_VIBRATE_DURATION,\r\n} from \"./utils/constants\";\r\nimport type {\r\n UseSayReturn,\r\n UseSayOptions,\r\n SayCallOptions,\r\n} from \"./utils/interface\";\r\nimport { nvl } from \"@/functions\";\r\nimport { useCallback, useState } from \"react\";\r\n\r\n/**\r\n * Hook para disparar fala, notificação e vibração de forma configurável.\r\n *\r\n * @example\r\n * ```tsx\r\n * const { say, isSpeaking, textSpeaking, setIsSpeaking } = useSay({\r\n * onNotify: (text, { type }) => toast(text, { type }),\r\n * });\r\n *\r\n * <Say\r\n * isSpeaking={isSpeaking}\r\n * text={textSpeaking}\r\n * setIsSpeaking={setIsSpeaking}\r\n * />\r\n *\r\n * <button type=\"button\" onClick={() => say(\"Operação concluída\")}>\r\n * Falar\r\n * </button>\r\n * ```\r\n */\r\nexport function useSay({\r\n onNotify,\r\n defaultNotifyType = \"success\",\r\n vibrateDuration = DEFAULT_SAY_VIBRATE_DURATION,\r\n defaultNotifyAutoClose = DEFAULT_SAY_NOTIFY_AUTO_CLOSE,\r\n shouldVibrate = defaultShouldVibrate,\r\n}: UseSayOptions = {}): UseSayReturn {\r\n const [isSpeaking, setIsSpeaking] = useState(false);\r\n const [textSpeaking, setTextSpeaking] = useState(\"\");\r\n\r\n const say = useCallback(\r\n (text: string, options?: SayCallOptions) => {\r\n const type = options?.type;\r\n const notify = nvl(options?.notify, true);\r\n const vibrate = nvl(options?.vibrate, true);\r\n\r\n setIsSpeaking(true);\r\n setTextSpeaking(text);\r\n\r\n if (notify && type !== false && onNotify) {\r\n onNotify(text, {\r\n type: nvl(type, defaultNotifyType),\r\n autoClose: nvl(options?.autoClose, defaultNotifyAutoClose),\r\n });\r\n }\r\n\r\n if (vibrate && shouldVibrate(type) && navigator.vibrate) {\r\n navigator.vibrate(vibrateDuration);\r\n }\r\n },\r\n [\r\n onNotify,\r\n shouldVibrate,\r\n vibrateDuration,\r\n defaultNotifyType,\r\n defaultNotifyAutoClose,\r\n ],\r\n );\r\n\r\n return { say, isSpeaking, textSpeaking, setIsSpeaking };\r\n}\r\n","import {\r\n getSpeechSupport,\r\n hasSpeechSupport,\r\n voicesAreEqual,\r\n} from \"./utils/constants\";\r\nimport { useEffect, useMemo, useState } from \"react\";\r\n\r\nexport function useSpeechPonyfill() {\r\n const ponyfill = useMemo(() => getSpeechSupport(), []);\r\n const [voices, setVoices] = useState<SpeechSynthesisVoice[]>([]);\r\n\r\n useEffect(() => {\r\n if (!hasSpeechSupport()) return;\r\n\r\n const synthesis = globalThis.speechSynthesis;\r\n\r\n const updateVoices = () => {\r\n setVoices((current) => {\r\n const next = synthesis.getVoices();\r\n return voicesAreEqual(current, next) ? current : next;\r\n });\r\n };\r\n\r\n synthesis.onvoiceschanged = updateVoices;\r\n updateVoices();\r\n\r\n return () => {\r\n synthesis.onvoiceschanged = null;\r\n };\r\n }, []);\r\n\r\n return { voices, ponyfill };\r\n}\r\n","\"use client\";\r\n\r\nimport {\r\n DEFAULT_SAY_PITCH,\r\n DEFAULT_SAY_RATE,\r\n DEFAULT_SAY_VOICE,\r\n DEFAULT_SAY_VOLUME,\r\n} from \"./utils/constants\";\r\nimport SayEngine from \"react-say\";\r\nexport { useSay } from \"./use-say\";\r\nimport React, { useCallback } from \"react\";\r\nimport type { SayProps } from \"./utils/interface\";\r\nimport { useSpeechPonyfill } from \"./use-speech-ponyfill\";\r\n\r\n/**\r\n * Sintetiza texto em fala via Web Speech API (`react-say`).\r\n * Use com {@link useSay} para controlar `isSpeaking`, `text` e `setIsSpeaking`.\r\n *\r\n * @example\r\n * ```tsx\r\n * const { say, isSpeaking, textSpeaking, setIsSpeaking } = useSay({\r\n * onNotify: (text, { type }) => toast(text, { type }),\r\n * });\r\n *\r\n * return (\r\n * <>\r\n * <Say\r\n * isSpeaking={isSpeaking}\r\n * text={textSpeaking}\r\n * setIsSpeaking={setIsSpeaking}\r\n * voice=\"Google português do Brasil\"\r\n * rate={1.4}\r\n * />\r\n * <button type=\"button\" onClick={() => say(\"Pronto\")}>Falar</button>\r\n * </>\r\n * );\r\n * ```\r\n */\r\nfunction Say({\r\n text,\r\n onEnd,\r\n onStart,\r\n isSpeaking,\r\n setIsSpeaking,\r\n rate = DEFAULT_SAY_RATE,\r\n voice = DEFAULT_SAY_VOICE,\r\n pitch = DEFAULT_SAY_PITCH,\r\n volume = DEFAULT_SAY_VOLUME,\r\n}: Readonly<SayProps>): React.ReactElement | null {\r\n const { voices, ponyfill } = useSpeechPonyfill();\r\n\r\n const handleEnd = useCallback(() => {\r\n setIsSpeaking(false);\r\n onEnd?.();\r\n }, [setIsSpeaking, onEnd]);\r\n\r\n if (!isSpeaking || voices.length === 0 || !ponyfill) return null;\r\n\r\n return (\r\n <SayEngine\r\n text={text}\r\n rate={rate}\r\n pitch={pitch}\r\n volume={volume}\r\n onEnd={handleEnd}\r\n onStart={onStart}\r\n ponyfill={ponyfill}\r\n voice={voices.find((v) => v.name === voice)}\r\n />\r\n );\r\n}\r\n\r\nexport default Say;\r\n"]}
1
+ {"version":3,"sources":["../../src/components/accessibility/say/utils/constants.ts","../../src/components/accessibility/say/use-speech-ponyfill.ts","../../src/components/accessibility/say/index.tsx","../../src/functions/nvl/nvl.ts","../../src/components/accessibility/say/use-say.ts"],"names":["toast","useMemo","useState","useEffect","useCallback","jsxs","Fragment","jsx","ToastContainer","SayEngine"],"mappings":"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","file":"index.js","sourcesContent":["import type {\r\n SayFeedbackType,\r\n SayNotifyOptions,\r\n SpeechPonyfill,\r\n} from \"./interface\";\r\nimport { toast } from \"react-toastify\";\r\n\r\nexport const DEFAULT_SAY_RATE = 1.4;\r\nexport const DEFAULT_SAY_VOLUME = 1;\r\nexport const DEFAULT_SAY_PITCH = 0.8;\r\nexport const DEFAULT_SAY_VIBRATE_DURATION = 500;\r\nexport const DEFAULT_SAY_VOICE = \"Google português do Brasil\";\r\nexport const SPEECH_APIS = [\"speechSynthesis\", \"SpeechSynthesisUtterance\"];\r\n\r\nexport function hasSpeechSupport(): boolean {\r\n return SPEECH_APIS.every((api) => api in globalThis);\r\n}\r\n\r\nexport function voicesAreEqual(\r\n current: SpeechSynthesisVoice[],\r\n next: SpeechSynthesisVoice[],\r\n): boolean {\r\n if (current.length !== next.length) return false;\r\n return current.every(\r\n (v, i) => v.name === next[i]?.name && v.lang === next[i]?.lang,\r\n );\r\n}\r\n\r\nexport function getSpeechSupport(): SpeechPonyfill | null {\r\n if (!hasSpeechSupport()) return null;\r\n return {\r\n speechSynthesis: globalThis.speechSynthesis,\r\n SpeechSynthesisUtterance: globalThis.SpeechSynthesisUtterance,\r\n };\r\n}\r\n\r\nexport function defaultShouldVibrate(type: SayFeedbackType): boolean {\r\n return type === \"warning\";\r\n}\r\n\r\n/** Exibe toast integrado do {@link Say}. */\r\nexport function showSayNotify(\r\n text: string,\r\n { type = \"success\", autoClose = 4500 }: SayNotifyOptions,\r\n): void {\r\n toast(text, { type, autoClose });\r\n}\r\n\r\nconst sayConstants = `\r\nSintetiza texto em fala via Web Speech API (\\`react-say\\`), com toast integrado (\\`react-toastify\\`) e vibração.\r\n\r\n**Importação:**\r\n\\`\\`\\`tsx\r\nimport { Say, useSay } from \"@grazziotin/react-components-next/accessibility\";\r\n\\`\\`\\`\r\n\r\n**Uso básico:**\r\n\\`\\`\\`tsx\r\nconst { handleSay, isSpeaking, textSpeaking, setIsSpeaking } = useSay();\r\n\r\n<>\r\n <Say\r\n isSpeaking={isSpeaking}\r\n text={textSpeaking}\r\n setIsSpeaking={setIsSpeaking}\r\n />\r\n <button type=\"button\" onClick={() => handleSay(\"Operação concluída\", { type: \"success\" })}>\r\n Falar\r\n </button>\r\n</>\r\nonSuccess: (r) => handleSay(r.status, { type: \"success\" })\r\n\\`\\`\\`\r\n\r\nMonte \\`<Say />\\` uma vez no layout. Toast e CSS já vêm embutidos.\r\nUse \\`notify: false\\` para falar sem toast: \\`handleSay(texto, { type: \"default\", notify: false })\\`.\r\n`;\r\nexport default sayConstants;\r\n","import {\r\n voicesAreEqual,\r\n getSpeechSupport,\r\n hasSpeechSupport,\r\n} from \"./utils/constants\";\r\n\r\nimport { useEffect, useMemo, useState } from \"react\";\r\n\r\nexport function useSpeechPonyfill() {\r\n const ponyfill = useMemo(() => getSpeechSupport(), []);\r\n const [voices, setVoices] = useState<SpeechSynthesisVoice[]>([]);\r\n\r\n useEffect(() => {\r\n if (!hasSpeechSupport()) return;\r\n const synthesis = globalThis.speechSynthesis;\r\n\r\n const updateVoices = () => {\r\n setVoices((current) => {\r\n const next = synthesis.getVoices();\r\n return voicesAreEqual(current, next) ? current : next;\r\n });\r\n };\r\n\r\n synthesis.onvoiceschanged = updateVoices;\r\n updateVoices();\r\n\r\n return () => {\r\n synthesis.onvoiceschanged = null;\r\n };\r\n }, []);\r\n\r\n return { voices, ponyfill };\r\n}\r\n","\"use client\";\r\nimport \"react-toastify/dist/ReactToastify.css\";\r\n\r\nimport {\r\n DEFAULT_SAY_PITCH,\r\n DEFAULT_SAY_RATE,\r\n DEFAULT_SAY_VOICE,\r\n DEFAULT_SAY_VOLUME,\r\n} from \"./utils/constants\";\r\nimport SayEngine from \"react-say\";\r\nimport React, { useCallback } from \"react\";\r\nimport { ToastContainer } from \"react-toastify\";\r\nimport type { SayProps } from \"./utils/interface\";\r\nimport { useSpeechPonyfill } from \"./use-speech-ponyfill\";\r\n\r\n/**\r\n * Sintetiza texto em fala via Web Speech API (`react-say`).\r\n * Inclui toast integrado para notificações do {@link useSay}.\r\n *\r\n * @example\r\n * ```tsx\r\n * const { handleSay, isSpeaking, textSpeaking, setIsSpeaking } = useSay();\r\n *\r\n * return (\r\n * <>\r\n * <Say\r\n * isSpeaking={isSpeaking}\r\n * text={textSpeaking}\r\n * setIsSpeaking={setIsSpeaking}\r\n * />\r\n * <button type=\"button\" onClick={() => handleSay(\"Pronto\", { type: \"success\" })}>\r\n * Falar\r\n * </button>\r\n * </>\r\n * );\r\n * ```\r\n */\r\nfunction Say({\r\n text,\r\n onEnd,\r\n onStart,\r\n isSpeaking,\r\n setIsSpeaking,\r\n rate = DEFAULT_SAY_RATE,\r\n voice = DEFAULT_SAY_VOICE,\r\n pitch = DEFAULT_SAY_PITCH,\r\n volume = DEFAULT_SAY_VOLUME,\r\n}: Readonly<SayProps>): React.ReactElement {\r\n const { voices, ponyfill } = useSpeechPonyfill();\r\n const canSpeak = isSpeaking && voices.length > 0 && ponyfill;\r\n\r\n const handleEnd = useCallback(() => {\r\n setIsSpeaking(false);\r\n onEnd?.();\r\n }, [setIsSpeaking, onEnd]);\r\n\r\n return (\r\n <>\r\n <ToastContainer\r\n limit={3}\r\n draggable\r\n newestOnTop\r\n pauseOnHover\r\n closeOnClick\r\n autoClose={4500}\r\n pauseOnFocusLoss\r\n closeButton={false}\r\n position=\"top-right\"\r\n style={{ fontSize: \"14px\" }}\r\n />\r\n {canSpeak && (\r\n <SayEngine\r\n text={text}\r\n rate={rate}\r\n pitch={pitch}\r\n volume={volume}\r\n onEnd={handleEnd}\r\n onStart={onStart}\r\n ponyfill={ponyfill}\r\n voice={voices.find((v) => v.name === voice)}\r\n />\r\n )}\r\n </>\r\n );\r\n}\r\nexport default Say;\r\n","/**\r\n * Função NVL (Null Value Logic) - retorna valor padrão se o valor for null/undefined\r\n * @param {T | null | undefined} value - Valor a ser verificado\r\n * @param {T} defaultValue - Valor padrão a ser retornado se value for null/undefined\r\n * @returns {T} Value se não for null/undefined, senão defaultValue\r\n * @template T - Tipo do valor\r\n * @example\r\n * nvl(null, 'padrão') // Retorna: 'padrão'\r\n * nvl('valor', 'padrão') // Retorna: 'valor'\r\n */\r\nexport function nvl<T>(value: T | null | undefined, defaultValue: T): T {\r\n return value ?? defaultValue;\r\n}\r\n","import {\r\n showSayNotify,\r\n defaultShouldVibrate,\r\n DEFAULT_SAY_VIBRATE_DURATION,\r\n} from \"./utils/constants\";\r\nimport type {\r\n SayCallOptions,\r\n UseSayReturn,\r\n UseSayOptions,\r\n} from \"./utils/interface\";\r\nimport { nvl } from \"@/functions\";\r\nimport { useCallback, useState } from \"react\";\r\n\r\n/**\r\n * Hook para disparar fala, toast integrado e vibração.\r\n *\r\n * @example\r\n * ```tsx\r\n * const { handleSay, isSpeaking, textSpeaking, setIsSpeaking } = useSay();\r\n *\r\n * <Say\r\n * isSpeaking={isSpeaking}\r\n * text={textSpeaking}\r\n * setIsSpeaking={setIsSpeaking}\r\n * />\r\n *\r\n * handleSay(\"Operação concluída\", { type: \"success\" });\r\n * handleSay(\"Atenção\", { type: \"warning\" });\r\n * handleSay(\"Apenas fala\", { type: \"default\", notify: false });\r\n * ```\r\n */\r\nexport function useSay({\r\n notifyAutoClose = 4500,\r\n vibrateDuration = DEFAULT_SAY_VIBRATE_DURATION,\r\n shouldVibrate = defaultShouldVibrate,\r\n}: UseSayOptions = {}): UseSayReturn {\r\n const [isSpeaking, setIsSpeaking] = useState(false);\r\n const [textSpeaking, setTextSpeaking] = useState(\"\");\r\n\r\n const handleSay = useCallback(\r\n (text: string, options: SayCallOptions) => {\r\n const notify = nvl(options.notify, true);\r\n const vibrate = nvl(options.vibrate, true);\r\n const { type } = options;\r\n\r\n setIsSpeaking(true);\r\n setTextSpeaking(text);\r\n\r\n if (notify) {\r\n showSayNotify(text, {\r\n type,\r\n autoClose: nvl(options.autoClose, notifyAutoClose),\r\n });\r\n }\r\n\r\n if (vibrate && shouldVibrate(type) && navigator.vibrate) {\r\n navigator.vibrate(vibrateDuration);\r\n }\r\n },\r\n [shouldVibrate, vibrateDuration, notifyAutoClose],\r\n );\r\n\r\n return { handleSay, isSpeaking, textSpeaking, setIsSpeaking };\r\n}\r\n"]}