@controleonline/ui-default 1.0.263 → 1.0.265
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 +47 -45
- package/package.json +20 -20
- package/src/react/components/errors/DefaultErrors.js +307 -325
- package/src/react/components/files/DefaultFile.js +46 -46
- package/src/react/components/filters/CompactFilterSelector.js +78 -24
- package/src/react/components/filters/CompactFilterSelector.styles.js +17 -5
- package/src/react/components/filters/DefaultColumnFilter.js +191 -57
- package/src/react/components/filters/DefaultExternalFilters.js +245 -182
- package/src/react/components/filters/DefaultSearch.js +131 -92
- package/src/react/components/filters/DefaultSearch.styles.js +65 -70
- package/src/react/components/form/DefaultForm.js +172 -151
- package/src/react/components/form/DefaultForm.styles.js +71 -66
- package/src/react/components/help/DefaultTooltip.js +87 -87
- package/src/react/components/help/DefaultTooltip.styles.js +61 -61
- package/src/react/components/inputs/DefaultDateInput.js +150 -0
- package/src/react/components/inputs/DefaultInput.js +162 -127
- package/src/react/components/inputs/DefaultInput.styles.js +6 -0
- package/src/react/components/inputs/DefaultSelect.js +320 -152
- package/src/react/components/inputs/defaultInputUtils.js +40 -19
- 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/DefaultColumnMenu.js +108 -0
- package/src/react/components/table/DefaultDebug.js +233 -0
- package/src/react/components/table/DefaultFiltersModal.js +142 -0
- package/src/react/components/table/DefaultModalButton.js +18 -0
- package/src/react/components/table/DefaultSearchModal.js +49 -0
- package/src/react/components/table/DefaultTable.js +345 -1846
- package/src/react/components/table/DefaultTable.styles.js +247 -136
- package/src/react/components/table/DefaultTable.utils.js +502 -0
- package/src/react/components/table/DefaultTableBody.js +26 -0
- package/src/react/components/table/DefaultTableCards.js +171 -0
- package/src/react/components/table/DefaultTableControls.js +208 -0
- package/src/react/components/table/DefaultTableEmptyState.js +22 -0
- package/src/react/components/table/DefaultTableFooter.js +172 -0
- package/src/react/components/table/DefaultTableInput.js +145 -0
- package/src/react/components/table/DefaultTableRows.js +340 -0
- package/src/react/components/table/DefaultTableSearch.js +100 -0
- package/src/react/components/table/DefaultTableToolbar.js +98 -0
- package/src/react/components/table/DefaultToolbarAction.js +56 -0
- package/src/react/components/table/useDefaultTablePagination.js +370 -0
- package/src/react/components/table/useDefaultTableSorting.js +126 -0
- package/src/react/components/table/useDefaultTableTheme.js +58 -0
- package/src/react/index.js +10 -10
- package/src/react/utils/tableVisibleColumnsPreferences.js +239 -211
- package/src/store/default/actions.js +51 -13
- package/src/store/default/getters.js +10 -8
- package/src/store/default/mutation_types.js +4 -2
- package/src/store/default/mutations.js +10 -1
- 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/form/DefaultForm.test.js +81 -0
- package/src/tests/react/components/inputs/DefaultSelect.test.js +185 -0
- package/src/tests/react/components/table/DefaultTable.test.js +1183 -0
- package/src/tests/react/store/defaultActions.test.js +59 -0
- package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +247 -168
|
@@ -1,192 +1,360 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import {
|
|
3
|
-
Modal,
|
|
4
|
-
ScrollView,
|
|
5
|
-
Text,
|
|
6
|
-
TextInput,
|
|
7
|
-
TouchableOpacity,
|
|
8
|
-
View,
|
|
9
|
-
} from 'react-native';
|
|
1
|
+
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Modal,
|
|
4
|
+
ScrollView,
|
|
5
|
+
Text,
|
|
6
|
+
TextInput,
|
|
7
|
+
TouchableOpacity,
|
|
8
|
+
View,
|
|
9
|
+
} from 'react-native';
|
|
10
10
|
import Icon from 'react-native-vector-icons/Feather';
|
|
11
|
+
import { getAllStores, useStore } from '@store';
|
|
11
12
|
import {
|
|
12
13
|
buildOptionsFromColumn,
|
|
13
14
|
getColumnKey,
|
|
14
15
|
isEditableColumn,
|
|
16
|
+
mapOptions,
|
|
15
17
|
normalizeOptionKey,
|
|
16
18
|
normalizeText,
|
|
17
19
|
resolveCellText,
|
|
18
20
|
resolveEditValue,
|
|
21
|
+
resolveStoreNameFromList,
|
|
19
22
|
} from './defaultInputUtils';
|
|
20
23
|
import inputStyles from './DefaultInput.styles';
|
|
21
24
|
import styles from './DefaultSelect.styles';
|
|
22
25
|
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
const COMPANY_SCOPED_LIST_STORES = new Set([
|
|
27
|
+
'categories',
|
|
28
|
+
'paymentType',
|
|
29
|
+
'wallet',
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
const normalizeCollectionItems = response => {
|
|
33
|
+
if (Array.isArray(response)) return response;
|
|
34
|
+
if (Array.isArray(response?.member)) return response.member;
|
|
35
|
+
if (Array.isArray(response?.['hydra:member'])) return response['hydra:member'];
|
|
36
|
+
return [];
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const stableSerialize = value => {
|
|
40
|
+
if (value === null || value === undefined) return 'null';
|
|
41
|
+
if (Array.isArray(value)) return `[${value.map(item => stableSerialize(item)).join(',')}]`;
|
|
42
|
+
if (value instanceof Date) return JSON.stringify(value.toISOString());
|
|
43
|
+
if (value && typeof value === 'object') {
|
|
44
|
+
return `{${Object.keys(value)
|
|
45
|
+
.sort()
|
|
46
|
+
.map(key => `${JSON.stringify(key)}:${stableSerialize(value[key])}`)
|
|
47
|
+
.join(',')}}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return JSON.stringify(value);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const resolveListActionName = list =>
|
|
54
|
+
normalizeText(list).split('/')[1] || 'getItems';
|
|
55
|
+
|
|
56
|
+
const resolveCompanyScopedParams = ({ currentCompanyId, listStoreName }) => {
|
|
57
|
+
if (!currentCompanyId || !COMPANY_SCOPED_LIST_STORES.has(listStoreName)) {
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return listStoreName === 'categories'
|
|
62
|
+
? { company: currentCompanyId }
|
|
63
|
+
: { people: currentCompanyId };
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const resolveColumnListLoadParams = ({
|
|
26
67
|
column,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
68
|
+
currentCompanyId,
|
|
69
|
+
requestParams = {},
|
|
70
|
+
searchValue = '',
|
|
71
|
+
}) => {
|
|
72
|
+
const listStoreName = resolveStoreNameFromList(column?.list);
|
|
73
|
+
const resolvedCustomParams = typeof column?.listRequestParams === 'function'
|
|
74
|
+
? column.listRequestParams({ currentCompanyId, requestParams })
|
|
75
|
+
: column?.listRequestParams;
|
|
76
|
+
const customParams =
|
|
77
|
+
resolvedCustomParams &&
|
|
78
|
+
typeof resolvedCustomParams === 'object' &&
|
|
79
|
+
!Array.isArray(resolvedCustomParams)
|
|
80
|
+
? resolvedCustomParams
|
|
81
|
+
: {};
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
...resolveCompanyScopedParams({ currentCompanyId, listStoreName }),
|
|
85
|
+
...customParams,
|
|
86
|
+
...(normalizeText(searchValue)
|
|
87
|
+
? { [column?.listSearchParam || column?.searchParam || 'search']: normalizeText(searchValue) }
|
|
88
|
+
: {}),
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const DefaultSelect = ({
|
|
93
|
+
accentColor = '#2563EB',
|
|
94
|
+
autoSave = true,
|
|
95
|
+
column,
|
|
96
|
+
columns = [],
|
|
97
|
+
containerStyle = null,
|
|
98
|
+
displayValue,
|
|
99
|
+
editing = false,
|
|
100
|
+
getOptionsForColumn = null,
|
|
101
|
+
label = '',
|
|
102
|
+
numberOfLines = 1,
|
|
103
|
+
onBeforeOpen = null,
|
|
34
104
|
onCancelEditing = null,
|
|
35
105
|
onChangeValue = null,
|
|
36
106
|
onSave = null,
|
|
107
|
+
onSearchChange = null,
|
|
37
108
|
onStartEditing = null,
|
|
38
|
-
readTextStyle = null,
|
|
39
|
-
row = {},
|
|
40
|
-
saving = false,
|
|
41
|
-
showLabel = false,
|
|
42
|
-
storeName = '',
|
|
43
|
-
value,
|
|
44
|
-
variant = 'cell',
|
|
109
|
+
readTextStyle = null,
|
|
110
|
+
row = {},
|
|
111
|
+
saving = false,
|
|
112
|
+
showLabel = false,
|
|
113
|
+
storeName = '',
|
|
114
|
+
value,
|
|
115
|
+
variant = 'cell',
|
|
45
116
|
}) => {
|
|
117
|
+
const peopleStore = useStore('people');
|
|
118
|
+
const ownerStore = useStore(storeName);
|
|
119
|
+
const loadedListKeysRef = useRef(new Set());
|
|
46
120
|
const [isOpen, setIsOpen] = useState(false);
|
|
121
|
+
const [loadedItems, setLoadedItems] = useState([]);
|
|
47
122
|
const [searchText, setSearchText] = useState('');
|
|
123
|
+
const currentCompanyId = peopleStore?.getters?.currentCompany?.id;
|
|
124
|
+
const requestParams = ownerStore?.getters?.configs?.requestParams || {};
|
|
125
|
+
|
|
126
|
+
const fieldName = getColumnKey(column);
|
|
127
|
+
const selectedKey = normalizeOptionKey(value ?? resolveEditValue(column, row));
|
|
128
|
+
const options = useMemo(() => {
|
|
129
|
+
const localOptions = buildOptionsFromColumn(column, getOptionsForColumn, storeName);
|
|
130
|
+
const remoteOptions = mapOptions(column, loadedItems, storeName);
|
|
131
|
+
|
|
132
|
+
if (remoteOptions.length === 0) {
|
|
133
|
+
return localOptions;
|
|
134
|
+
}
|
|
48
135
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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';
|
|
136
|
+
const seenKeys = new Set(remoteOptions.map(option => option.key));
|
|
137
|
+
return [
|
|
138
|
+
...remoteOptions,
|
|
139
|
+
...localOptions.filter(option => !seenKeys.has(option.key)),
|
|
140
|
+
];
|
|
141
|
+
}, [column, getOptionsForColumn, loadedItems, storeName]);
|
|
142
|
+
const selected = options.find(option => option.key === selectedKey);
|
|
143
|
+
const resolvedLabel =
|
|
144
|
+
displayValue ??
|
|
145
|
+
selected?.label ??
|
|
146
|
+
resolveCellText({ column, columns, row, storeName, value });
|
|
147
|
+
const canEdit = isEditableColumn(column) && typeof onStartEditing === 'function';
|
|
148
|
+
const isForm = variant === 'form';
|
|
62
149
|
const filteredOptions = useMemo(() => {
|
|
63
150
|
const query = normalizeText(searchText).toLowerCase();
|
|
64
|
-
if (!query) return options;
|
|
151
|
+
if (!query || onSearchChange) return options;
|
|
65
152
|
return options.filter(option =>
|
|
66
|
-
normalizeText(option.label).toLowerCase().includes(query) ||
|
|
67
|
-
normalizeText(option.key).toLowerCase().includes(query),
|
|
68
|
-
);
|
|
153
|
+
normalizeText(option.label).toLowerCase().includes(query) ||
|
|
154
|
+
normalizeText(option.key).toLowerCase().includes(query),
|
|
155
|
+
);
|
|
69
156
|
}, [options, searchText]);
|
|
70
157
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
158
|
+
const applyLoadedItems = useCallback(value => {
|
|
159
|
+
const nextItems = normalizeCollectionItems(value);
|
|
160
|
+
if (nextItems.length > 0 || Array.isArray(value)) {
|
|
161
|
+
setLoadedItems(nextItems);
|
|
162
|
+
}
|
|
163
|
+
}, []);
|
|
74
164
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
165
|
+
const loadRemoteOptions = useCallback((searchValue = '') => {
|
|
166
|
+
const listStoreName = resolveStoreNameFromList(column?.list);
|
|
167
|
+
const actionName = resolveListActionName(column?.list);
|
|
168
|
+
const stores = getAllStores?.() || {};
|
|
169
|
+
const listStore = stores?.[listStoreName];
|
|
170
|
+
const listAction = listStore?.actions?.[actionName];
|
|
80
171
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
172
|
+
if (!listStoreName || typeof listAction !== 'function') {
|
|
173
|
+
return Promise.resolve([]);
|
|
174
|
+
}
|
|
84
175
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
176
|
+
const listLoadParams = resolveColumnListLoadParams({
|
|
177
|
+
column,
|
|
178
|
+
currentCompanyId,
|
|
179
|
+
requestParams,
|
|
180
|
+
searchValue,
|
|
181
|
+
});
|
|
182
|
+
const loadKey = `${getColumnKey(column)}:${listStoreName}:${actionName}:${stableSerialize(listLoadParams)}`;
|
|
183
|
+
|
|
184
|
+
if (
|
|
185
|
+
!normalizeText(searchValue) &&
|
|
186
|
+
loadedListKeysRef.current.has(loadKey) &&
|
|
187
|
+
loadedItems.length > 0
|
|
188
|
+
) {
|
|
189
|
+
return Promise.resolve(loadedItems);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!normalizeText(searchValue)) {
|
|
193
|
+
loadedListKeysRef.current.add(loadKey);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return Promise.resolve(
|
|
197
|
+
listAction({
|
|
198
|
+
...listLoadParams,
|
|
199
|
+
__storeMeta: {
|
|
200
|
+
dedupeKey: `default-select-list-options:${loadKey}`,
|
|
201
|
+
skipSystemError: true,
|
|
202
|
+
},
|
|
203
|
+
}),
|
|
204
|
+
)
|
|
205
|
+
.then(response => {
|
|
206
|
+
applyLoadedItems(response);
|
|
207
|
+
return response;
|
|
208
|
+
})
|
|
209
|
+
.catch(error => {
|
|
210
|
+
loadedListKeysRef.current.delete(loadKey);
|
|
211
|
+
throw error;
|
|
90
212
|
});
|
|
213
|
+
}, [applyLoadedItems, column, currentCompanyId, loadedItems, requestParams]);
|
|
214
|
+
|
|
215
|
+
const handleSearchChange = useCallback(value => {
|
|
216
|
+
setSearchText(value);
|
|
217
|
+
const maybeLoad = onSearchChange ? onSearchChange(value) : loadRemoteOptions(value);
|
|
218
|
+
if (maybeLoad && typeof maybeLoad.then === 'function') {
|
|
219
|
+
maybeLoad.then(applyLoadedItems).catch(() => {});
|
|
91
220
|
return;
|
|
92
221
|
}
|
|
93
222
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
223
|
+
applyLoadedItems(maybeLoad);
|
|
224
|
+
}, [applyLoadedItems, loadRemoteOptions, onSearchChange]);
|
|
225
|
+
|
|
226
|
+
const close = () => {
|
|
227
|
+
setIsOpen(false);
|
|
228
|
+
setSearchText('');
|
|
229
|
+
if (autoSave) onCancelEditing?.();
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const selectOption = option => {
|
|
233
|
+
setIsOpen(false);
|
|
234
|
+
setSearchText('');
|
|
235
|
+
|
|
236
|
+
if (autoSave) {
|
|
237
|
+
onSave?.({
|
|
238
|
+
value: option.key,
|
|
239
|
+
label: option.label,
|
|
240
|
+
object: option.raw,
|
|
241
|
+
});
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
onChangeValue?.(option.key, {
|
|
246
|
+
value: option.key,
|
|
247
|
+
label: option.label,
|
|
248
|
+
object: option.raw,
|
|
249
|
+
});
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const open = async () => {
|
|
253
|
+
if (!canEdit && !isForm) return;
|
|
254
|
+
try {
|
|
255
|
+
if (!editing && !isForm) {
|
|
256
|
+
const maybePromise = onStartEditing?.();
|
|
257
|
+
if (maybePromise && typeof maybePromise.then === 'function') {
|
|
258
|
+
await maybePromise;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const maybeLoad = onBeforeOpen?.();
|
|
263
|
+
if (maybeLoad && typeof maybeLoad.then === 'function') {
|
|
264
|
+
applyLoadedItems(await maybeLoad);
|
|
265
|
+
} else {
|
|
266
|
+
applyLoadedItems(maybeLoad);
|
|
267
|
+
}
|
|
268
|
+
} catch (error) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
104
271
|
setIsOpen(true);
|
|
272
|
+
loadRemoteOptions().catch(() => {});
|
|
105
273
|
};
|
|
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
|
|
274
|
+
|
|
275
|
+
return (
|
|
276
|
+
<View style={[inputStyles.wrap, containerStyle]}>
|
|
277
|
+
{showLabel ? <Text style={inputStyles.fieldLabel}>{label || column?.label || fieldName}</Text> : null}
|
|
278
|
+
|
|
279
|
+
<TouchableOpacity
|
|
280
|
+
style={[inputStyles.readButton, isForm ? inputStyles.readButtonForm : null]}
|
|
281
|
+
activeOpacity={canEdit || isForm ? 0.78 : 1}
|
|
282
|
+
disabled={!canEdit && !isForm}
|
|
283
|
+
onPress={open}
|
|
284
|
+
>
|
|
285
|
+
<Text
|
|
286
|
+
style={[
|
|
287
|
+
inputStyles.readText,
|
|
288
|
+
resolvedLabel === '-' ? inputStyles.mutedText : null,
|
|
289
|
+
readTextStyle,
|
|
290
|
+
]}
|
|
291
|
+
numberOfLines={numberOfLines}
|
|
292
|
+
>
|
|
293
|
+
{resolvedLabel || '-'}
|
|
294
|
+
</Text>
|
|
295
|
+
{saving ? (
|
|
296
|
+
<Text style={[inputStyles.savingText, { color: accentColor }]}>Salvando</Text>
|
|
297
|
+
) : canEdit || isForm ? (
|
|
298
|
+
<Icon style={inputStyles.editIcon} name="chevron-down" size={14} color="#64748B" />
|
|
299
|
+
) : null}
|
|
300
|
+
</TouchableOpacity>
|
|
301
|
+
|
|
302
|
+
<Modal visible={isOpen} transparent animationType="fade" onRequestClose={close}>
|
|
303
|
+
<View style={styles.overlay}>
|
|
304
|
+
<View style={styles.modalCard}>
|
|
305
|
+
<View style={styles.modalHeader}>
|
|
306
|
+
<Text style={styles.modalTitle} numberOfLines={1}>
|
|
307
|
+
{label || column?.label || fieldName}
|
|
308
|
+
</Text>
|
|
309
|
+
<TouchableOpacity style={inputStyles.cancelButton} onPress={close}>
|
|
310
|
+
<Icon name="x" size={16} color="#64748B" />
|
|
311
|
+
</TouchableOpacity>
|
|
312
|
+
</View>
|
|
313
|
+
|
|
314
|
+
<View style={styles.searchBox}>
|
|
315
|
+
<TextInput
|
|
148
316
|
style={[inputStyles.input, inputStyles.formInput]}
|
|
149
317
|
value={searchText}
|
|
150
318
|
placeholder={global.t?.t(storeName, 'input', column?.searchParam || 'search')}
|
|
151
|
-
onChangeText={
|
|
319
|
+
onChangeText={handleSearchChange}
|
|
152
320
|
/>
|
|
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;
|
|
321
|
+
</View>
|
|
322
|
+
|
|
323
|
+
<ScrollView keyboardShouldPersistTaps="handled">
|
|
324
|
+
{filteredOptions.length > 0 ? (
|
|
325
|
+
filteredOptions.map(option => {
|
|
326
|
+
const isSelected = option.key === selectedKey;
|
|
327
|
+
return (
|
|
328
|
+
<TouchableOpacity
|
|
329
|
+
key={`${option.key}_${option.label}`}
|
|
330
|
+
style={styles.optionRow}
|
|
331
|
+
activeOpacity={0.78}
|
|
332
|
+
onPress={() => selectOption(option)}
|
|
333
|
+
>
|
|
334
|
+
<Icon
|
|
335
|
+
name={isSelected ? 'check-circle' : 'circle'}
|
|
336
|
+
size={15}
|
|
337
|
+
color={isSelected ? accentColor : '#CBD5E1'}
|
|
338
|
+
/>
|
|
339
|
+
<Text style={styles.optionText}>
|
|
340
|
+
{option.label}
|
|
341
|
+
</Text>
|
|
342
|
+
</TouchableOpacity>
|
|
343
|
+
);
|
|
344
|
+
})
|
|
345
|
+
) : (
|
|
346
|
+
<View style={styles.emptyBox}>
|
|
347
|
+
<Text style={styles.emptyText}>
|
|
348
|
+
Nenhum resultado encontrado
|
|
349
|
+
</Text>
|
|
350
|
+
</View>
|
|
351
|
+
)}
|
|
352
|
+
</ScrollView>
|
|
353
|
+
</View>
|
|
354
|
+
</View>
|
|
355
|
+
</Modal>
|
|
356
|
+
</View>
|
|
357
|
+
);
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
export default DefaultSelect;
|
|
@@ -25,7 +25,18 @@ const DEFAULT_TRANSLATABLE_FIELDS = new Set([
|
|
|
25
25
|
'status',
|
|
26
26
|
]);
|
|
27
27
|
|
|
28
|
-
export const getColumnKey = column => column?.key || column?.name || '';
|
|
28
|
+
export const getColumnKey = column => column?.key || column?.name || '';
|
|
29
|
+
|
|
30
|
+
export const isDateColumn = column =>
|
|
31
|
+
column?.inputType === 'date' ||
|
|
32
|
+
column?.type === 'date';
|
|
33
|
+
|
|
34
|
+
export const isDateRangeColumn = column =>
|
|
35
|
+
column?.inputType === 'date-range' ||
|
|
36
|
+
column?.type === 'range-date';
|
|
37
|
+
|
|
38
|
+
export const isDateLikeColumn = column =>
|
|
39
|
+
isDateColumn(column) || isDateRangeColumn(column);
|
|
29
40
|
|
|
30
41
|
const normalizeColumnKey = value =>
|
|
31
42
|
normalizeText(value)
|
|
@@ -114,7 +125,8 @@ export const resolveOptionLabel = (column, option, storeName = '') => {
|
|
|
114
125
|
return translateOptionLabel(rawLabel);
|
|
115
126
|
};
|
|
116
127
|
|
|
117
|
-
export const resolveStoreNameFromList = list =>
|
|
128
|
+
export const resolveStoreNameFromList = list =>
|
|
129
|
+
typeof list === 'string' ? normalizeText(list).split('/')[0] || '' : '';
|
|
118
130
|
|
|
119
131
|
const filterItemsByListRequestParams = (items = [], column = null) => {
|
|
120
132
|
const requestParams =
|
|
@@ -164,13 +176,17 @@ export const mapOptions = (column, items = [], storeName = '') => {
|
|
|
164
176
|
return options;
|
|
165
177
|
};
|
|
166
178
|
|
|
167
|
-
export const buildOptionsFromColumn = (column, getOptionsForColumn = null, storeName = '') => {
|
|
168
|
-
const explicitOptions = getOptionsForColumn?.(column);
|
|
169
|
-
if (Array.isArray(explicitOptions) && explicitOptions.length > 0) {
|
|
170
|
-
return mapOptions(column, explicitOptions, storeName);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
export const buildOptionsFromColumn = (column, getOptionsForColumn = null, storeName = '') => {
|
|
180
|
+
const explicitOptions = getOptionsForColumn?.(column);
|
|
181
|
+
if (Array.isArray(explicitOptions) && explicitOptions.length > 0) {
|
|
182
|
+
return mapOptions(column, explicitOptions, storeName);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (Array.isArray(column?.list)) {
|
|
186
|
+
return mapOptions(column, column.list, storeName);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const listStoreName = resolveStoreNameFromList(column?.list);
|
|
174
190
|
if (!listStoreName) return [];
|
|
175
191
|
|
|
176
192
|
const listStore = getAllStores()?.[listStoreName];
|
|
@@ -209,9 +225,9 @@ export const resolveEditValue = (column, row, value) => {
|
|
|
209
225
|
return normalizeText(column.editFormat(rawValue, column, row, true));
|
|
210
226
|
}
|
|
211
227
|
|
|
212
|
-
if ((column
|
|
213
|
-
return normalizeText(Formatter.formatDateYmdTodmY(rawValue));
|
|
214
|
-
}
|
|
228
|
+
if (isDateLikeColumn(column) && rawValue) {
|
|
229
|
+
return normalizeText(Formatter.formatDateYmdTodmY(rawValue));
|
|
230
|
+
}
|
|
215
231
|
|
|
216
232
|
return normalizeText(rawValue);
|
|
217
233
|
};
|
|
@@ -221,10 +237,15 @@ export const formatSaveValue = (column, value, row) => {
|
|
|
221
237
|
return column.saveFormat(value, column, row);
|
|
222
238
|
}
|
|
223
239
|
|
|
224
|
-
if (value && typeof value === 'object') {
|
|
225
|
-
if (value.value) return Number.isNaN(Number(value.value)) ? value.value : Number(value.value);
|
|
226
|
-
if (value['@id']) return value['@id'];
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
240
|
+
if (value && typeof value === 'object') {
|
|
241
|
+
if (value.value) return Number.isNaN(Number(value.value)) ? value.value : Number(value.value);
|
|
242
|
+
if (value['@id']) return value['@id'];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (isDateLikeColumn(column) && value) {
|
|
246
|
+
const normalizedDate = Formatter.buildAmericanDate(String(value));
|
|
247
|
+
return normalizedDate || value;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return Number.isNaN(Number(value)) || value === '' ? value : Number(value);
|
|
251
|
+
};
|