@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.
Files changed (61) hide show
  1. package/AGENTS.md +47 -45
  2. package/package.json +20 -20
  3. package/src/react/components/errors/DefaultErrors.js +307 -325
  4. package/src/react/components/files/DefaultFile.js +46 -46
  5. package/src/react/components/filters/CompactFilterSelector.js +78 -24
  6. package/src/react/components/filters/CompactFilterSelector.styles.js +17 -5
  7. package/src/react/components/filters/DefaultColumnFilter.js +191 -57
  8. package/src/react/components/filters/DefaultExternalFilters.js +245 -182
  9. package/src/react/components/filters/DefaultSearch.js +131 -92
  10. package/src/react/components/filters/DefaultSearch.styles.js +65 -70
  11. package/src/react/components/form/DefaultForm.js +172 -151
  12. package/src/react/components/form/DefaultForm.styles.js +71 -66
  13. package/src/react/components/help/DefaultTooltip.js +87 -87
  14. package/src/react/components/help/DefaultTooltip.styles.js +61 -61
  15. package/src/react/components/inputs/DefaultDateInput.js +150 -0
  16. package/src/react/components/inputs/DefaultInput.js +162 -127
  17. package/src/react/components/inputs/DefaultInput.styles.js +6 -0
  18. package/src/react/components/inputs/DefaultSelect.js +320 -152
  19. package/src/react/components/inputs/defaultInputUtils.js +40 -19
  20. package/src/react/components/map/DefaultGoogleMap.styles.js +115 -9
  21. package/src/react/components/map/DefaultGoogleMap.web.js +560 -446
  22. package/src/react/components/map/DefaultMap.native.js +73 -71
  23. package/src/react/components/map/DefaultMap.styles.js +16 -16
  24. package/src/react/components/map/DefaultMap.web.js +68 -66
  25. package/src/react/components/map/DefaultNativeMap.native.js +307 -183
  26. package/src/react/components/map/DefaultNativeMap.shared.js +439 -340
  27. package/src/react/components/map/DefaultNativeMap.styles.js +156 -122
  28. package/src/react/components/table/DefaultColumnMenu.js +108 -0
  29. package/src/react/components/table/DefaultDebug.js +233 -0
  30. package/src/react/components/table/DefaultFiltersModal.js +142 -0
  31. package/src/react/components/table/DefaultModalButton.js +18 -0
  32. package/src/react/components/table/DefaultSearchModal.js +49 -0
  33. package/src/react/components/table/DefaultTable.js +345 -1846
  34. package/src/react/components/table/DefaultTable.styles.js +247 -136
  35. package/src/react/components/table/DefaultTable.utils.js +502 -0
  36. package/src/react/components/table/DefaultTableBody.js +26 -0
  37. package/src/react/components/table/DefaultTableCards.js +171 -0
  38. package/src/react/components/table/DefaultTableControls.js +208 -0
  39. package/src/react/components/table/DefaultTableEmptyState.js +22 -0
  40. package/src/react/components/table/DefaultTableFooter.js +172 -0
  41. package/src/react/components/table/DefaultTableInput.js +145 -0
  42. package/src/react/components/table/DefaultTableRows.js +340 -0
  43. package/src/react/components/table/DefaultTableSearch.js +100 -0
  44. package/src/react/components/table/DefaultTableToolbar.js +98 -0
  45. package/src/react/components/table/DefaultToolbarAction.js +56 -0
  46. package/src/react/components/table/useDefaultTablePagination.js +370 -0
  47. package/src/react/components/table/useDefaultTableSorting.js +126 -0
  48. package/src/react/components/table/useDefaultTableTheme.js +58 -0
  49. package/src/react/index.js +10 -10
  50. package/src/react/utils/tableVisibleColumnsPreferences.js +239 -211
  51. package/src/store/default/actions.js +51 -13
  52. package/src/store/default/getters.js +10 -8
  53. package/src/store/default/mutation_types.js +4 -2
  54. package/src/store/default/mutations.js +10 -1
  55. package/src/tests/react/components/errors/DefaultErrors.test.js +162 -162
  56. package/src/tests/react/components/files/DefaultFile.test.js +80 -80
  57. package/src/tests/react/components/form/DefaultForm.test.js +81 -0
  58. package/src/tests/react/components/inputs/DefaultSelect.test.js +185 -0
  59. package/src/tests/react/components/table/DefaultTable.test.js +1183 -0
  60. package/src/tests/react/store/defaultActions.test.js +59 -0
  61. package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +247 -168
@@ -1,50 +1,51 @@
1
1
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
2
  import { ScrollView, Text, TouchableOpacity, View } from 'react-native';
3
+ import { useStore } from '@store';
3
4
  import { formatStoreColumnLabel } from '@controleonline/ui-common/src/react/utils/storeColumns';
4
5
  import DefaultInput from '../inputs/DefaultInput';
5
6
  import {
6
7
  formatSaveValue,
7
- getColumnKey,
8
- isEditableColumn,
9
- normalizeId,
8
+ getColumnKey,
9
+ isEditableColumn,
10
+ normalizeId,
10
11
  normalizeText,
11
12
  resolveEditValue,
12
13
  } from '../inputs/defaultInputUtils';
13
- import styles from './DefaultForm.styles';
14
-
15
- const hasValue = value => {
16
- if (value === null || value === undefined) return false;
17
- if (typeof value === 'boolean' || typeof value === 'number') return true;
18
- if (typeof value === 'object') {
19
- return normalizeText(value.value ?? value.id ?? value['@id'] ?? '') !== '';
20
- }
21
-
22
- return normalizeText(value) !== '';
23
- };
24
-
25
- const resolveDefaultValue = (column, row) => {
26
- const fieldName = getColumnKey(column);
27
- if (row && row[fieldName] !== undefined) return row[fieldName];
28
- if (typeof column?.defaultValue === 'function') return column.defaultValue(row, column);
29
- if (column?.defaultValue !== undefined) return column.defaultValue;
30
- if (typeof column?.initialValue === 'function') return column.initialValue(row, column);
31
- if (column?.initialValue !== undefined) return column.initialValue;
32
- return undefined;
33
- };
34
-
35
- const shouldIncludeColumn = (column, row, mode) => {
36
- const fieldName = getColumnKey(column);
37
- if (!fieldName || column?.isIdentity === true) return false;
38
- if (column?.form === false || column?.multiline === true) return false;
39
- if (mode === 'create' && column?.add === false) return false;
40
- if (mode !== 'create' && !isEditableColumn(column)) return false;
41
- if (typeof column?.visibleForm === 'function') {
42
- return column.visibleForm(row, column) !== false;
43
- }
44
-
45
- return column?.visibleForm !== false;
46
- };
47
-
14
+ import { createStyles } from './DefaultForm.styles';
15
+
16
+ const hasValue = value => {
17
+ if (value === null || value === undefined) return false;
18
+ if (typeof value === 'boolean' || typeof value === 'number') return true;
19
+ if (typeof value === 'object') {
20
+ return normalizeText(value.value ?? value.id ?? value['@id'] ?? '') !== '';
21
+ }
22
+
23
+ return normalizeText(value) !== '';
24
+ };
25
+
26
+ const resolveDefaultValue = (column, row) => {
27
+ const fieldName = getColumnKey(column);
28
+ if (row && row[fieldName] !== undefined) return row[fieldName];
29
+ if (typeof column?.defaultValue === 'function') return column.defaultValue(row, column);
30
+ if (column?.defaultValue !== undefined) return column.defaultValue;
31
+ if (typeof column?.initialValue === 'function') return column.initialValue(row, column);
32
+ if (column?.initialValue !== undefined) return column.initialValue;
33
+ return undefined;
34
+ };
35
+
36
+ const shouldIncludeColumn = (column, row, mode) => {
37
+ const fieldName = getColumnKey(column);
38
+ if (!fieldName || column?.isIdentity === true) return false;
39
+ if (column?.form === false || column?.multiline === true) return false;
40
+ if (mode === 'create' && column?.add === false) return false;
41
+ if (mode !== 'create' && !isEditableColumn(column)) return false;
42
+ if (typeof column?.visibleForm === 'function') {
43
+ return column.visibleForm(row, column) !== false;
44
+ }
45
+
46
+ return column?.visibleForm !== false;
47
+ };
48
+
48
49
  const buildDraft = (columns, row) =>
49
50
  columns.reduce((draft, column) => {
50
51
  const fieldName = getColumnKey(column);
@@ -53,146 +54,166 @@ const buildDraft = (columns, row) =>
53
54
  return draft;
54
55
  }, {});
55
56
 
57
+ const EMPTY_ROW = {};
58
+
56
59
  const DefaultForm = ({
57
- accentColor = '#2563EB',
58
60
  actions = {},
59
61
  columns = [],
60
62
  getOptionsForColumn = null,
61
- mode = 'edit',
63
+ mode = 'edit',
64
+ onBeforeOpen = null,
62
65
  onCancel = null,
63
66
  onSaved = null,
64
- row = {},
67
+ row = EMPTY_ROW,
65
68
  storeName = '',
66
69
  }) => {
70
+ const themeStore = useStore('theme');
71
+ const { colors: themeColors } = themeStore.getters;
72
+ const palette = useMemo(
73
+ () => ({
74
+ buttonBackground: themeColors.buttonBackground,
75
+ buttonBackgroundSecondary: themeColors.buttonBackgroundSecondary,
76
+ buttonBorderSecondary: themeColors.buttonBorderSecondary,
77
+ buttonDisabledBackground: themeColors.buttonDisabledBackground,
78
+ buttonDisabledText: themeColors.buttonDisabledText,
79
+ buttonText: themeColors.buttonText,
80
+ buttonTextSecondary: themeColors.buttonTextSecondary,
81
+ dividerBorder: themeColors.dividerBorder,
82
+ textSecondary: themeColors.textSecondary,
83
+ }),
84
+ [themeColors],
85
+ );
86
+ const styles = useMemo(() => createStyles(palette), [palette]);
67
87
  const formColumns = useMemo(
68
88
  () => columns.filter(column => shouldIncludeColumn(column, row, mode)),
69
89
  [columns, mode, row],
70
- );
71
- const [draft, setDraft] = useState(() => buildDraft(formColumns, row));
72
- const [isSaving, setIsSaving] = useState(false);
73
-
74
- useEffect(() => {
75
- setDraft(buildDraft(formColumns, row));
76
- }, [formColumns, row]);
77
-
78
- const save = useCallback(() => {
79
- if (typeof actions.save !== 'function') {
80
- onCancel?.();
81
- return Promise.resolve(null);
82
- }
83
-
84
- const isCreate = mode === 'create';
85
- const payload = isCreate
86
- ? {}
87
- : { id: normalizeId(row?.['@id'] || row?.id) };
88
-
89
- formColumns.forEach(column => {
90
- const fieldName = getColumnKey(column);
91
- const nextValue = draft[fieldName];
92
-
93
- if (isCreate) {
94
- if (hasValue(nextValue)) {
95
- payload[fieldName] = formatSaveValue(column, nextValue, row);
96
- }
97
- return;
98
- }
99
-
100
- const currentValue = resolveEditValue(column, row);
101
- if (normalizeText(currentValue) !== normalizeText(nextValue)) {
102
- payload[fieldName] = formatSaveValue(column, nextValue, row);
103
- }
104
- });
105
-
106
- const payloadKeys = Object.keys(payload);
107
- if ((isCreate && payloadKeys.length === 0) || (!isCreate && payloadKeys.length <= 1)) {
108
- onCancel?.();
109
- return Promise.resolve(null);
110
- }
111
-
112
- setIsSaving(true);
113
-
114
- return actions.save(payload)
115
- .then(savedItem => {
116
- onSaved?.(savedItem || { ...row, ...draft }, row);
117
- return savedItem;
118
- })
119
- .catch(error => {
120
- console.error(error);
121
- return null;
122
- })
123
- .finally(() => {
124
- setIsSaving(false);
125
- });
126
- }, [actions, draft, formColumns, mode, onCancel, onSaved, row]);
127
-
128
- return (
129
- <>
130
- <ScrollView style={styles.body} keyboardShouldPersistTaps="handled">
131
- {formColumns.length > 0 ? (
132
- <View style={styles.formGrid}>
133
- {formColumns.map(column => {
134
- const fieldName = getColumnKey(column);
135
- const label = formatStoreColumnLabel({
136
- columns,
137
- fieldName,
138
- fallbackLabel: column?.label || fieldName,
139
- storeName,
140
- });
141
-
142
- return (
143
- <View key={fieldName} style={styles.formField}>
90
+ );
91
+ const [draft, setDraft] = useState(() => buildDraft(formColumns, row));
92
+ const [isSaving, setIsSaving] = useState(false);
93
+
94
+ useEffect(() => {
95
+ setDraft(buildDraft(formColumns, row));
96
+ }, [formColumns, row]);
97
+
98
+ const save = useCallback(() => {
99
+ if (typeof actions.save !== 'function') {
100
+ onCancel?.();
101
+ return Promise.resolve(null);
102
+ }
103
+
104
+ const isCreate = mode === 'create';
105
+ const payload = isCreate
106
+ ? {}
107
+ : { id: normalizeId(row?.['@id'] || row?.id) };
108
+
109
+ formColumns.forEach(column => {
110
+ const fieldName = getColumnKey(column);
111
+ const nextValue = draft[fieldName];
112
+
113
+ if (isCreate) {
114
+ if (hasValue(nextValue)) {
115
+ payload[fieldName] = formatSaveValue(column, nextValue, row);
116
+ }
117
+ return;
118
+ }
119
+
120
+ const currentValue = resolveEditValue(column, row);
121
+ if (normalizeText(currentValue) !== normalizeText(nextValue)) {
122
+ payload[fieldName] = formatSaveValue(column, nextValue, row);
123
+ }
124
+ });
125
+
126
+ const payloadKeys = Object.keys(payload);
127
+ if ((isCreate && payloadKeys.length === 0) || (!isCreate && payloadKeys.length <= 1)) {
128
+ onCancel?.();
129
+ return Promise.resolve(null);
130
+ }
131
+
132
+ setIsSaving(true);
133
+
134
+ return actions.save(payload)
135
+ .then(savedItem => {
136
+ onSaved?.(savedItem || { ...row, ...draft }, row);
137
+ return savedItem;
138
+ })
139
+ .catch(error => {
140
+ console.error(error);
141
+ return null;
142
+ })
143
+ .finally(() => {
144
+ setIsSaving(false);
145
+ });
146
+ }, [actions, draft, formColumns, mode, onCancel, onSaved, row]);
147
+
148
+ return (
149
+ <>
150
+ <ScrollView style={styles.body} keyboardShouldPersistTaps="handled">
151
+ {formColumns.length > 0 ? (
152
+ <View style={styles.formGrid}>
153
+ {formColumns.map(column => {
154
+ const fieldName = getColumnKey(column);
155
+ const label = formatStoreColumnLabel({
156
+ columns,
157
+ fieldName,
158
+ fallbackLabel: column?.label || fieldName,
159
+ storeName,
160
+ });
161
+
162
+ return (
163
+ <View key={fieldName} style={styles.formField}>
144
164
  <DefaultInput
145
- accentColor={accentColor}
165
+ accentColor={themeColors.buttonBackground}
146
166
  autoFocus={false}
147
167
  autoSave={false}
148
168
  column={column}
149
- columns={columns}
150
- editing
151
- getOptionsForColumn={getOptionsForColumn}
152
- label={label}
153
- onChangeValue={value => setDraft(prev => ({ ...prev, [fieldName]: value }))}
154
- row={row}
155
- showLabel
156
- storeName={storeName}
157
- value={draft[fieldName]}
158
- variant="form"
159
- />
160
- </View>
161
- );
162
- })}
163
- </View>
164
- ) : (
165
- <View style={styles.emptyBox}>
166
- <Text style={styles.emptyText}>
167
- {global.t?.t(storeName, 'label', 'no_fields') || 'Nenhum campo disponivel'}
168
- </Text>
169
- </View>
170
- )}
171
- </ScrollView>
172
-
169
+ columns={columns}
170
+ editing
171
+ getOptionsForColumn={getOptionsForColumn}
172
+ label={label}
173
+ onBeforeOpen={onBeforeOpen ? () => onBeforeOpen(column) : null}
174
+ onChangeValue={value => setDraft(prev => ({ ...prev, [fieldName]: value }))}
175
+ row={row}
176
+ showLabel
177
+ storeName={storeName}
178
+ value={draft[fieldName]}
179
+ variant="form"
180
+ />
181
+ </View>
182
+ );
183
+ })}
184
+ </View>
185
+ ) : (
186
+ <View style={styles.emptyBox}>
187
+ <Text style={styles.emptyText}>
188
+ {global.t?.t(storeName, 'label', 'no_fields') || 'Nenhum campo disponivel'}
189
+ </Text>
190
+ </View>
191
+ )}
192
+ </ScrollView>
193
+
173
194
  <View style={styles.actions}>
174
195
  <TouchableOpacity style={styles.secondaryButton} onPress={onCancel}>
175
196
  <Text style={styles.secondaryButtonText}>
176
- {global.t?.t(storeName, 'button', 'cancel') || 'Cancelar'}
177
- </Text>
178
- </TouchableOpacity>
197
+ {global.t?.t(storeName, 'button', 'cancel') || 'Cancelar'}
198
+ </Text>
199
+ </TouchableOpacity>
179
200
  <TouchableOpacity
180
201
  style={[
181
202
  styles.primaryButton,
182
- { backgroundColor: accentColor },
203
+ { backgroundColor: palette.buttonBackground },
183
204
  isSaving ? styles.primaryButtonDisabled : null,
184
205
  ]}
185
206
  activeOpacity={0.86}
186
207
  disabled={isSaving}
187
208
  onPress={save}
188
209
  >
189
- <Text style={styles.primaryButtonText}>
210
+ <Text style={[styles.primaryButtonText, isSaving ? styles.primaryButtonTextDisabled : null]}>
190
211
  {isSaving ? 'Salvando' : global.t?.t(storeName, 'button', 'save') || 'Salvar'}
191
212
  </Text>
192
213
  </TouchableOpacity>
193
- </View>
194
- </>
195
- );
196
- };
197
-
198
- export default DefaultForm;
214
+ </View>
215
+ </>
216
+ );
217
+ };
218
+
219
+ export default DefaultForm;
@@ -1,70 +1,75 @@
1
1
  import { StyleSheet } from 'react-native';
2
2
 
3
- const styles = StyleSheet.create({
4
- body: {
5
- maxHeight: 520,
6
- },
7
- formGrid: {
8
- padding: 14,
9
- flexDirection: 'row',
10
- flexWrap: 'wrap',
11
- gap: 10,
12
- },
13
- formField: {
14
- flexGrow: 1,
15
- flexBasis: 220,
16
- minWidth: 220,
17
- },
18
- emptyBox: {
19
- padding: 18,
20
- alignItems: 'center',
21
- justifyContent: 'center',
22
- },
23
- emptyText: {
24
- color: '#64748B',
25
- fontSize: 12,
26
- fontWeight: '700',
27
- },
28
- actions: {
29
- minHeight: 52,
30
- paddingHorizontal: 14,
31
- borderTopWidth: 1,
32
- borderTopColor: '#E2E8F0',
33
- flexDirection: 'row',
34
- alignItems: 'center',
35
- justifyContent: 'flex-end',
36
- gap: 8,
37
- },
38
- secondaryButton: {
39
- height: 34,
40
- borderRadius: 8,
41
- borderWidth: 1,
42
- borderColor: '#CBD5E1',
43
- paddingHorizontal: 14,
44
- alignItems: 'center',
45
- justifyContent: 'center',
46
- backgroundColor: '#FFFFFF',
47
- },
48
- secondaryButtonText: {
49
- color: '#334155',
50
- fontSize: 12,
51
- fontWeight: '800',
52
- },
53
- primaryButton: {
54
- height: 34,
55
- borderRadius: 8,
56
- paddingHorizontal: 16,
57
- alignItems: 'center',
58
- justifyContent: 'center',
59
- },
60
- primaryButtonDisabled: {
61
- opacity: 0.62,
62
- },
63
- primaryButtonText: {
64
- color: '#FFFFFF',
65
- fontSize: 12,
66
- fontWeight: '900',
67
- },
68
- });
3
+ export const createStyles = palette =>
4
+ StyleSheet.create({
5
+ body: {
6
+ maxHeight: 520,
7
+ },
8
+ formGrid: {
9
+ padding: 14,
10
+ flexDirection: 'row',
11
+ flexWrap: 'wrap',
12
+ gap: 10,
13
+ },
14
+ formField: {
15
+ flexGrow: 1,
16
+ flexBasis: 220,
17
+ minWidth: 220,
18
+ },
19
+ emptyBox: {
20
+ padding: 18,
21
+ alignItems: 'center',
22
+ justifyContent: 'center',
23
+ },
24
+ emptyText: {
25
+ color: palette.textSecondary,
26
+ fontSize: 12,
27
+ fontWeight: '700',
28
+ },
29
+ actions: {
30
+ minHeight: 52,
31
+ paddingHorizontal: 14,
32
+ borderTopWidth: 1,
33
+ borderTopColor: palette.dividerBorder,
34
+ flexDirection: 'row',
35
+ alignItems: 'center',
36
+ justifyContent: 'flex-end',
37
+ gap: 8,
38
+ },
39
+ secondaryButton: {
40
+ height: 34,
41
+ borderRadius: 8,
42
+ borderWidth: 1,
43
+ borderColor: palette.buttonBorderSecondary,
44
+ paddingHorizontal: 14,
45
+ alignItems: 'center',
46
+ justifyContent: 'center',
47
+ backgroundColor: palette.buttonBackgroundSecondary,
48
+ },
49
+ secondaryButtonText: {
50
+ color: palette.buttonTextSecondary,
51
+ fontSize: 12,
52
+ fontWeight: '800',
53
+ },
54
+ primaryButton: {
55
+ height: 34,
56
+ borderRadius: 8,
57
+ paddingHorizontal: 16,
58
+ alignItems: 'center',
59
+ justifyContent: 'center',
60
+ },
61
+ primaryButtonDisabled: {
62
+ backgroundColor: palette.buttonDisabledBackground,
63
+ },
64
+ primaryButtonText: {
65
+ color: palette.buttonText,
66
+ fontSize: 12,
67
+ fontWeight: '900',
68
+ },
69
+ primaryButtonTextDisabled: {
70
+ color: palette.buttonDisabledText,
71
+ },
72
+ });
69
73
 
74
+ const styles = createStyles({});
70
75
  export default styles;