@controleonline/ui-default 1.0.272 → 1.0.273

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 (41) hide show
  1. package/.github/workflows/publish-npm.yml +91 -0
  2. package/AGENTS.md +1 -1
  3. package/docs/technical/DefaultTable.md +70 -1
  4. package/package.json +22 -22
  5. package/src/react/components/address/DefaultAddress.js +20 -9
  6. package/src/react/components/address/DefaultAddressCoordFields.js +59 -0
  7. package/src/react/components/address/LatLonReadonlyFields.js +2 -2
  8. package/src/react/components/address/defaultAddressStyles.js +13 -1
  9. package/src/react/components/files/DefaultFile.js +2 -2
  10. package/src/react/components/files/DefaultFileColumn.js +1 -1
  11. package/src/react/components/filters/DefaultColumnFilter.js +2 -7
  12. package/src/react/components/filters/DefaultExternalFilters.js +4 -11
  13. package/src/react/components/filters/filterValue.js +65 -0
  14. package/src/react/components/help/DefaultTooltip.js +73 -63
  15. package/src/react/components/help/DefaultTooltip.styles.js +23 -23
  16. package/src/react/components/inputs/DefaultInput.js +301 -210
  17. package/src/react/components/inputs/defaultInputUtils.js +193 -170
  18. package/src/react/components/table/DefaultColumnMenu.js +127 -109
  19. package/src/react/components/table/DefaultTable.js +4 -4
  20. package/src/react/components/table/DefaultTable.styles.js +10 -4
  21. package/src/react/components/table/DefaultTable.utils.js +38 -4
  22. package/src/react/components/table/DefaultTableRows.js +324 -153
  23. package/src/react/components/upload/DefaultUpload.js +6 -36
  24. package/src/react/components/upload/DefaultUpload.styles.js +5 -5
  25. package/src/react/components/upload/DefaultUploadAttachmentsList.js +11 -3
  26. package/src/react/hooks/usePostalCodeLookup.js +15 -1
  27. package/src/react/services/addressFormUtils.js +104 -38
  28. package/src/react/services/addressGeo.js +6 -3
  29. package/src/react/utils/tableVisibleColumnsPreferences.js +84 -3
  30. package/src/tests/react/components/files/DefaultFile.test.js +16 -0
  31. package/src/tests/react/components/files/DefaultFileColumn.company.test.js +24 -0
  32. package/src/tests/react/components/help/DefaultTooltip.test.js +93 -0
  33. package/src/tests/react/components/table/DefaultTable.utils.test.js +10 -0
  34. package/src/tests/react/components/table/DefaultTableRowActions.test.js +23 -0
  35. package/src/tests/react/components/table/DefaultTableRows.rowActions.test.js +152 -0
  36. package/src/tests/react/components/upload/defaultUploadLibrary.test.js +31 -9
  37. package/src/tests/react/filters/filterValue.test.js +46 -0
  38. package/src/tests/react/hooks/usePostalCodeLookup.test.js +67 -0
  39. package/src/tests/react/inputs/defaultInputUtils.test.js +9 -1
  40. package/src/tests/react/services/addressFormUtils.test.js +6 -3
  41. package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +12 -1
@@ -1,47 +1,51 @@
1
- import React, { useEffect, useMemo, useState } from 'react';
2
- import { Image, Text, TextInput, TouchableOpacity, View } from 'react-native';
3
- import DefaultExtraData from '../extra-data/DefaultExtraData';
4
- import DefaultFileColumn from '../files/DefaultFileColumn';
5
- import Icon from 'react-native-vector-icons/Feather';
6
- import {
7
- buildReadPresentationStyles,
8
- getColumnKey,
9
- isColorColumn,
10
- isDateLikeColumn,
11
- isEditableColumn,
12
- isExtraDataColumn,
13
- isFileColumn,
14
- isValidFeatherIcon,
15
- normalizeText,
16
- resolveCellPresentation,
17
- resolveCellText,
18
- resolveEditValue,
19
- } from './defaultInputUtils';
20
- import DefaultColorInput from './DefaultColorInput';
21
- import DefaultDateInput from './DefaultDateInput';
22
- import DefaultSelect from './DefaultSelect';
23
- import styles from './DefaultInput.styles';
24
-
25
- const DefaultInput = ({
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import { Image, Text, TextInput, TouchableOpacity, View } from 'react-native';
3
+ import DefaultExtraData from '../extra-data/DefaultExtraData';
4
+ import DefaultFileColumn from '../files/DefaultFileColumn';
5
+ import Icon from 'react-native-vector-icons/Feather';
6
+ import {
7
+ buildReadPresentationStyles,
8
+ getColumnKey,
9
+ isColorColumn,
10
+ isDateLikeColumn,
11
+ isEditableColumn,
12
+ isExtraDataColumn,
13
+ isFileColumn,
14
+ isIconColumn,
15
+ isValidFeatherIcon,
16
+ normalizeText,
17
+ resolveCellPresentation,
18
+ resolveCellText,
19
+ resolveEditValue,
20
+ } from './defaultInputUtils';
21
+ import DefaultColorInput from './DefaultColorInput';
22
+ import DefaultDateInput from './DefaultDateInput';
23
+ import DefaultFeatherIconPicker, {
24
+ normalizeFeatherIconName,
25
+ } from './DefaultFeatherIconPicker';
26
+ import DefaultSelect from './DefaultSelect';
27
+ import styles from './DefaultInput.styles';
28
+
29
+ const DefaultInput = ({
26
30
  accentColor = '#2563EB',
27
31
  autoFocus = true,
28
32
  autoSave = true,
29
33
  column,
30
34
  columns = [],
31
35
  containerStyle = null,
32
- displayValue,
33
- defaultColor = '',
34
- editing = false,
36
+ displayValue,
37
+ defaultColor = '',
38
+ editing = false,
35
39
  getOptionsForColumn = null,
36
40
  inputStyle = null,
37
41
  label = '',
38
42
  numberOfLines = 1,
39
- onBeforeOpen = null,
40
- onCancelEditing = null,
41
- onChangeValue = null,
42
- onSave = null,
43
- onSearchChange = null,
44
- onStartEditing = null,
43
+ onBeforeOpen = null,
44
+ onCancelEditing = null,
45
+ onChangeValue = null,
46
+ onSave = null,
47
+ onSearchChange = null,
48
+ onStartEditing = null,
45
49
  readTextStyle = null,
46
50
  row = {},
47
51
  saving = false,
@@ -50,10 +54,10 @@ const DefaultInput = ({
50
54
  value,
51
55
  variant = 'cell',
52
56
  }) => {
53
- const fieldName = getColumnKey(column);
54
- const isExtraDataField = isExtraDataColumn(column) || ['extradata', 'extra_data'].includes(
55
- normalizeText(fieldName).replace(/[^a-zA-Z0-9_]/g, '').toLowerCase(),
56
- );
57
+ const fieldName = getColumnKey(column);
58
+ const isExtraDataField = isExtraDataColumn(column) || ['extradata', 'extra_data'].includes(
59
+ normalizeText(fieldName).replace(/[^a-zA-Z0-9_]/g, '').toLowerCase(),
60
+ );
57
61
  const isForm = variant === 'form';
58
62
  const canEdit = isEditableColumn(column) && typeof onStartEditing === 'function';
59
63
  const editValue = useMemo(
@@ -61,25 +65,25 @@ const DefaultInput = ({
61
65
  [column, row, value],
62
66
  );
63
67
  const [draftValue, setDraftValue] = useState(editValue);
64
- const resolvedLabel =
65
- displayValue ?? resolveCellText({ column, columns, row, storeName, value });
66
- const readPresentation = useMemo(
67
- () => resolveCellPresentation({ column, columns, row, storeName, value }),
68
- [column, columns, row, storeName, value],
69
- );
70
- const readPresentationStyles = useMemo(
71
- () => buildReadPresentationStyles({
72
- ...readPresentation,
73
- label: resolvedLabel,
74
- }),
75
- [readPresentation, resolvedLabel],
76
- );
68
+ const resolvedLabel =
69
+ displayValue ?? resolveCellText({ column, columns, row, storeName, value });
70
+ const readPresentation = useMemo(
71
+ () => resolveCellPresentation({ column, columns, row, storeName, value }),
72
+ [column, columns, row, storeName, value],
73
+ );
74
+ const readPresentationStyles = useMemo(
75
+ () => buildReadPresentationStyles({
76
+ ...readPresentation,
77
+ label: resolvedLabel,
78
+ }),
79
+ [readPresentation, resolvedLabel],
80
+ );
77
81
 
78
82
  useEffect(() => {
79
83
  setDraftValue(editValue);
80
84
  }, [editValue, editing]);
81
85
 
82
- if (column?.list) {
86
+ if (column?.list) {
83
87
  return (
84
88
  <DefaultSelect
85
89
  accentColor={accentColor}
@@ -106,63 +110,150 @@ const DefaultInput = ({
106
110
  variant={variant}
107
111
  />
108
112
  );
109
- }
110
-
111
- if (isColorColumn(column)) {
112
- return (
113
- <DefaultColorInput
114
- accentColor={accentColor}
115
- autoFocus={autoFocus}
116
- autoSave={autoSave}
117
- column={column}
118
- containerStyle={containerStyle}
119
- defaultColor={defaultColor}
120
- editing={editing}
121
- inputStyle={inputStyle}
122
- label={label}
123
- onCancelEditing={onCancelEditing}
124
- onChangeValue={onChangeValue}
125
- onSave={onSave}
126
- onStartEditing={onStartEditing}
127
- row={row}
128
- saving={saving}
129
- showLabel={showLabel}
130
- storeName={storeName}
131
- value={value}
132
- variant={variant}
133
- />
134
- );
135
- }
136
-
137
- if (isDateLikeColumn(column)) {
138
- return (
139
- <DefaultDateInput
140
- accentColor={accentColor}
141
- autoFocus={autoFocus}
142
- autoSave={autoSave}
143
- column={column}
144
- columns={columns}
145
- containerStyle={containerStyle}
146
- displayValue={displayValue}
147
- editing={editing}
148
- inputStyle={inputStyle}
149
- label={label}
150
- numberOfLines={numberOfLines}
151
- onCancelEditing={onCancelEditing}
152
- onChangeValue={onChangeValue}
153
- onSave={onSave}
154
- onSearchChange={onSearchChange}
155
- onStartEditing={onStartEditing}
156
- readTextStyle={readTextStyle}
157
- row={row}
158
- saving={saving}
159
- showLabel={showLabel}
160
- storeName={storeName}
161
- value={value}
162
- variant={variant}
163
- />
164
- );
165
- }
113
+ }
114
+
115
+ if (isColorColumn(column)) {
116
+ return (
117
+ <DefaultColorInput
118
+ accentColor={accentColor}
119
+ autoFocus={autoFocus}
120
+ autoSave={autoSave}
121
+ column={column}
122
+ containerStyle={containerStyle}
123
+ defaultColor={defaultColor}
124
+ editing={editing}
125
+ inputStyle={inputStyle}
126
+ label={label}
127
+ onCancelEditing={onCancelEditing}
128
+ onChangeValue={onChangeValue}
129
+ onSave={onSave}
130
+ onStartEditing={onStartEditing}
131
+ row={row}
132
+ saving={saving}
133
+ showLabel={showLabel}
134
+ storeName={storeName}
135
+ value={value}
136
+ variant={variant}
137
+ />
138
+ );
139
+ }
140
+
141
+ if (isIconColumn(column)) {
142
+ const fieldNameIcon = getColumnKey(column);
143
+ const rawIconValue = value ?? row?.[fieldNameIcon];
144
+ const selectedIcon = normalizeFeatherIconName(rawIconValue);
145
+ const isFormIcon = variant === 'form';
146
+ const canEditIcon = isEditableColumn(column) && typeof onStartEditing === 'function';
147
+
148
+ const commitIcon = nextIcon => {
149
+ const normalized = normalizeFeatherIconName(nextIcon);
150
+ if (autoSave) {
151
+ onSave?.(normalized);
152
+ return;
153
+ }
154
+ onChangeValue?.(normalized);
155
+ };
156
+
157
+ if (!editing && !isFormIcon) {
158
+ return (
159
+ <View style={[styles.wrap, containerStyle]}>
160
+ {showLabel ? (
161
+ <Text style={styles.fieldLabel}>{label || column?.label || fieldNameIcon}</Text>
162
+ ) : null}
163
+ <TouchableOpacity
164
+ style={styles.readButton}
165
+ activeOpacity={canEditIcon ? 0.78 : 1}
166
+ disabled={!canEditIcon}
167
+ onPress={() => onStartEditing?.()}
168
+ >
169
+ <View style={styles.readValueWrap}>
170
+ <Icon
171
+ name={selectedIcon || 'search'}
172
+ size={14}
173
+ color={selectedIcon ? '#0F172A' : '#94A3B8'}
174
+ />
175
+ <Text
176
+ style={[
177
+ styles.readText,
178
+ !selectedIcon ? styles.mutedText : null,
179
+ readTextStyle,
180
+ ]}
181
+ numberOfLines={numberOfLines}
182
+ >
183
+ {selectedIcon || '-'}
184
+ </Text>
185
+ </View>
186
+ {saving ? (
187
+ <Text style={[styles.savingText, { color: accentColor }]}>Salvando</Text>
188
+ ) : canEditIcon ? (
189
+ <Icon style={styles.editIcon} name="edit-2" size={13} color="#64748B" />
190
+ ) : null}
191
+ </TouchableOpacity>
192
+ </View>
193
+ );
194
+ }
195
+
196
+ return (
197
+ <View style={[styles.wrap, containerStyle]}>
198
+ {showLabel ? (
199
+ <Text style={styles.fieldLabel}>{label || column?.label || fieldNameIcon}</Text>
200
+ ) : null}
201
+ <View style={styles.editingWrap}>
202
+ <View style={styles.editingRow}>
203
+ <DefaultFeatherIconPicker
204
+ value={selectedIcon}
205
+ onChange={commitIcon}
206
+ placeholder={
207
+ global.t?.t(storeName, 'input', column?.label || fieldNameIcon) ||
208
+ 'Buscar icone'
209
+ }
210
+ style={[isFormIcon ? styles.formInput : null, inputStyle, { flex: 1 }]}
211
+ />
212
+ {!isFormIcon ? (
213
+ <TouchableOpacity
214
+ style={styles.cancelButton}
215
+ activeOpacity={0.8}
216
+ onPress={onCancelEditing}
217
+ >
218
+ <Icon name="x" size={14} color="#64748B" />
219
+ </TouchableOpacity>
220
+ ) : null}
221
+ </View>
222
+ {saving ? <Text style={styles.savingText}>Salvando</Text> : null}
223
+ </View>
224
+ </View>
225
+ );
226
+ }
227
+
228
+ if (isDateLikeColumn(column)) {
229
+ return (
230
+ <DefaultDateInput
231
+ accentColor={accentColor}
232
+ autoFocus={autoFocus}
233
+ autoSave={autoSave}
234
+ column={column}
235
+ columns={columns}
236
+ containerStyle={containerStyle}
237
+ displayValue={displayValue}
238
+ editing={editing}
239
+ inputStyle={inputStyle}
240
+ label={label}
241
+ numberOfLines={numberOfLines}
242
+ onCancelEditing={onCancelEditing}
243
+ onChangeValue={onChangeValue}
244
+ onSave={onSave}
245
+ onSearchChange={onSearchChange}
246
+ onStartEditing={onStartEditing}
247
+ readTextStyle={readTextStyle}
248
+ row={row}
249
+ saving={saving}
250
+ showLabel={showLabel}
251
+ storeName={storeName}
252
+ value={value}
253
+ variant={variant}
254
+ />
255
+ );
256
+ }
166
257
 
167
258
  const saveDraft = () => {
168
259
  if (autoSave) {
@@ -178,109 +269,109 @@ const DefaultInput = ({
178
269
  if (!autoSave) onChangeValue?.(nextValue);
179
270
  };
180
271
 
181
- if (!editing && !isForm) {
182
- if (isExtraDataField) {
183
- return (
184
- <View style={[styles.wrap, containerStyle]}>
185
- {showLabel ? <Text style={styles.fieldLabel}>{label || column?.label || fieldName}</Text> : null}
186
- <DefaultExtraData
187
- column={column}
188
- row={row}
189
- value={value ?? row?.[fieldName]}
190
- variant={variant}
191
- />
192
- </View>
193
- );
194
- }
195
-
196
- if (isFileColumn(column)) {
197
- return (
198
- <View style={[styles.wrap, containerStyle]}>
199
- {showLabel ? <Text style={styles.fieldLabel}>{label || column?.label || fieldName}</Text> : null}
200
- <DefaultFileColumn
201
- column={column}
202
- row={row}
203
- value={value}
204
- variant={variant}
205
- />
206
- </View>
207
- );
208
- }
209
-
210
- const readIconName = isValidFeatherIcon(readPresentation.icon)
211
- ? readPresentation.icon
212
- : '';
213
- const shouldRenderIconFallbackText = Boolean(
214
- readPresentationStyles.hasDecoration &&
215
- !readIconName &&
216
- readPresentationStyles.color &&
217
- normalizeText(resolvedLabel) &&
218
- resolvedLabel !== '-',
219
- );
220
-
221
- return (
222
- <View style={[styles.wrap, containerStyle]}>
223
- {showLabel ? <Text style={styles.fieldLabel}>{label || column?.label || fieldName}</Text> : null}
224
- <TouchableOpacity
272
+ if (!editing && !isForm) {
273
+ if (isExtraDataField) {
274
+ return (
275
+ <View style={[styles.wrap, containerStyle]}>
276
+ {showLabel ? <Text style={styles.fieldLabel}>{label || column?.label || fieldName}</Text> : null}
277
+ <DefaultExtraData
278
+ column={column}
279
+ row={row}
280
+ value={value ?? row?.[fieldName]}
281
+ variant={variant}
282
+ />
283
+ </View>
284
+ );
285
+ }
286
+
287
+ if (isFileColumn(column)) {
288
+ return (
289
+ <View style={[styles.wrap, containerStyle]}>
290
+ {showLabel ? <Text style={styles.fieldLabel}>{label || column?.label || fieldName}</Text> : null}
291
+ <DefaultFileColumn
292
+ column={column}
293
+ row={row}
294
+ value={value}
295
+ variant={variant}
296
+ />
297
+ </View>
298
+ );
299
+ }
300
+
301
+ const readIconName = isValidFeatherIcon(readPresentation.icon)
302
+ ? readPresentation.icon
303
+ : '';
304
+ const shouldRenderIconFallbackText = Boolean(
305
+ readPresentationStyles.hasDecoration &&
306
+ !readIconName &&
307
+ readPresentationStyles.color &&
308
+ normalizeText(resolvedLabel) &&
309
+ resolvedLabel !== '-',
310
+ );
311
+
312
+ return (
313
+ <View style={[styles.wrap, containerStyle]}>
314
+ {showLabel ? <Text style={styles.fieldLabel}>{label || column?.label || fieldName}</Text> : null}
315
+ <TouchableOpacity
225
316
  style={styles.readButton}
226
317
  activeOpacity={canEdit ? 0.78 : 1}
227
- disabled={!canEdit}
228
- onPress={() => onStartEditing?.()}
229
- >
230
- {shouldRenderIconFallbackText ? (
231
- <Text
232
- style={[
233
- styles.readText,
234
- styles.readTextBadge,
235
- readPresentationStyles.color ? { color: readPresentationStyles.color } : null,
236
- readTextStyle,
237
- ]}
238
- numberOfLines={numberOfLines}
239
- >
240
- {resolvedLabel}
241
- </Text>
242
- ) : (
243
- <View
244
- style={[
245
- styles.readValueWrap,
246
- readPresentationStyles.hasDecoration ? styles.readBadge : null,
247
- readPresentationStyles.badgeStyle,
248
- ]}
249
- >
250
- {readPresentation.image ? (
251
- <Image
252
- source={readPresentation.image}
253
- style={styles.readImage}
254
- resizeMode="contain"
255
- />
256
- ) : null}
257
- {readPresentationStyles.hasDecoration && readIconName && readPresentationStyles.color ? (
258
- <Icon
259
- style={styles.readBadgeIcon}
260
- name={readIconName}
261
- size={12}
262
- color={readPresentationStyles.color}
263
- />
264
- ) : null}
265
- <Text
266
- style={[
267
- styles.readText,
268
- readPresentationStyles.hasDecoration ? styles.readBadgeText : null,
269
- resolvedLabel === '-' ? styles.mutedText : null,
270
- readPresentationStyles.color ? { color: readPresentationStyles.color } : null,
271
- readTextStyle,
272
- ]}
273
- numberOfLines={numberOfLines}
274
- >
275
- {column?.prefix || ''}
276
- {resolvedLabel || '-'}
277
- {column?.sufix || column?.suffix || ''}
278
- </Text>
279
- </View>
280
- )}
281
- {saving ? (
282
- <Text style={[styles.savingText, { color: accentColor }]}>Salvando</Text>
283
- ) : canEdit ? (
318
+ disabled={!canEdit}
319
+ onPress={() => onStartEditing?.()}
320
+ >
321
+ {shouldRenderIconFallbackText ? (
322
+ <Text
323
+ style={[
324
+ styles.readText,
325
+ styles.readTextBadge,
326
+ readPresentationStyles.color ? { color: readPresentationStyles.color } : null,
327
+ readTextStyle,
328
+ ]}
329
+ numberOfLines={numberOfLines}
330
+ >
331
+ {resolvedLabel}
332
+ </Text>
333
+ ) : (
334
+ <View
335
+ style={[
336
+ styles.readValueWrap,
337
+ readPresentationStyles.hasDecoration ? styles.readBadge : null,
338
+ readPresentationStyles.badgeStyle,
339
+ ]}
340
+ >
341
+ {readPresentation.image ? (
342
+ <Image
343
+ source={readPresentation.image}
344
+ style={styles.readImage}
345
+ resizeMode="contain"
346
+ />
347
+ ) : null}
348
+ {readPresentationStyles.hasDecoration && readIconName && readPresentationStyles.color ? (
349
+ <Icon
350
+ style={styles.readBadgeIcon}
351
+ name={readIconName}
352
+ size={12}
353
+ color={readPresentationStyles.color}
354
+ />
355
+ ) : null}
356
+ <Text
357
+ style={[
358
+ styles.readText,
359
+ readPresentationStyles.hasDecoration ? styles.readBadgeText : null,
360
+ resolvedLabel === '-' ? styles.mutedText : null,
361
+ readPresentationStyles.color ? { color: readPresentationStyles.color } : null,
362
+ readTextStyle,
363
+ ]}
364
+ numberOfLines={numberOfLines}
365
+ >
366
+ {column?.prefix || ''}
367
+ {resolvedLabel || '-'}
368
+ {column?.sufix || column?.suffix || ''}
369
+ </Text>
370
+ </View>
371
+ )}
372
+ {saving ? (
373
+ <Text style={[styles.savingText, { color: accentColor }]}>Salvando</Text>
374
+ ) : canEdit ? (
284
375
  <Icon style={styles.editIcon} name="edit-2" size={13} color="#64748B" />
285
376
  ) : null}
286
377
  </TouchableOpacity>