@controleonline/ui-default 1.0.263

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 (56) hide show
  1. package/.github/agents/developer.agent.md +30 -0
  2. package/.github/agents/devops.agent.md +30 -0
  3. package/.github/agents/qa.agent.md +30 -0
  4. package/.github/agents/security.agent.md +30 -0
  5. package/.scrutinizer.yml +20 -0
  6. package/AGENTS.md +47 -0
  7. package/FUNDING.yml +1 -0
  8. package/package.json +21 -0
  9. package/src/react/components/errors/DefaultErrors.js +360 -0
  10. package/src/react/components/files/DefaultFile.js +46 -0
  11. package/src/react/components/filters/CompactFilterSelector.js +262 -0
  12. package/src/react/components/filters/CompactFilterSelector.styles.js +124 -0
  13. package/src/react/components/filters/DateShortcutFilter.js +264 -0
  14. package/src/react/components/filters/DateShortcutFilter.styles.js +82 -0
  15. package/src/react/components/filters/DefaultColumnFilter.js +97 -0
  16. package/src/react/components/filters/DefaultColumnFilter.styles.js +21 -0
  17. package/src/react/components/filters/DefaultExternalFilters.js +441 -0
  18. package/src/react/components/filters/DefaultExternalFilters.styles.js +177 -0
  19. package/src/react/components/filters/DefaultSearch.js +103 -0
  20. package/src/react/components/filters/DefaultSearch.styles.js +70 -0
  21. package/src/react/components/filters/dateFilterSelection.js +29 -0
  22. package/src/react/components/form/DefaultForm.js +198 -0
  23. package/src/react/components/form/DefaultForm.styles.js +70 -0
  24. package/src/react/components/help/DefaultTooltip.js +87 -0
  25. package/src/react/components/help/DefaultTooltip.styles.js +61 -0
  26. package/src/react/components/inputs/DefaultInput.js +160 -0
  27. package/src/react/components/inputs/DefaultInput.styles.js +93 -0
  28. package/src/react/components/inputs/DefaultSelect.js +192 -0
  29. package/src/react/components/inputs/DefaultSelect.styles.js +65 -0
  30. package/src/react/components/inputs/defaultInputUtils.js +230 -0
  31. package/src/react/components/map/DefaultGoogleMap.styles.js +9 -0
  32. package/src/react/components/map/DefaultGoogleMap.web.js +698 -0
  33. package/src/react/components/map/DefaultMap.native.js +71 -0
  34. package/src/react/components/map/DefaultMap.shared.js +762 -0
  35. package/src/react/components/map/DefaultMap.styles.js +16 -0
  36. package/src/react/components/map/DefaultMap.web.js +66 -0
  37. package/src/react/components/map/DefaultNativeMap.native.js +615 -0
  38. package/src/react/components/map/DefaultNativeMap.shared.js +532 -0
  39. package/src/react/components/map/DefaultNativeMap.styles.js +122 -0
  40. package/src/react/components/table/DefaultTable.js +1897 -0
  41. package/src/react/components/table/DefaultTable.styles.js +610 -0
  42. package/src/react/index.js +10 -0
  43. package/src/react/utils/tableVisibleColumnsPreferences.js +264 -0
  44. package/src/store/default/actions.js +444 -0
  45. package/src/store/default/getters.js +28 -0
  46. package/src/store/default/mutation_types.js +26 -0
  47. package/src/store/default/mutations.js +138 -0
  48. package/src/tests/react/components/DateShortcutFilter.test.js +96 -0
  49. package/src/tests/react/components/errors/DefaultErrors.test.js +162 -0
  50. package/src/tests/react/components/files/DefaultFile.test.js +80 -0
  51. package/src/tests/react/components/map/DefaultMap.shared.test.js +162 -0
  52. package/src/tests/react/filters/dateFilterSelection.test.js +46 -0
  53. package/src/tests/react/inputs/defaultInputUtils.test.js +45 -0
  54. package/src/tests/react/store/defaultActions.test.js +112 -0
  55. package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +223 -0
  56. package/src/utils/filters.js +56 -0
@@ -0,0 +1,87 @@
1
+ import React, {useCallback, useState} from 'react';
2
+ import {Modal, Pressable, Text, TouchableOpacity, View} from 'react-native';
3
+ import styles from './DefaultTooltip.styles';
4
+
5
+ const DefaultTooltip = ({
6
+ accentColor = '#0EA5E9',
7
+ label = '?',
8
+ message = '',
9
+ style = null,
10
+ textStyle = null,
11
+ title = 'Ajuda',
12
+ }) => {
13
+ const [visible, setVisible] = useState(false);
14
+
15
+ const openTooltip = useCallback(() => {
16
+ if (!message) {
17
+ return;
18
+ }
19
+
20
+ setVisible(true);
21
+ }, [message]);
22
+
23
+ const closeTooltip = useCallback(() => {
24
+ setVisible(false);
25
+ }, []);
26
+
27
+ return (
28
+ <>
29
+ <TouchableOpacity
30
+ accessibilityRole="button"
31
+ activeOpacity={0.85}
32
+ disabled={!message}
33
+ onPress={openTooltip}
34
+ style={[
35
+ styles.button,
36
+ {
37
+ borderColor: accentColor,
38
+ backgroundColor: `${accentColor}14`,
39
+ },
40
+ !message && {opacity: 0.55},
41
+ style,
42
+ ]}>
43
+ <Text style={[styles.label, {color: accentColor}, textStyle]}>
44
+ {label}
45
+ </Text>
46
+ </TouchableOpacity>
47
+
48
+ <Modal
49
+ animationType="fade"
50
+ onRequestClose={closeTooltip}
51
+ transparent
52
+ visible={visible}>
53
+ <Pressable style={styles.overlay} onPress={closeTooltip}>
54
+ <View
55
+ onStartShouldSetResponder={() => true}
56
+ style={[
57
+ styles.card,
58
+ {
59
+ borderColor: accentColor,
60
+ },
61
+ ]}>
62
+ <View style={styles.cardHeader}>
63
+ <Text style={[styles.title, {color: accentColor}]}>{title}</Text>
64
+ <TouchableOpacity
65
+ accessibilityRole="button"
66
+ activeOpacity={0.8}
67
+ onPress={closeTooltip}
68
+ style={[
69
+ styles.closeButton,
70
+ {
71
+ borderColor: accentColor,
72
+ },
73
+ ]}>
74
+ <Text style={[styles.closeButtonText, {color: accentColor}]}>
75
+ Fechar
76
+ </Text>
77
+ </TouchableOpacity>
78
+ </View>
79
+ <Text style={styles.message}>{message}</Text>
80
+ </View>
81
+ </Pressable>
82
+ </Modal>
83
+ </>
84
+ );
85
+ };
86
+
87
+ export default DefaultTooltip;
@@ -0,0 +1,61 @@
1
+ import {StyleSheet} from 'react-native';
2
+
3
+ const styles = StyleSheet.create({
4
+ button: {
5
+ alignItems: 'center',
6
+ borderRadius: 999,
7
+ borderWidth: 1,
8
+ height: 22,
9
+ justifyContent: 'center',
10
+ width: 22,
11
+ },
12
+ label: {
13
+ fontSize: 12,
14
+ fontWeight: '800',
15
+ lineHeight: 12,
16
+ },
17
+ overlay: {
18
+ alignItems: 'center',
19
+ backgroundColor: 'rgba(15, 23, 42, 0.55)',
20
+ flex: 1,
21
+ justifyContent: 'center',
22
+ padding: 18,
23
+ },
24
+ card: {
25
+ backgroundColor: '#FFFFFF',
26
+ borderRadius: 16,
27
+ borderWidth: 1,
28
+ maxWidth: 420,
29
+ padding: 16,
30
+ width: '100%',
31
+ },
32
+ cardHeader: {
33
+ alignItems: 'center',
34
+ flexDirection: 'row',
35
+ gap: 10,
36
+ justifyContent: 'space-between',
37
+ marginBottom: 12,
38
+ },
39
+ title: {
40
+ flex: 1,
41
+ fontSize: 15,
42
+ fontWeight: '800',
43
+ },
44
+ message: {
45
+ color: '#334155',
46
+ fontSize: 13,
47
+ lineHeight: 19,
48
+ },
49
+ closeButton: {
50
+ borderRadius: 999,
51
+ borderWidth: 1,
52
+ paddingHorizontal: 10,
53
+ paddingVertical: 6,
54
+ },
55
+ closeButtonText: {
56
+ fontSize: 12,
57
+ fontWeight: '700',
58
+ },
59
+ });
60
+
61
+ export default styles;
@@ -0,0 +1,160 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import { Text, TextInput, TouchableOpacity, View } from 'react-native';
3
+ import Icon from 'react-native-vector-icons/Feather';
4
+ import {
5
+ getColumnKey,
6
+ isEditableColumn,
7
+ normalizeText,
8
+ resolveCellText,
9
+ resolveEditValue,
10
+ } from './defaultInputUtils';
11
+ import DefaultSelect from './DefaultSelect';
12
+ import styles from './DefaultInput.styles';
13
+
14
+ const DefaultInput = ({
15
+ accentColor = '#2563EB',
16
+ autoFocus = true,
17
+ autoSave = true,
18
+ column,
19
+ columns = [],
20
+ containerStyle = null,
21
+ displayValue,
22
+ editing = false,
23
+ getOptionsForColumn = null,
24
+ inputStyle = null,
25
+ label = '',
26
+ numberOfLines = 1,
27
+ onCancelEditing = null,
28
+ onChangeValue = null,
29
+ onSave = null,
30
+ onStartEditing = null,
31
+ readTextStyle = null,
32
+ row = {},
33
+ saving = false,
34
+ showLabel = false,
35
+ storeName = '',
36
+ value,
37
+ variant = 'cell',
38
+ }) => {
39
+ const fieldName = getColumnKey(column);
40
+ const isForm = variant === 'form';
41
+ const canEdit = isEditableColumn(column) && typeof onStartEditing === 'function';
42
+ const editValue = useMemo(
43
+ () => normalizeText(value ?? resolveEditValue(column, row)),
44
+ [column, row, value],
45
+ );
46
+ const [draftValue, setDraftValue] = useState(editValue);
47
+ const resolvedLabel =
48
+ displayValue ?? resolveCellText({ column, columns, row, storeName, value });
49
+
50
+ useEffect(() => {
51
+ setDraftValue(editValue);
52
+ }, [editValue, editing]);
53
+
54
+ if (column?.list) {
55
+ return (
56
+ <DefaultSelect
57
+ accentColor={accentColor}
58
+ autoSave={autoSave}
59
+ column={column}
60
+ columns={columns}
61
+ containerStyle={containerStyle}
62
+ displayValue={displayValue}
63
+ editing={editing}
64
+ getOptionsForColumn={getOptionsForColumn}
65
+ label={label}
66
+ numberOfLines={numberOfLines}
67
+ onCancelEditing={onCancelEditing}
68
+ onChangeValue={onChangeValue}
69
+ onSave={onSave}
70
+ onStartEditing={onStartEditing}
71
+ readTextStyle={readTextStyle}
72
+ row={row}
73
+ saving={saving}
74
+ showLabel={showLabel}
75
+ storeName={storeName}
76
+ value={value}
77
+ variant={variant}
78
+ />
79
+ );
80
+ }
81
+
82
+ const saveDraft = () => {
83
+ if (autoSave) {
84
+ onSave?.(draftValue);
85
+ return;
86
+ }
87
+
88
+ onChangeValue?.(draftValue);
89
+ };
90
+
91
+ const updateDraft = nextValue => {
92
+ setDraftValue(nextValue);
93
+ if (!autoSave) onChangeValue?.(nextValue);
94
+ };
95
+
96
+ if (!editing && !isForm) {
97
+ return (
98
+ <View style={[styles.wrap, containerStyle]}>
99
+ {showLabel ? <Text style={styles.fieldLabel}>{label || column?.label || fieldName}</Text> : null}
100
+ <TouchableOpacity
101
+ style={styles.readButton}
102
+ activeOpacity={canEdit ? 0.78 : 1}
103
+ disabled={!canEdit}
104
+ onPress={() => onStartEditing?.()}
105
+ >
106
+ <Text
107
+ style={[
108
+ styles.readText,
109
+ resolvedLabel === '-' ? styles.mutedText : null,
110
+ readTextStyle,
111
+ ]}
112
+ numberOfLines={numberOfLines}
113
+ >
114
+ {column?.prefix || ''}
115
+ {resolvedLabel || '-'}
116
+ {column?.sufix || column?.suffix || ''}
117
+ </Text>
118
+ {saving ? (
119
+ <Text style={[styles.savingText, { color: accentColor }]}>Salvando</Text>
120
+ ) : canEdit ? (
121
+ <Icon style={styles.editIcon} name="edit-2" size={13} color="#64748B" />
122
+ ) : null}
123
+ </TouchableOpacity>
124
+ </View>
125
+ );
126
+ }
127
+
128
+ return (
129
+ <View style={[styles.wrap, containerStyle]}>
130
+ {showLabel ? <Text style={styles.fieldLabel}>{label || column?.label || fieldName}</Text> : null}
131
+ <View style={styles.editingWrap}>
132
+ <View style={styles.editingRow}>
133
+ <TextInput
134
+ style={[
135
+ styles.input,
136
+ isForm ? styles.formInput : null,
137
+ inputStyle,
138
+ ]}
139
+ value={draftValue}
140
+ keyboardType={column?.inputType === 'number' || column?.inputType === 'float' ? 'numeric' : 'default'}
141
+ placeholder={global.t?.t(storeName, 'input', column?.label || fieldName)}
142
+ onBlur={autoSave ? saveDraft : undefined}
143
+ onChangeText={updateDraft}
144
+ onSubmitEditing={saveDraft}
145
+ autoFocus={autoFocus && !isForm}
146
+ selectTextOnFocus
147
+ />
148
+ {!isForm ? (
149
+ <TouchableOpacity style={styles.cancelButton} activeOpacity={0.8} onPress={onCancelEditing}>
150
+ <Icon name="x" size={14} color="#64748B" />
151
+ </TouchableOpacity>
152
+ ) : null}
153
+ </View>
154
+ {saving ? <Text style={styles.savingText}>Salvando</Text> : null}
155
+ </View>
156
+ </View>
157
+ );
158
+ };
159
+
160
+ export default DefaultInput;
@@ -0,0 +1,93 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ const styles = StyleSheet.create({
4
+ wrap: {
5
+ width: '100%',
6
+ minWidth: 0,
7
+ },
8
+ readButton: {
9
+ minHeight: 28,
10
+ width: '100%',
11
+ minWidth: 0,
12
+ flexDirection: 'row',
13
+ alignItems: 'center',
14
+ gap: 6,
15
+ },
16
+ readButtonForm: {
17
+ minHeight: 34,
18
+ borderWidth: 1,
19
+ borderColor: '#CBD5E1',
20
+ borderRadius: 8,
21
+ paddingHorizontal: 10,
22
+ backgroundColor: '#FFFFFF',
23
+ },
24
+ readText: {
25
+ flex: 1,
26
+ minWidth: 0,
27
+ color: '#0F172A',
28
+ fontSize: 12,
29
+ fontWeight: '700',
30
+ },
31
+ mutedText: {
32
+ color: '#94A3B8',
33
+ },
34
+ editIcon: {
35
+ flexShrink: 0,
36
+ },
37
+ fieldLabel: {
38
+ marginBottom: 4,
39
+ color: '#64748B',
40
+ fontSize: 10,
41
+ fontWeight: '900',
42
+ letterSpacing: 0.3,
43
+ textTransform: 'uppercase',
44
+ },
45
+ editingWrap: {
46
+ width: '100%',
47
+ minWidth: 0,
48
+ },
49
+ editingRow: {
50
+ width: '100%',
51
+ minWidth: 0,
52
+ flexDirection: 'row',
53
+ alignItems: 'center',
54
+ gap: 6,
55
+ },
56
+ input: {
57
+ flex: 1,
58
+ minWidth: 0,
59
+ height: 32,
60
+ borderWidth: 1,
61
+ borderColor: '#93C5FD',
62
+ borderRadius: 8,
63
+ paddingHorizontal: 8,
64
+ color: '#0F172A',
65
+ fontSize: 12,
66
+ fontWeight: '700',
67
+ backgroundColor: '#FFFFFF',
68
+ },
69
+ formInput: {
70
+ height: 34,
71
+ borderColor: '#CBD5E1',
72
+ paddingHorizontal: 10,
73
+ },
74
+ cancelButton: {
75
+ width: 28,
76
+ height: 28,
77
+ borderRadius: 14,
78
+ alignItems: 'center',
79
+ justifyContent: 'center',
80
+ backgroundColor: '#FFFFFF',
81
+ borderWidth: 1,
82
+ borderColor: '#CBD5E1',
83
+ },
84
+ savingText: {
85
+ marginTop: 2,
86
+ color: '#64748B',
87
+ fontSize: 9,
88
+ fontWeight: '800',
89
+ textTransform: 'uppercase',
90
+ },
91
+ });
92
+
93
+ export default styles;
@@ -0,0 +1,192 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import {
3
+ Modal,
4
+ ScrollView,
5
+ Text,
6
+ TextInput,
7
+ TouchableOpacity,
8
+ View,
9
+ } from 'react-native';
10
+ import Icon from 'react-native-vector-icons/Feather';
11
+ import {
12
+ buildOptionsFromColumn,
13
+ getColumnKey,
14
+ isEditableColumn,
15
+ normalizeOptionKey,
16
+ normalizeText,
17
+ resolveCellText,
18
+ resolveEditValue,
19
+ } from './defaultInputUtils';
20
+ import inputStyles from './DefaultInput.styles';
21
+ import styles from './DefaultSelect.styles';
22
+
23
+ const DefaultSelect = ({
24
+ accentColor = '#2563EB',
25
+ autoSave = true,
26
+ column,
27
+ columns = [],
28
+ containerStyle = null,
29
+ displayValue,
30
+ editing = false,
31
+ getOptionsForColumn = null,
32
+ label = '',
33
+ numberOfLines = 1,
34
+ onCancelEditing = null,
35
+ onChangeValue = null,
36
+ onSave = null,
37
+ onStartEditing = null,
38
+ readTextStyle = null,
39
+ row = {},
40
+ saving = false,
41
+ showLabel = false,
42
+ storeName = '',
43
+ value,
44
+ variant = 'cell',
45
+ }) => {
46
+ const [isOpen, setIsOpen] = useState(false);
47
+ const [searchText, setSearchText] = useState('');
48
+
49
+ const fieldName = getColumnKey(column);
50
+ const selectedKey = normalizeOptionKey(value ?? resolveEditValue(column, row));
51
+ const options = useMemo(
52
+ () => buildOptionsFromColumn(column, getOptionsForColumn, storeName),
53
+ [column, getOptionsForColumn, storeName],
54
+ );
55
+ const selected = options.find(option => option.key === selectedKey);
56
+ const resolvedLabel =
57
+ displayValue ??
58
+ selected?.label ??
59
+ resolveCellText({ column, columns, row, storeName, value });
60
+ const canEdit = isEditableColumn(column) && typeof onStartEditing === 'function';
61
+ const isForm = variant === 'form';
62
+ const filteredOptions = useMemo(() => {
63
+ const query = normalizeText(searchText).toLowerCase();
64
+ if (!query) return options;
65
+ return options.filter(option =>
66
+ normalizeText(option.label).toLowerCase().includes(query) ||
67
+ normalizeText(option.key).toLowerCase().includes(query),
68
+ );
69
+ }, [options, searchText]);
70
+
71
+ useEffect(() => {
72
+ if (editing && autoSave) setIsOpen(true);
73
+ }, [autoSave, editing]);
74
+
75
+ const close = () => {
76
+ setIsOpen(false);
77
+ setSearchText('');
78
+ if (autoSave) onCancelEditing?.();
79
+ };
80
+
81
+ const selectOption = option => {
82
+ setIsOpen(false);
83
+ setSearchText('');
84
+
85
+ if (autoSave) {
86
+ onSave?.({
87
+ value: option.key,
88
+ label: option.label,
89
+ object: option.raw,
90
+ });
91
+ return;
92
+ }
93
+
94
+ onChangeValue?.(option.key, {
95
+ value: option.key,
96
+ label: option.label,
97
+ object: option.raw,
98
+ });
99
+ };
100
+
101
+ const open = () => {
102
+ if (!canEdit && !isForm) return;
103
+ if (!editing && !isForm) onStartEditing?.();
104
+ setIsOpen(true);
105
+ };
106
+
107
+ return (
108
+ <View style={[inputStyles.wrap, containerStyle]}>
109
+ {showLabel ? <Text style={inputStyles.fieldLabel}>{label || column?.label || fieldName}</Text> : null}
110
+
111
+ <TouchableOpacity
112
+ style={[inputStyles.readButton, isForm ? inputStyles.readButtonForm : null]}
113
+ activeOpacity={canEdit || isForm ? 0.78 : 1}
114
+ disabled={!canEdit && !isForm}
115
+ onPress={open}
116
+ >
117
+ <Text
118
+ style={[
119
+ inputStyles.readText,
120
+ resolvedLabel === '-' ? inputStyles.mutedText : null,
121
+ readTextStyle,
122
+ ]}
123
+ numberOfLines={numberOfLines}
124
+ >
125
+ {resolvedLabel || '-'}
126
+ </Text>
127
+ {saving ? (
128
+ <Text style={[inputStyles.savingText, { color: accentColor }]}>Salvando</Text>
129
+ ) : canEdit || isForm ? (
130
+ <Icon style={inputStyles.editIcon} name="chevron-down" size={14} color="#64748B" />
131
+ ) : null}
132
+ </TouchableOpacity>
133
+
134
+ <Modal visible={isOpen} transparent animationType="fade" onRequestClose={close}>
135
+ <View style={styles.overlay}>
136
+ <View style={styles.modalCard}>
137
+ <View style={styles.modalHeader}>
138
+ <Text style={styles.modalTitle} numberOfLines={1}>
139
+ {label || column?.label || fieldName}
140
+ </Text>
141
+ <TouchableOpacity style={inputStyles.cancelButton} onPress={close}>
142
+ <Icon name="x" size={16} color="#64748B" />
143
+ </TouchableOpacity>
144
+ </View>
145
+
146
+ <View style={styles.searchBox}>
147
+ <TextInput
148
+ style={[inputStyles.input, inputStyles.formInput]}
149
+ value={searchText}
150
+ placeholder={global.t?.t(storeName, 'input', column?.searchParam || 'search')}
151
+ onChangeText={setSearchText}
152
+ />
153
+ </View>
154
+
155
+ <ScrollView keyboardShouldPersistTaps="handled">
156
+ {filteredOptions.length > 0 ? (
157
+ filteredOptions.map(option => {
158
+ const isSelected = option.key === selectedKey;
159
+ return (
160
+ <TouchableOpacity
161
+ key={`${option.key}_${option.label}`}
162
+ style={styles.optionRow}
163
+ activeOpacity={0.78}
164
+ onPress={() => selectOption(option)}
165
+ >
166
+ <Icon
167
+ name={isSelected ? 'check-circle' : 'circle'}
168
+ size={15}
169
+ color={isSelected ? accentColor : '#CBD5E1'}
170
+ />
171
+ <Text style={styles.optionText}>
172
+ {option.label}
173
+ </Text>
174
+ </TouchableOpacity>
175
+ );
176
+ })
177
+ ) : (
178
+ <View style={styles.emptyBox}>
179
+ <Text style={styles.emptyText}>
180
+ Nenhum resultado encontrado
181
+ </Text>
182
+ </View>
183
+ )}
184
+ </ScrollView>
185
+ </View>
186
+ </View>
187
+ </Modal>
188
+ </View>
189
+ );
190
+ };
191
+
192
+ export default DefaultSelect;
@@ -0,0 +1,65 @@
1
+ import { StyleSheet } from 'react-native';
2
+
3
+ const styles = StyleSheet.create({
4
+ overlay: {
5
+ flex: 1,
6
+ alignItems: 'center',
7
+ justifyContent: 'center',
8
+ padding: 16,
9
+ backgroundColor: 'rgba(15,23,42,0.38)',
10
+ },
11
+ modalCard: {
12
+ width: '100%',
13
+ maxWidth: 420,
14
+ maxHeight: '78%',
15
+ borderRadius: 12,
16
+ backgroundColor: '#FFFFFF',
17
+ overflow: 'hidden',
18
+ },
19
+ modalHeader: {
20
+ minHeight: 48,
21
+ paddingHorizontal: 12,
22
+ borderBottomWidth: 1,
23
+ borderBottomColor: '#E2E8F0',
24
+ flexDirection: 'row',
25
+ alignItems: 'center',
26
+ justifyContent: 'space-between',
27
+ gap: 8,
28
+ },
29
+ modalTitle: {
30
+ flex: 1,
31
+ color: '#0F172A',
32
+ fontSize: 15,
33
+ fontWeight: '900',
34
+ },
35
+ searchBox: {
36
+ padding: 10,
37
+ },
38
+ optionRow: {
39
+ minHeight: 40,
40
+ paddingHorizontal: 14,
41
+ paddingVertical: 9,
42
+ borderTopWidth: 1,
43
+ borderTopColor: '#F1F5F9',
44
+ flexDirection: 'row',
45
+ alignItems: 'center',
46
+ gap: 8,
47
+ },
48
+ optionText: {
49
+ flex: 1,
50
+ color: '#0F172A',
51
+ fontSize: 13,
52
+ fontWeight: '700',
53
+ },
54
+ emptyBox: {
55
+ padding: 18,
56
+ alignItems: 'center',
57
+ },
58
+ emptyText: {
59
+ color: '#64748B',
60
+ fontSize: 12,
61
+ fontWeight: '700',
62
+ },
63
+ });
64
+
65
+ export default styles;