@controleonline/ui-default 1.0.263 → 1.0.264
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/AGENTS.md +48 -47
- package/package.json +3 -6
- package/src/react/components/errors/DefaultErrors.js +360 -360
- package/src/react/components/files/DefaultFile.js +46 -46
- package/src/react/components/filters/CompactFilterSelector.js +22 -3
- package/src/react/components/filters/DefaultColumnFilter.js +15 -13
- package/src/react/components/filters/DefaultExternalFilters.js +44 -39
- package/src/react/components/filters/DefaultSearch.js +140 -103
- package/src/react/components/filters/DefaultSearch.styles.js +65 -70
- package/src/react/components/form/DefaultForm.js +200 -198
- package/src/react/components/help/DefaultTooltip.js +87 -87
- package/src/react/components/help/DefaultTooltip.styles.js +61 -61
- package/src/react/components/inputs/DefaultInput.js +162 -160
- package/src/react/components/inputs/DefaultSelect.js +203 -192
- package/src/react/components/map/DefaultGoogleMap.styles.js +115 -9
- package/src/react/components/map/DefaultGoogleMap.web.js +560 -446
- package/src/react/components/map/DefaultMap.native.js +73 -71
- package/src/react/components/map/DefaultMap.styles.js +16 -16
- package/src/react/components/map/DefaultMap.web.js +68 -66
- package/src/react/components/map/DefaultNativeMap.native.js +307 -183
- package/src/react/components/map/DefaultNativeMap.shared.js +439 -340
- package/src/react/components/map/DefaultNativeMap.styles.js +156 -122
- package/src/react/components/table/DefaultTable.js +529 -355
- package/src/react/components/table/DefaultTable.styles.js +30 -39
- package/src/react/index.js +10 -10
- package/src/tests/react/components/errors/DefaultErrors.test.js +162 -162
- package/src/tests/react/components/files/DefaultFile.test.js +80 -80
- package/src/tests/react/components/inputs/DefaultSelect.test.js +93 -0
- package/src/tests/react/components/table/DefaultTable.test.js +334 -0
|
@@ -1,360 +1,360 @@
|
|
|
1
|
-
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
|
2
|
-
import {Modal, Pressable, Text, TouchableOpacity, View} from 'react-native';
|
|
3
|
-
import {useStore, useStores} from '@store';
|
|
4
|
-
import resolveSystemErrorMessage from '@controleonline/ui-common/src/react/utils/systemErrorMessage';
|
|
5
|
-
|
|
6
|
-
const AUTO_CLOSE_MS = 5000;
|
|
7
|
-
|
|
8
|
-
const resolveText = value => String(value ?? '').trim();
|
|
9
|
-
|
|
10
|
-
const normalizeStoreNames = (store, stores) => {
|
|
11
|
-
const resolvedStoreNames = [
|
|
12
|
-
...(Array.isArray(store) ? store : store ? [store] : []),
|
|
13
|
-
...(Array.isArray(stores) ? stores : []),
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
return resolvedStoreNames.filter(name => typeof name === 'string' && name.trim()).map(name => name.trim());
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const pickThemeColor = (colors, keys, fallback) => {
|
|
20
|
-
for (const key of keys) {
|
|
21
|
-
const value = colors?.[key];
|
|
22
|
-
|
|
23
|
-
if (typeof value === 'string' && value.trim()) {
|
|
24
|
-
return value.trim();
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return fallback;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const DefaultErrors = ({
|
|
32
|
-
store = null,
|
|
33
|
-
stores = [],
|
|
34
|
-
error = null,
|
|
35
|
-
title = '',
|
|
36
|
-
message = '',
|
|
37
|
-
compact = false,
|
|
38
|
-
align = 'center',
|
|
39
|
-
onRetry = null,
|
|
40
|
-
retryLabel = 'Tentar novamente',
|
|
41
|
-
containerStyle = null,
|
|
42
|
-
contentStyle = null,
|
|
43
|
-
titleStyle = null,
|
|
44
|
-
messageStyle = null,
|
|
45
|
-
retryButtonStyle = null,
|
|
46
|
-
retryTextStyle = null,
|
|
47
|
-
}) => {
|
|
48
|
-
const themeStore = useStore('theme');
|
|
49
|
-
const themeColors = themeStore?.getters?.colors || {};
|
|
50
|
-
const allStores = typeof useStores === 'function' ? useStores(state => state) : null;
|
|
51
|
-
const storeNames = useMemo(() => normalizeStoreNames(store, stores), [store, stores]);
|
|
52
|
-
const watchedStoreEntries = useMemo(() => {
|
|
53
|
-
if (!allStores || storeNames.length === 0) {
|
|
54
|
-
return [];
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return storeNames
|
|
58
|
-
.map(storeName => {
|
|
59
|
-
const currentStore = allStores?.[storeName] || null;
|
|
60
|
-
const storeError = resolveSystemErrorMessage(currentStore?.getters?.error);
|
|
61
|
-
|
|
62
|
-
if (!storeError) {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
error: storeError,
|
|
68
|
-
name: storeName,
|
|
69
|
-
store: currentStore,
|
|
70
|
-
};
|
|
71
|
-
})
|
|
72
|
-
.filter(Boolean);
|
|
73
|
-
}, [allStores, storeNames]);
|
|
74
|
-
|
|
75
|
-
const explicitTitle = resolveText(title);
|
|
76
|
-
const explicitMessage = resolveText(message) || resolveSystemErrorMessage(error);
|
|
77
|
-
const hasStoreError = watchedStoreEntries.length > 0;
|
|
78
|
-
const activeStoreEntry = watchedStoreEntries[0] || null;
|
|
79
|
-
const visibleMessage = hasStoreError ? activeStoreEntry?.error || '' : explicitMessage;
|
|
80
|
-
const hasExplicitContent = storeNames.length === 0
|
|
81
|
-
? Boolean(explicitTitle || explicitMessage || typeof onRetry === 'function')
|
|
82
|
-
: Boolean(explicitMessage || typeof onRetry === 'function');
|
|
83
|
-
const storeDriverSignature = hasStoreError
|
|
84
|
-
? `store:${watchedStoreEntries.map(entry => `${entry.name}:${entry.error}`).join('|')}`
|
|
85
|
-
: '';
|
|
86
|
-
const explicitDriverSignature = hasExplicitContent
|
|
87
|
-
? `explicit:${[explicitTitle, explicitMessage, typeof onRetry === 'function' ? 'retry' : '']
|
|
88
|
-
.map(value => resolveText(value))
|
|
89
|
-
.filter(Boolean)
|
|
90
|
-
.join('|')}`
|
|
91
|
-
: '';
|
|
92
|
-
const activeDriverSignature = storeDriverSignature || explicitDriverSignature;
|
|
93
|
-
const [dismissedSignatures, setDismissedSignatures] = useState(() => new Set());
|
|
94
|
-
const timerRef = useRef(null);
|
|
95
|
-
const textAlign = align === 'left' ? 'left' : 'center';
|
|
96
|
-
const accentColor = pickThemeColor(
|
|
97
|
-
themeColors,
|
|
98
|
-
['inputErrorBorder', 'textDanger', 'modalBorder', 'primary'],
|
|
99
|
-
'#DC2626',
|
|
100
|
-
);
|
|
101
|
-
const backgroundColor = pickThemeColor(
|
|
102
|
-
themeColors,
|
|
103
|
-
['inputErrorBackground', 'modalBackground', 'surface', 'background'],
|
|
104
|
-
'#FFFFFF',
|
|
105
|
-
);
|
|
106
|
-
const overlayColor = pickThemeColor(
|
|
107
|
-
themeColors,
|
|
108
|
-
['modalOverlay', 'overlayBackground'],
|
|
109
|
-
'rgba(15, 23, 42, 0.55)',
|
|
110
|
-
);
|
|
111
|
-
const titleColor = pickThemeColor(
|
|
112
|
-
themeColors,
|
|
113
|
-
['inputErrorText', 'textDanger', 'modalHeaderText', 'text'],
|
|
114
|
-
'#B91C1C',
|
|
115
|
-
);
|
|
116
|
-
const messageColor = pickThemeColor(
|
|
117
|
-
themeColors,
|
|
118
|
-
['modalText', 'textSecondary', 'text'],
|
|
119
|
-
'#334155',
|
|
120
|
-
);
|
|
121
|
-
const closeColor = pickThemeColor(
|
|
122
|
-
themeColors,
|
|
123
|
-
['modalCloseIcon', 'textDanger', 'primary'],
|
|
124
|
-
accentColor,
|
|
125
|
-
);
|
|
126
|
-
const retryAccentColor = pickThemeColor(themeColors, ['primary'], accentColor);
|
|
127
|
-
|
|
128
|
-
const clearStoreErrors = useCallback(() => {
|
|
129
|
-
watchedStoreEntries.forEach(entry => {
|
|
130
|
-
const setError = entry?.store?.actions?.setError;
|
|
131
|
-
|
|
132
|
-
if (typeof setError === 'function') {
|
|
133
|
-
setError('');
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
}, [watchedStoreEntries]);
|
|
137
|
-
|
|
138
|
-
const dismissPopup = useCallback(() => {
|
|
139
|
-
if (timerRef.current) {
|
|
140
|
-
clearTimeout(timerRef.current);
|
|
141
|
-
timerRef.current = null;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
setDismissedSignatures(previous => {
|
|
145
|
-
const next = new Set(previous);
|
|
146
|
-
|
|
147
|
-
if (storeDriverSignature) {
|
|
148
|
-
next.add(storeDriverSignature);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (explicitDriverSignature) {
|
|
152
|
-
next.add(explicitDriverSignature);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return next;
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
clearStoreErrors();
|
|
159
|
-
}, [clearStoreErrors, explicitDriverSignature, storeDriverSignature]);
|
|
160
|
-
|
|
161
|
-
const isVisible = Boolean(activeDriverSignature) && !dismissedSignatures.has(activeDriverSignature);
|
|
162
|
-
const hasContent = Boolean(explicitTitle || visibleMessage || typeof onRetry === 'function');
|
|
163
|
-
|
|
164
|
-
useEffect(() => {
|
|
165
|
-
if (!activeDriverSignature) {
|
|
166
|
-
if (timerRef.current) {
|
|
167
|
-
clearTimeout(timerRef.current);
|
|
168
|
-
timerRef.current = null;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
setDismissedSignatures(previous => (previous.size > 0 ? new Set() : previous));
|
|
172
|
-
return undefined;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (!isVisible) {
|
|
176
|
-
if (timerRef.current) {
|
|
177
|
-
clearTimeout(timerRef.current);
|
|
178
|
-
timerRef.current = null;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return undefined;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (timerRef.current) {
|
|
185
|
-
clearTimeout(timerRef.current);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
timerRef.current = setTimeout(() => {
|
|
189
|
-
dismissPopup();
|
|
190
|
-
}, AUTO_CLOSE_MS);
|
|
191
|
-
|
|
192
|
-
return () => {
|
|
193
|
-
if (timerRef.current) {
|
|
194
|
-
clearTimeout(timerRef.current);
|
|
195
|
-
timerRef.current = null;
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
}, [activeDriverSignature, dismissPopup, isVisible]);
|
|
199
|
-
|
|
200
|
-
if (!isVisible || !hasContent) {
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return (
|
|
205
|
-
<Modal
|
|
206
|
-
animationType="fade"
|
|
207
|
-
onRequestClose={dismissPopup}
|
|
208
|
-
transparent
|
|
209
|
-
visible={isVisible}
|
|
210
|
-
>
|
|
211
|
-
<Pressable
|
|
212
|
-
accessibilityRole="button"
|
|
213
|
-
onPress={dismissPopup}
|
|
214
|
-
style={[
|
|
215
|
-
{
|
|
216
|
-
alignItems: 'center',
|
|
217
|
-
backgroundColor: overlayColor,
|
|
218
|
-
flex: 1,
|
|
219
|
-
justifyContent: 'center',
|
|
220
|
-
padding: 18,
|
|
221
|
-
},
|
|
222
|
-
]}
|
|
223
|
-
>
|
|
224
|
-
<View
|
|
225
|
-
onStartShouldSetResponder={() => true}
|
|
226
|
-
style={[
|
|
227
|
-
{
|
|
228
|
-
backgroundColor,
|
|
229
|
-
borderColor: accentColor,
|
|
230
|
-
borderRadius: 16,
|
|
231
|
-
borderWidth: 1,
|
|
232
|
-
maxWidth: compact ? 360 : 440,
|
|
233
|
-
padding: compact ? 14 : 16,
|
|
234
|
-
shadowColor: accentColor,
|
|
235
|
-
shadowOffset: {height: 10, width: 0},
|
|
236
|
-
shadowOpacity: 0.12,
|
|
237
|
-
shadowRadius: 18,
|
|
238
|
-
width: '100%',
|
|
239
|
-
elevation: 12,
|
|
240
|
-
},
|
|
241
|
-
containerStyle,
|
|
242
|
-
]}
|
|
243
|
-
>
|
|
244
|
-
<View
|
|
245
|
-
style={[
|
|
246
|
-
{
|
|
247
|
-
alignItems: 'flex-start',
|
|
248
|
-
flexDirection: 'row',
|
|
249
|
-
gap: 10,
|
|
250
|
-
justifyContent: 'space-between',
|
|
251
|
-
},
|
|
252
|
-
contentStyle,
|
|
253
|
-
]}
|
|
254
|
-
>
|
|
255
|
-
<View style={{flex: 1, gap: explicitTitle ? 10 : 8}}>
|
|
256
|
-
{explicitTitle ? (
|
|
257
|
-
<Text
|
|
258
|
-
style={[
|
|
259
|
-
{
|
|
260
|
-
color: titleColor,
|
|
261
|
-
fontSize: compact ? 14 : 15,
|
|
262
|
-
fontWeight: '800',
|
|
263
|
-
textAlign,
|
|
264
|
-
},
|
|
265
|
-
titleStyle,
|
|
266
|
-
]}
|
|
267
|
-
>
|
|
268
|
-
{explicitTitle}
|
|
269
|
-
</Text>
|
|
270
|
-
) : null}
|
|
271
|
-
|
|
272
|
-
{visibleMessage ? (
|
|
273
|
-
<Text
|
|
274
|
-
style={[
|
|
275
|
-
{
|
|
276
|
-
color: messageColor,
|
|
277
|
-
fontSize: compact ? 12 : 13,
|
|
278
|
-
lineHeight: compact ? 18 : 19,
|
|
279
|
-
textAlign,
|
|
280
|
-
},
|
|
281
|
-
messageStyle,
|
|
282
|
-
]}
|
|
283
|
-
>
|
|
284
|
-
{visibleMessage}
|
|
285
|
-
</Text>
|
|
286
|
-
) : null}
|
|
287
|
-
|
|
288
|
-
{typeof onRetry === 'function' ? (
|
|
289
|
-
<TouchableOpacity
|
|
290
|
-
accessibilityRole="button"
|
|
291
|
-
activeOpacity={0.85}
|
|
292
|
-
onPress={onRetry}
|
|
293
|
-
style={[
|
|
294
|
-
{
|
|
295
|
-
alignItems: 'center',
|
|
296
|
-
alignSelf: textAlign === 'left' ? 'flex-start' : 'center',
|
|
297
|
-
borderRadius: 999,
|
|
298
|
-
borderWidth: 1,
|
|
299
|
-
borderColor: retryAccentColor,
|
|
300
|
-
backgroundColor: `${retryAccentColor}14`,
|
|
301
|
-
marginTop: 4,
|
|
302
|
-
paddingHorizontal: 12,
|
|
303
|
-
paddingVertical: 8,
|
|
304
|
-
},
|
|
305
|
-
retryButtonStyle,
|
|
306
|
-
]}
|
|
307
|
-
>
|
|
308
|
-
<Text
|
|
309
|
-
style={[
|
|
310
|
-
{
|
|
311
|
-
color: retryAccentColor,
|
|
312
|
-
fontSize: 12,
|
|
313
|
-
fontWeight: '800',
|
|
314
|
-
},
|
|
315
|
-
retryTextStyle,
|
|
316
|
-
]}
|
|
317
|
-
>
|
|
318
|
-
{retryLabel}
|
|
319
|
-
</Text>
|
|
320
|
-
</TouchableOpacity>
|
|
321
|
-
) : null}
|
|
322
|
-
</View>
|
|
323
|
-
|
|
324
|
-
<TouchableOpacity
|
|
325
|
-
accessibilityLabel="Fechar erro"
|
|
326
|
-
accessibilityRole="button"
|
|
327
|
-
activeOpacity={0.8}
|
|
328
|
-
onPress={dismissPopup}
|
|
329
|
-
testID="default-errors-close"
|
|
330
|
-
style={[
|
|
331
|
-
{
|
|
332
|
-
alignItems: 'center',
|
|
333
|
-
borderColor: accentColor,
|
|
334
|
-
borderRadius: 999,
|
|
335
|
-
borderWidth: 1,
|
|
336
|
-
height: 28,
|
|
337
|
-
justifyContent: 'center',
|
|
338
|
-
width: 28,
|
|
339
|
-
},
|
|
340
|
-
]}
|
|
341
|
-
>
|
|
342
|
-
<Text
|
|
343
|
-
style={{
|
|
344
|
-
color: closeColor,
|
|
345
|
-
fontSize: 16,
|
|
346
|
-
fontWeight: '800',
|
|
347
|
-
lineHeight: 16,
|
|
348
|
-
}}
|
|
349
|
-
>
|
|
350
|
-
×
|
|
351
|
-
</Text>
|
|
352
|
-
</TouchableOpacity>
|
|
353
|
-
</View>
|
|
354
|
-
</View>
|
|
355
|
-
</Pressable>
|
|
356
|
-
</Modal>
|
|
357
|
-
);
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
export default DefaultErrors;
|
|
1
|
+
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
|
2
|
+
import {Modal, Pressable, Text, TouchableOpacity, View} from 'react-native';
|
|
3
|
+
import {useStore, useStores} from '@store';
|
|
4
|
+
import resolveSystemErrorMessage from '@controleonline/ui-common/src/react/utils/systemErrorMessage';
|
|
5
|
+
|
|
6
|
+
const AUTO_CLOSE_MS = 5000;
|
|
7
|
+
|
|
8
|
+
const resolveText = value => String(value ?? '').trim();
|
|
9
|
+
|
|
10
|
+
const normalizeStoreNames = (store, stores) => {
|
|
11
|
+
const resolvedStoreNames = [
|
|
12
|
+
...(Array.isArray(store) ? store : store ? [store] : []),
|
|
13
|
+
...(Array.isArray(stores) ? stores : []),
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
return resolvedStoreNames.filter(name => typeof name === 'string' && name.trim()).map(name => name.trim());
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const pickThemeColor = (colors, keys, fallback) => {
|
|
20
|
+
for (const key of keys) {
|
|
21
|
+
const value = colors?.[key];
|
|
22
|
+
|
|
23
|
+
if (typeof value === 'string' && value.trim()) {
|
|
24
|
+
return value.trim();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return fallback;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const DefaultErrors = ({
|
|
32
|
+
store = null,
|
|
33
|
+
stores = [],
|
|
34
|
+
error = null,
|
|
35
|
+
title = '',
|
|
36
|
+
message = '',
|
|
37
|
+
compact = false,
|
|
38
|
+
align = 'center',
|
|
39
|
+
onRetry = null,
|
|
40
|
+
retryLabel = 'Tentar novamente',
|
|
41
|
+
containerStyle = null,
|
|
42
|
+
contentStyle = null,
|
|
43
|
+
titleStyle = null,
|
|
44
|
+
messageStyle = null,
|
|
45
|
+
retryButtonStyle = null,
|
|
46
|
+
retryTextStyle = null,
|
|
47
|
+
}) => {
|
|
48
|
+
const themeStore = useStore('theme');
|
|
49
|
+
const themeColors = themeStore?.getters?.colors || {};
|
|
50
|
+
const allStores = typeof useStores === 'function' ? useStores(state => state) : null;
|
|
51
|
+
const storeNames = useMemo(() => normalizeStoreNames(store, stores), [store, stores]);
|
|
52
|
+
const watchedStoreEntries = useMemo(() => {
|
|
53
|
+
if (!allStores || storeNames.length === 0) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return storeNames
|
|
58
|
+
.map(storeName => {
|
|
59
|
+
const currentStore = allStores?.[storeName] || null;
|
|
60
|
+
const storeError = resolveSystemErrorMessage(currentStore?.getters?.error);
|
|
61
|
+
|
|
62
|
+
if (!storeError) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
error: storeError,
|
|
68
|
+
name: storeName,
|
|
69
|
+
store: currentStore,
|
|
70
|
+
};
|
|
71
|
+
})
|
|
72
|
+
.filter(Boolean);
|
|
73
|
+
}, [allStores, storeNames]);
|
|
74
|
+
|
|
75
|
+
const explicitTitle = resolveText(title);
|
|
76
|
+
const explicitMessage = resolveText(message) || resolveSystemErrorMessage(error);
|
|
77
|
+
const hasStoreError = watchedStoreEntries.length > 0;
|
|
78
|
+
const activeStoreEntry = watchedStoreEntries[0] || null;
|
|
79
|
+
const visibleMessage = hasStoreError ? activeStoreEntry?.error || '' : explicitMessage;
|
|
80
|
+
const hasExplicitContent = storeNames.length === 0
|
|
81
|
+
? Boolean(explicitTitle || explicitMessage || typeof onRetry === 'function')
|
|
82
|
+
: Boolean(explicitMessage || typeof onRetry === 'function');
|
|
83
|
+
const storeDriverSignature = hasStoreError
|
|
84
|
+
? `store:${watchedStoreEntries.map(entry => `${entry.name}:${entry.error}`).join('|')}`
|
|
85
|
+
: '';
|
|
86
|
+
const explicitDriverSignature = hasExplicitContent
|
|
87
|
+
? `explicit:${[explicitTitle, explicitMessage, typeof onRetry === 'function' ? 'retry' : '']
|
|
88
|
+
.map(value => resolveText(value))
|
|
89
|
+
.filter(Boolean)
|
|
90
|
+
.join('|')}`
|
|
91
|
+
: '';
|
|
92
|
+
const activeDriverSignature = storeDriverSignature || explicitDriverSignature;
|
|
93
|
+
const [dismissedSignatures, setDismissedSignatures] = useState(() => new Set());
|
|
94
|
+
const timerRef = useRef(null);
|
|
95
|
+
const textAlign = align === 'left' ? 'left' : 'center';
|
|
96
|
+
const accentColor = pickThemeColor(
|
|
97
|
+
themeColors,
|
|
98
|
+
['inputErrorBorder', 'textDanger', 'modalBorder', 'primary'],
|
|
99
|
+
'#DC2626',
|
|
100
|
+
);
|
|
101
|
+
const backgroundColor = pickThemeColor(
|
|
102
|
+
themeColors,
|
|
103
|
+
['inputErrorBackground', 'modalBackground', 'surface', 'background'],
|
|
104
|
+
'#FFFFFF',
|
|
105
|
+
);
|
|
106
|
+
const overlayColor = pickThemeColor(
|
|
107
|
+
themeColors,
|
|
108
|
+
['modalOverlay', 'overlayBackground'],
|
|
109
|
+
'rgba(15, 23, 42, 0.55)',
|
|
110
|
+
);
|
|
111
|
+
const titleColor = pickThemeColor(
|
|
112
|
+
themeColors,
|
|
113
|
+
['inputErrorText', 'textDanger', 'modalHeaderText', 'text'],
|
|
114
|
+
'#B91C1C',
|
|
115
|
+
);
|
|
116
|
+
const messageColor = pickThemeColor(
|
|
117
|
+
themeColors,
|
|
118
|
+
['modalText', 'textSecondary', 'text'],
|
|
119
|
+
'#334155',
|
|
120
|
+
);
|
|
121
|
+
const closeColor = pickThemeColor(
|
|
122
|
+
themeColors,
|
|
123
|
+
['modalCloseIcon', 'textDanger', 'primary'],
|
|
124
|
+
accentColor,
|
|
125
|
+
);
|
|
126
|
+
const retryAccentColor = pickThemeColor(themeColors, ['primary'], accentColor);
|
|
127
|
+
|
|
128
|
+
const clearStoreErrors = useCallback(() => {
|
|
129
|
+
watchedStoreEntries.forEach(entry => {
|
|
130
|
+
const setError = entry?.store?.actions?.setError;
|
|
131
|
+
|
|
132
|
+
if (typeof setError === 'function') {
|
|
133
|
+
setError('');
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}, [watchedStoreEntries]);
|
|
137
|
+
|
|
138
|
+
const dismissPopup = useCallback(() => {
|
|
139
|
+
if (timerRef.current) {
|
|
140
|
+
clearTimeout(timerRef.current);
|
|
141
|
+
timerRef.current = null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
setDismissedSignatures(previous => {
|
|
145
|
+
const next = new Set(previous);
|
|
146
|
+
|
|
147
|
+
if (storeDriverSignature) {
|
|
148
|
+
next.add(storeDriverSignature);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (explicitDriverSignature) {
|
|
152
|
+
next.add(explicitDriverSignature);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return next;
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
clearStoreErrors();
|
|
159
|
+
}, [clearStoreErrors, explicitDriverSignature, storeDriverSignature]);
|
|
160
|
+
|
|
161
|
+
const isVisible = Boolean(activeDriverSignature) && !dismissedSignatures.has(activeDriverSignature);
|
|
162
|
+
const hasContent = Boolean(explicitTitle || visibleMessage || typeof onRetry === 'function');
|
|
163
|
+
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (!activeDriverSignature) {
|
|
166
|
+
if (timerRef.current) {
|
|
167
|
+
clearTimeout(timerRef.current);
|
|
168
|
+
timerRef.current = null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
setDismissedSignatures(previous => (previous.size > 0 ? new Set() : previous));
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!isVisible) {
|
|
176
|
+
if (timerRef.current) {
|
|
177
|
+
clearTimeout(timerRef.current);
|
|
178
|
+
timerRef.current = null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (timerRef.current) {
|
|
185
|
+
clearTimeout(timerRef.current);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
timerRef.current = setTimeout(() => {
|
|
189
|
+
dismissPopup();
|
|
190
|
+
}, AUTO_CLOSE_MS);
|
|
191
|
+
|
|
192
|
+
return () => {
|
|
193
|
+
if (timerRef.current) {
|
|
194
|
+
clearTimeout(timerRef.current);
|
|
195
|
+
timerRef.current = null;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}, [activeDriverSignature, dismissPopup, isVisible]);
|
|
199
|
+
|
|
200
|
+
if (!isVisible || !hasContent) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<Modal
|
|
206
|
+
animationType="fade"
|
|
207
|
+
onRequestClose={dismissPopup}
|
|
208
|
+
transparent
|
|
209
|
+
visible={isVisible}
|
|
210
|
+
>
|
|
211
|
+
<Pressable
|
|
212
|
+
accessibilityRole="button"
|
|
213
|
+
onPress={dismissPopup}
|
|
214
|
+
style={[
|
|
215
|
+
{
|
|
216
|
+
alignItems: 'center',
|
|
217
|
+
backgroundColor: overlayColor,
|
|
218
|
+
flex: 1,
|
|
219
|
+
justifyContent: 'center',
|
|
220
|
+
padding: 18,
|
|
221
|
+
},
|
|
222
|
+
]}
|
|
223
|
+
>
|
|
224
|
+
<View
|
|
225
|
+
onStartShouldSetResponder={() => true}
|
|
226
|
+
style={[
|
|
227
|
+
{
|
|
228
|
+
backgroundColor,
|
|
229
|
+
borderColor: accentColor,
|
|
230
|
+
borderRadius: 16,
|
|
231
|
+
borderWidth: 1,
|
|
232
|
+
maxWidth: compact ? 360 : 440,
|
|
233
|
+
padding: compact ? 14 : 16,
|
|
234
|
+
shadowColor: accentColor,
|
|
235
|
+
shadowOffset: {height: 10, width: 0},
|
|
236
|
+
shadowOpacity: 0.12,
|
|
237
|
+
shadowRadius: 18,
|
|
238
|
+
width: '100%',
|
|
239
|
+
elevation: 12,
|
|
240
|
+
},
|
|
241
|
+
containerStyle,
|
|
242
|
+
]}
|
|
243
|
+
>
|
|
244
|
+
<View
|
|
245
|
+
style={[
|
|
246
|
+
{
|
|
247
|
+
alignItems: 'flex-start',
|
|
248
|
+
flexDirection: 'row',
|
|
249
|
+
gap: 10,
|
|
250
|
+
justifyContent: 'space-between',
|
|
251
|
+
},
|
|
252
|
+
contentStyle,
|
|
253
|
+
]}
|
|
254
|
+
>
|
|
255
|
+
<View style={{flex: 1, gap: explicitTitle ? 10 : 8}}>
|
|
256
|
+
{explicitTitle ? (
|
|
257
|
+
<Text
|
|
258
|
+
style={[
|
|
259
|
+
{
|
|
260
|
+
color: titleColor,
|
|
261
|
+
fontSize: compact ? 14 : 15,
|
|
262
|
+
fontWeight: '800',
|
|
263
|
+
textAlign,
|
|
264
|
+
},
|
|
265
|
+
titleStyle,
|
|
266
|
+
]}
|
|
267
|
+
>
|
|
268
|
+
{explicitTitle}
|
|
269
|
+
</Text>
|
|
270
|
+
) : null}
|
|
271
|
+
|
|
272
|
+
{visibleMessage ? (
|
|
273
|
+
<Text
|
|
274
|
+
style={[
|
|
275
|
+
{
|
|
276
|
+
color: messageColor,
|
|
277
|
+
fontSize: compact ? 12 : 13,
|
|
278
|
+
lineHeight: compact ? 18 : 19,
|
|
279
|
+
textAlign,
|
|
280
|
+
},
|
|
281
|
+
messageStyle,
|
|
282
|
+
]}
|
|
283
|
+
>
|
|
284
|
+
{visibleMessage}
|
|
285
|
+
</Text>
|
|
286
|
+
) : null}
|
|
287
|
+
|
|
288
|
+
{typeof onRetry === 'function' ? (
|
|
289
|
+
<TouchableOpacity
|
|
290
|
+
accessibilityRole="button"
|
|
291
|
+
activeOpacity={0.85}
|
|
292
|
+
onPress={onRetry}
|
|
293
|
+
style={[
|
|
294
|
+
{
|
|
295
|
+
alignItems: 'center',
|
|
296
|
+
alignSelf: textAlign === 'left' ? 'flex-start' : 'center',
|
|
297
|
+
borderRadius: 999,
|
|
298
|
+
borderWidth: 1,
|
|
299
|
+
borderColor: retryAccentColor,
|
|
300
|
+
backgroundColor: `${retryAccentColor}14`,
|
|
301
|
+
marginTop: 4,
|
|
302
|
+
paddingHorizontal: 12,
|
|
303
|
+
paddingVertical: 8,
|
|
304
|
+
},
|
|
305
|
+
retryButtonStyle,
|
|
306
|
+
]}
|
|
307
|
+
>
|
|
308
|
+
<Text
|
|
309
|
+
style={[
|
|
310
|
+
{
|
|
311
|
+
color: retryAccentColor,
|
|
312
|
+
fontSize: 12,
|
|
313
|
+
fontWeight: '800',
|
|
314
|
+
},
|
|
315
|
+
retryTextStyle,
|
|
316
|
+
]}
|
|
317
|
+
>
|
|
318
|
+
{retryLabel}
|
|
319
|
+
</Text>
|
|
320
|
+
</TouchableOpacity>
|
|
321
|
+
) : null}
|
|
322
|
+
</View>
|
|
323
|
+
|
|
324
|
+
<TouchableOpacity
|
|
325
|
+
accessibilityLabel="Fechar erro"
|
|
326
|
+
accessibilityRole="button"
|
|
327
|
+
activeOpacity={0.8}
|
|
328
|
+
onPress={dismissPopup}
|
|
329
|
+
testID="default-errors-close"
|
|
330
|
+
style={[
|
|
331
|
+
{
|
|
332
|
+
alignItems: 'center',
|
|
333
|
+
borderColor: accentColor,
|
|
334
|
+
borderRadius: 999,
|
|
335
|
+
borderWidth: 1,
|
|
336
|
+
height: 28,
|
|
337
|
+
justifyContent: 'center',
|
|
338
|
+
width: 28,
|
|
339
|
+
},
|
|
340
|
+
]}
|
|
341
|
+
>
|
|
342
|
+
<Text
|
|
343
|
+
style={{
|
|
344
|
+
color: closeColor,
|
|
345
|
+
fontSize: 16,
|
|
346
|
+
fontWeight: '800',
|
|
347
|
+
lineHeight: 16,
|
|
348
|
+
}}
|
|
349
|
+
>
|
|
350
|
+
×
|
|
351
|
+
</Text>
|
|
352
|
+
</TouchableOpacity>
|
|
353
|
+
</View>
|
|
354
|
+
</View>
|
|
355
|
+
</Pressable>
|
|
356
|
+
</Modal>
|
|
357
|
+
);
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
export default DefaultErrors;
|