@flowtomic/logic 0.1.7 → 0.1.8

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 (38) hide show
  1. package/dist/hooks/useDashboardLayout/index.d.ts +2 -2
  2. package/dist/hooks/useDashboardLayout/index.d.ts.map +1 -1
  3. package/dist/hooks/useDashboardLayout/useDashboardLayout.d.ts +21 -0
  4. package/dist/hooks/useDashboardLayout/useDashboardLayout.d.ts.map +1 -1
  5. package/dist/hooks/useResizable/index.d.ts +1 -1
  6. package/dist/hooks/useResizable/index.d.ts.map +1 -1
  7. package/dist/hooks/useResizable/useResizable.d.ts +5 -0
  8. package/dist/hooks/useResizable/useResizable.d.ts.map +1 -1
  9. package/dist/index.js +2 -2
  10. package/package.json +3 -3
  11. package/dist/hooks/useAnimatedIndicator/index.js +0 -1
  12. package/dist/hooks/useAnimatedIndicator/useAnimatedIndicator.js +0 -242
  13. package/dist/hooks/useAutocomplete/index.js +0 -1
  14. package/dist/hooks/useAutocomplete/useAutocomplete.js +0 -380
  15. package/dist/hooks/useDashboardLayout/index.js +0 -1
  16. package/dist/hooks/useDashboardLayout/useDashboardLayout.js +0 -175
  17. package/dist/hooks/useGenealogy/index.js +0 -1
  18. package/dist/hooks/useGenealogy/useGenealogy.js +0 -341
  19. package/dist/hooks/useMoblile/index.js +0 -1
  20. package/dist/hooks/useMoblile/useMobile.js +0 -15
  21. package/dist/hooks/useProjectProgress/index.js +0 -1
  22. package/dist/hooks/useProjectProgress/useProjectProgress.js +0 -124
  23. package/dist/hooks/useProjectStats/index.js +0 -1
  24. package/dist/hooks/useProjectStats/useProjectStats.js +0 -87
  25. package/dist/hooks/useReactTableBack/index.js +0 -1
  26. package/dist/hooks/useReactTableBack/useReactTableBack.js +0 -124
  27. package/dist/hooks/useReactTableFront/index.js +0 -1
  28. package/dist/hooks/useReactTableFront/useReactTableFront.js +0 -112
  29. package/dist/hooks/useResizable/index.js +0 -1
  30. package/dist/hooks/useResizable/useResizable.js +0 -129
  31. package/dist/hooks/useScriptEditor/index.js +0 -1
  32. package/dist/hooks/useScriptEditor/useScriptEditor.js +0 -247
  33. package/dist/hooks/useStatCard/index.js +0 -1
  34. package/dist/hooks/useStatCard/useStatCard.js +0 -187
  35. package/dist/hooks/useThemeTransition/index.js +0 -1
  36. package/dist/hooks/useThemeTransition/useThemeTransition.js +0 -35
  37. package/dist/hooks/useTimeTracker/index.js +0 -1
  38. package/dist/hooks/useTimeTracker/useTimeTracker.js +0 -143
@@ -1,247 +0,0 @@
1
- /**
2
- * useScriptEditor - Headless UI Hook
3
- *
4
- * Fornece apenas: lógica, estado, processamento e API
5
- * NÃO fornece: markup, styles ou implementações pré-construídas
6
- *
7
- * Padrão Headless UI: você controla o markup e styles
8
- *
9
- * @example
10
- * ```tsx
11
- * function MyScriptEditor() {
12
- * const {
13
- * script,
14
- * setScript,
15
- * terminalLines,
16
- * preview,
17
- * isRunning,
18
- * isConnected,
19
- * executeScript,
20
- * stopExecution,
21
- * clearTerminal,
22
- * } = useScriptEditor({
23
- * wsUrl: "ws://localhost:8080/ws/terminal",
24
- * executeScript: async (script) => {
25
- * const response = await fetch("/api/scripts/execute", {
26
- * method: "POST",
27
- * body: JSON.stringify({ script }),
28
- * });
29
- * return response.json();
30
- * },
31
- * });
32
- *
33
- * return (
34
- * <div>
35
- * <textarea value={script} onChange={(e) => setScript(e.target.value)} />
36
- * <button onClick={executeScript}>Executar</button>
37
- * <div>
38
- * {terminalLines.map((line) => (
39
- * <div key={line.id}>{line.content}</div>
40
- * ))}
41
- * </div>
42
- * </div>
43
- * );
44
- * }
45
- * ```
46
- */
47
- import { useCallback, useEffect, useRef, useState } from "react";
48
- /**
49
- * Hook headless para gerenciar editor de scripts com terminal interativo
50
- */
51
- export function useScriptEditor(options = {}) {
52
- const { wsUrl, executeScript: executeScriptFn, onOutput, onError, autoConnect = true, maxReconnectAttempts = 3, defaultScript = "", } = options;
53
- const [script, setScript] = useState(defaultScript);
54
- const [terminalLines, setTerminalLines] = useState([]);
55
- const [preview, setPreview] = useState("");
56
- const [activeTab, setActiveTab] = useState("terminal");
57
- const [isRunning, setIsRunning] = useState(false);
58
- const [isConnected, setIsConnected] = useState(false);
59
- const wsRef = useRef(null);
60
- const reconnectAttemptsRef = useRef(0);
61
- const shouldReconnectRef = useRef(true);
62
- // Adicionar linha ao terminal
63
- const addTerminalLine = useCallback((type, content) => {
64
- const line = {
65
- id: `${Date.now()}-${Math.random()}`,
66
- type,
67
- content,
68
- timestamp: new Date(),
69
- };
70
- setTerminalLines((prev) => [...prev, line]);
71
- onOutput?.(line);
72
- }, [onOutput]);
73
- // Conectar WebSocket
74
- const connectWebSocket = useCallback(() => {
75
- if (!wsUrl)
76
- return;
77
- // Verificar se já existe uma conexão ativa
78
- if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
79
- return;
80
- }
81
- // Fechar conexão anterior se existir
82
- if (wsRef.current) {
83
- try {
84
- wsRef.current.close();
85
- }
86
- catch {
87
- // Ignorar erros ao fechar
88
- }
89
- wsRef.current = null;
90
- }
91
- if (!shouldReconnectRef.current)
92
- return;
93
- try {
94
- const ws = new WebSocket(wsUrl);
95
- wsRef.current = ws;
96
- ws.onopen = () => {
97
- setIsConnected(true);
98
- reconnectAttemptsRef.current = 0;
99
- };
100
- ws.onmessage = (event) => {
101
- try {
102
- const data = JSON.parse(event.data);
103
- if (data.type === "output") {
104
- addTerminalLine("output", data.content);
105
- }
106
- else if (data.type === "error") {
107
- addTerminalLine("error", data.content);
108
- }
109
- else if (data.type === "result") {
110
- setPreview(JSON.stringify(data.content, null, 2));
111
- setActiveTab("preview");
112
- }
113
- else if (data.type === "system") {
114
- // Mensagens de sistema não são exibidas no terminal
115
- console.log("[useScriptEditor] Mensagem de sistema:", data.content);
116
- }
117
- }
118
- catch {
119
- addTerminalLine("output", event.data);
120
- }
121
- };
122
- ws.onerror = (error) => {
123
- console.error("[useScriptEditor] WebSocket error:", error);
124
- setIsConnected(false);
125
- onError?.(new Error("Erro na conexão WebSocket"));
126
- };
127
- ws.onclose = (event) => {
128
- setIsConnected(false);
129
- wsRef.current = null;
130
- // Tentar reconectar apenas se não excedeu tentativas e ainda deve reconectar
131
- if (event.code !== 1000 &&
132
- shouldReconnectRef.current &&
133
- reconnectAttemptsRef.current < maxReconnectAttempts) {
134
- reconnectAttemptsRef.current += 1;
135
- setTimeout(() => {
136
- if (shouldReconnectRef.current) {
137
- connectWebSocket();
138
- }
139
- }, 3000);
140
- }
141
- else if (reconnectAttemptsRef.current >= maxReconnectAttempts) {
142
- console.warn("[useScriptEditor] Máximo de tentativas de reconexão atingido");
143
- }
144
- };
145
- }
146
- catch (error) {
147
- console.error("[useScriptEditor] Erro ao conectar WebSocket:", error);
148
- setIsConnected(false);
149
- onError?.(error instanceof Error ? error : new Error("Erro desconhecido ao conectar"));
150
- }
151
- }, [wsUrl, maxReconnectAttempts, addTerminalLine, onError]);
152
- // Conectar WebSocket quando montar (se autoConnect)
153
- useEffect(() => {
154
- if (autoConnect && wsUrl) {
155
- const connectTimeout = setTimeout(() => {
156
- connectWebSocket();
157
- }, 100);
158
- return () => {
159
- clearTimeout(connectTimeout);
160
- };
161
- }
162
- }, [autoConnect, wsUrl, connectWebSocket]);
163
- // Limpar conexão ao desmontar
164
- useEffect(() => {
165
- return () => {
166
- shouldReconnectRef.current = false;
167
- if (wsRef.current) {
168
- try {
169
- wsRef.current.close(1000, "Component unmounting");
170
- }
171
- catch {
172
- // Ignorar erros ao fechar
173
- }
174
- wsRef.current = null;
175
- }
176
- };
177
- }, []);
178
- // Executar script
179
- const executeScript = useCallback(async () => {
180
- if (!script.trim()) {
181
- addTerminalLine("error", "❌ Script vazio");
182
- return;
183
- }
184
- setIsRunning(true);
185
- addTerminalLine("input", script);
186
- try {
187
- // Enviar script via WebSocket se conectado
188
- if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
189
- wsRef.current.send(JSON.stringify({ type: "execute", script }));
190
- }
191
- else if (executeScriptFn) {
192
- // Fallback para HTTP se WebSocket não estiver disponível
193
- try {
194
- const result = await executeScriptFn(script);
195
- if (result.output) {
196
- addTerminalLine("output", result.output);
197
- }
198
- if (result.error) {
199
- addTerminalLine("error", result.error);
200
- }
201
- if (result.result) {
202
- setPreview(JSON.stringify(result.result, null, 2));
203
- setActiveTab("preview");
204
- }
205
- }
206
- catch (error) {
207
- addTerminalLine("error", `❌ Erro na requisição: ${error instanceof Error ? error.message : "Erro desconhecido"}`);
208
- }
209
- }
210
- else {
211
- addTerminalLine("error", "❌ Nenhuma forma de execução disponível (WebSocket ou HTTP)");
212
- }
213
- }
214
- catch (error) {
215
- addTerminalLine("error", `❌ Erro ao executar: ${error instanceof Error ? error.message : "Erro desconhecido"}`);
216
- }
217
- finally {
218
- setIsRunning(false);
219
- }
220
- }, [script, executeScriptFn, addTerminalLine]);
221
- // Parar execução
222
- const stopExecution = useCallback(() => {
223
- if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
224
- wsRef.current.send(JSON.stringify({ type: "stop" }));
225
- }
226
- setIsRunning(false);
227
- }, []);
228
- // Limpar terminal
229
- const clearTerminal = useCallback(() => {
230
- setTerminalLines([]);
231
- setPreview("");
232
- }, []);
233
- return {
234
- script,
235
- setScript,
236
- terminalLines,
237
- preview,
238
- activeTab,
239
- setActiveTab,
240
- isRunning,
241
- isConnected,
242
- executeScript,
243
- stopExecution,
244
- clearTerminal,
245
- addTerminalLine,
246
- };
247
- }
@@ -1 +0,0 @@
1
- export { useStatCard } from "./useStatCard";
@@ -1,187 +0,0 @@
1
- /**
2
- * useStatCard - Headless UI Hook
3
- *
4
- * Fornece apenas: lógica, estado, processamento e API
5
- * NÃO fornece: markup, styles ou implementações pré-construídas
6
- *
7
- * Padrão Headless UI: você controla o markup e styles
8
- *
9
- * @example
10
- * ```tsx
11
- * // Cálculo automático do delta a partir de lastMonth
12
- * function MyCustomStatCard() {
13
- * const { formattedValue, trend, getCardProps } = useStatCard({
14
- * value: 122380,
15
- * lastMonth: 105922, // delta será calculado automaticamente: +15.5%
16
- * })
17
- *
18
- * return (
19
- * <div {...getCardProps()}>
20
- * <span>{formattedValue}</span>
21
- * <Badge>{trend.percentage}</Badge>
22
- * </div>
23
- * )
24
- * }
25
- *
26
- * // Ou forneça delta explicitamente para casos especiais
27
- * function CustomDeltaCard() {
28
- * const { formattedValue, trend } = useStatCard({
29
- * value: 122380,
30
- * delta: 15.1, // delta explícito tem prioridade
31
- * lastMonth: 105922,
32
- * })
33
- * }
34
- * ```
35
- */
36
- import { useCallback, useMemo } from "react";
37
- /**
38
- * Formata um número seguindo padrões comuns com suporte a locale
39
- * - Valores >= 1.000.000: formata como "X.XM" (respeitando locale para decimais)
40
- * - Valores >= 1.000: formata com separador de milhares conforme locale
41
- * - Valores < 1.000: retorna formatado conforme locale
42
- */
43
- function formatNumberDefault(n, locale = "pt-BR") {
44
- if (n >= 1_000_000) {
45
- const formatter = new Intl.NumberFormat(locale, {
46
- minimumFractionDigits: 1,
47
- maximumFractionDigits: 1,
48
- });
49
- return `${formatter.format(n / 1_000_000)}M`;
50
- }
51
- if (n >= 1_000) {
52
- return new Intl.NumberFormat(locale).format(n);
53
- }
54
- return new Intl.NumberFormat(locale).format(n);
55
- }
56
- /**
57
- * Formata um número como moeda usando locale
58
- */
59
- function formatCurrency(n, locale = "pt-BR", currency = "BRL", currencyDisplay = "symbol") {
60
- return new Intl.NumberFormat(locale, {
61
- style: "currency",
62
- currency,
63
- currencyDisplay,
64
- minimumFractionDigits: 0,
65
- maximumFractionDigits: 2,
66
- }).format(n);
67
- }
68
- /**
69
- * Hook Headless UI para StatCard
70
- *
71
- * Fornece apenas a lógica e API, sem markup ou styles.
72
- * Você é responsável por criar o visual.
73
- *
74
- * @example
75
- * ```tsx
76
- * // Cálculo automático do delta a partir de lastMonth
77
- * function MyCustomStatCard() {
78
- * const { formattedValue, trend, getCardProps } = useStatCard({
79
- * value: 122380,
80
- * lastMonth: 105922, // delta será calculado automaticamente: +15.5%
81
- * })
82
- *
83
- * return (
84
- * <div {...getCardProps()}>
85
- * <span>{formattedValue}</span>
86
- * <Badge variant={trend.variant}>{trend.percentage}</Badge>
87
- * </div>
88
- * )
89
- * }
90
- *
91
- * // Ou forneça delta explicitamente para casos especiais
92
- * function CustomDeltaCard() {
93
- * const { formattedValue, trend } = useStatCard({
94
- * value: 122380,
95
- * delta: 15.1, // delta explícito tem prioridade
96
- * lastMonth: 105922,
97
- * })
98
- * }
99
- * ```
100
- */
101
- export function useStatCard(data) {
102
- const { value, delta: explicitDelta, lastMonth, prefix = "", suffix = "", locale = "pt-BR", currency, currencyDisplay = "symbol", format, lastFormat, positive: explicitPositive, } = data;
103
- // Calcula delta automaticamente se não fornecido mas lastMonth estiver disponível
104
- const delta = useMemo(() => {
105
- // Se delta foi fornecido explicitamente, usa ele (prioridade)
106
- if (explicitDelta !== undefined) {
107
- return explicitDelta;
108
- }
109
- // Se lastMonth foi fornecido, calcula o delta automaticamente
110
- if (lastMonth !== undefined && lastMonth !== 0) {
111
- return ((Number(value) - Number(lastMonth)) / Number(lastMonth)) * 100;
112
- }
113
- // Caso contrário, retorna 0
114
- return 0;
115
- }, [value, lastMonth, explicitDelta]);
116
- // Calcula se a tendência é positiva
117
- const isPositive = useMemo(() => {
118
- if (explicitPositive !== undefined) {
119
- return explicitPositive;
120
- }
121
- return delta >= 0;
122
- }, [delta, explicitPositive]);
123
- // Calcula informações da tendência
124
- const trend = useMemo(() => {
125
- const direction = delta > 0 ? "up" : delta < 0 ? "down" : "neutral";
126
- const variant = direction === "up" ? "success" : direction === "down" ? "destructive" : "secondary";
127
- const percentage = delta > 0 ? `+${delta.toFixed(1)}%` : delta < 0 ? `${delta.toFixed(1)}%` : "0%";
128
- return {
129
- isPositive,
130
- percentage,
131
- delta: Math.abs(delta),
132
- variant,
133
- direction,
134
- };
135
- }, [delta, isPositive]);
136
- // Formata o valor principal
137
- const formattedValue = useMemo(() => {
138
- if (format) {
139
- return format(Number(value));
140
- }
141
- const numValue = Number(value);
142
- if (currency) {
143
- // Se currency está definido, usa formatação de moeda (ignora prefix)
144
- return (formatCurrency(numValue, locale, currency, currencyDisplay) + (suffix ? ` ${suffix}` : ""));
145
- }
146
- // Caso contrário, usa formatação numérica padrão com prefix/suffix
147
- return prefix + formatNumberDefault(numValue, locale) + suffix;
148
- }, [value, prefix, suffix, locale, currency, currencyDisplay, format]);
149
- // Formata o valor do mês anterior
150
- const formattedLastMonth = useMemo(() => {
151
- if (!lastMonth)
152
- return null;
153
- if (lastFormat) {
154
- return lastFormat(lastMonth);
155
- }
156
- if (currency) {
157
- // Se currency está definido, usa formatação de moeda (ignora prefix)
158
- return formatCurrency(lastMonth, locale, currency, currencyDisplay);
159
- }
160
- // Caso contrário, usa formatação numérica padrão com prefix/suffix
161
- return prefix + formatNumberDefault(lastMonth, locale) + suffix;
162
- }, [lastMonth, prefix, suffix, locale, currency, currencyDisplay, lastFormat]);
163
- // Props helper para o card
164
- const getCardProps = useCallback((props = {}) => {
165
- return {
166
- onClick: props.onClick,
167
- className: props.className,
168
- "aria-label": props["aria-label"] || `Estatística: ${formattedValue}`,
169
- role: props.onClick ? "button" : undefined,
170
- };
171
- }, [formattedValue]);
172
- // Função utilitária de formatação
173
- const formatNumber = useCallback((n) => {
174
- if (currency) {
175
- return formatCurrency(n, locale, currency, currencyDisplay);
176
- }
177
- return formatNumberDefault(n, locale);
178
- }, [locale, currency, currencyDisplay]);
179
- return {
180
- formattedValue,
181
- formattedLastMonth,
182
- trend,
183
- isPositive,
184
- getCardProps,
185
- formatNumber,
186
- };
187
- }
@@ -1 +0,0 @@
1
- export { useThemeTransition } from "./useThemeTransition";
@@ -1,35 +0,0 @@
1
- /**
2
- * useThemeTransition - Hook para View Transitions API
3
- *
4
- * Gerencia transições suaves de tema usando a View Transitions API
5
- * com fallback automático para navegadores que não suportam
6
- */
7
- import { useCallback } from "react";
8
- /**
9
- * Hook para gerenciar transições de tema com View Transitions API
10
- *
11
- * @example
12
- * ```tsx
13
- * const { startTransition } = useThemeTransition()
14
- *
15
- * const handleToggle = () => {
16
- * startTransition(() => {
17
- * toggleTheme()
18
- * })
19
- * }
20
- * ```
21
- */
22
- export function useThemeTransition() {
23
- const startTransition = useCallback((updateFn) => {
24
- // Verifica se View Transitions API está disponível
25
- if (typeof document !== "undefined" && "startViewTransition" in document) {
26
- const doc = document;
27
- doc.startViewTransition?.(updateFn);
28
- }
29
- else {
30
- // Fallback: executa diretamente sem animação
31
- updateFn();
32
- }
33
- }, []);
34
- return { startTransition };
35
- }
@@ -1 +0,0 @@
1
- export { useTimeTracker } from "./useTimeTracker";
@@ -1,143 +0,0 @@
1
- /**
2
- * useTimeTracker - Headless UI Hook
3
- *
4
- * Fornece apenas: lógica, estado, processamento e API
5
- * NÃO fornece: markup, styles ou implementações pré-construídas
6
- *
7
- * Padrão Headless UI: você controla o markup e styles
8
- *
9
- * @example
10
- * ```tsx
11
- * function MyTimer() {
12
- * const { time, formattedTime, isRunning, start, pause, stop, reset } = useTimeTracker({
13
- * initialTime: 0,
14
- * })
15
- *
16
- * return (
17
- * <div>
18
- * <span>{formattedTime}</span>
19
- * <button onClick={start}>Start</button>
20
- * <button onClick={pause}>Pause</button>
21
- * <button onClick={stop}>Stop</button>
22
- * <button onClick={reset}>Reset</button>
23
- * </div>
24
- * )
25
- * }
26
- * ```
27
- */
28
- import { useCallback, useEffect, useRef, useState } from "react";
29
- /**
30
- * Hook headless para gerenciar timer
31
- */
32
- export function useTimeTracker(options = {}) {
33
- const { initialTime = 0, onPause, onStop, onStart, onResume, onReset, format = "HH:mm:ss", } = options;
34
- const [time, setTime] = useState(initialTime);
35
- const [isRunning, setIsRunning] = useState(false);
36
- const [isPaused, setIsPaused] = useState(false);
37
- const intervalRef = useRef(null);
38
- const startTimeRef = useRef(null);
39
- const pausedTimeRef = useRef(0);
40
- // Limpar intervalo ao desmontar
41
- useEffect(() => {
42
- return () => {
43
- if (intervalRef.current) {
44
- clearInterval(intervalRef.current);
45
- intervalRef.current = null;
46
- }
47
- };
48
- }, []);
49
- // Formatar tempo
50
- const formatTime = useCallback((seconds) => {
51
- if (typeof format === "function") {
52
- return format(seconds);
53
- }
54
- const hours = Math.floor(seconds / 3600);
55
- const minutes = Math.floor((seconds % 3600) / 60);
56
- const secs = seconds % 60;
57
- switch (format) {
58
- case "HH:mm:ss":
59
- return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
60
- case "mm:ss":
61
- return `${String(minutes).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
62
- case "ss":
63
- return String(seconds);
64
- default:
65
- return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}:${String(secs).padStart(2, "0")}`;
66
- }
67
- }, [format]);
68
- const formattedTime = formatTime(time);
69
- // Limpar intervalo
70
- const clearTimerInterval = useCallback(() => {
71
- if (intervalRef.current) {
72
- clearInterval(intervalRef.current);
73
- intervalRef.current = null;
74
- }
75
- }, []);
76
- // Iniciar timer
77
- const start = useCallback(() => {
78
- if (isRunning)
79
- return;
80
- clearTimerInterval();
81
- // Se estava pausado, continuar de onde parou
82
- const startFrom = isPaused ? time : initialTime;
83
- setTime(startFrom);
84
- setIsRunning(true);
85
- setIsPaused(false);
86
- pausedTimeRef.current = startFrom;
87
- startTimeRef.current = Date.now() - startFrom * 1000;
88
- intervalRef.current = setInterval(() => {
89
- const elapsed = Math.floor((Date.now() - (startTimeRef.current || 0)) / 1000);
90
- setTime(elapsed);
91
- }, 100);
92
- if (!isPaused) {
93
- onStart?.(startFrom);
94
- }
95
- else {
96
- onResume?.(startFrom);
97
- }
98
- }, [isRunning, isPaused, time, initialTime, onStart, onResume, clearTimerInterval]);
99
- // Pausar timer
100
- const pause = useCallback(() => {
101
- if (!isRunning)
102
- return;
103
- clearTimerInterval();
104
- setIsRunning(false);
105
- setIsPaused(true);
106
- onPause?.(time);
107
- }, [isRunning, time, onPause, clearTimerInterval]);
108
- // Parar timer
109
- const stop = useCallback(() => {
110
- clearTimerInterval();
111
- setIsRunning(false);
112
- setIsPaused(false);
113
- onStop?.(time);
114
- }, [time, onStop, clearTimerInterval]);
115
- // Retomar timer
116
- const resume = useCallback(() => {
117
- if (!isPaused)
118
- return;
119
- start();
120
- }, [isPaused, start]);
121
- // Resetar timer
122
- const reset = useCallback(() => {
123
- clearTimerInterval();
124
- setTime(initialTime);
125
- setIsRunning(false);
126
- setIsPaused(false);
127
- pausedTimeRef.current = 0;
128
- startTimeRef.current = null;
129
- onReset?.();
130
- }, [initialTime, onReset, clearTimerInterval]);
131
- return {
132
- time,
133
- formattedTime,
134
- isRunning,
135
- isPaused,
136
- isStopped: !isRunning && !isPaused,
137
- start,
138
- pause,
139
- stop,
140
- resume,
141
- reset,
142
- };
143
- }