@controleonline/ui-default 1.0.270 → 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 (100) hide show
  1. package/.github/workflows/integration-source-gate.yml +68 -0
  2. package/.github/workflows/manager-worker.yml +19 -0
  3. package/.github/workflows/publish-npm.yml +91 -0
  4. package/AGENTS.md +46 -50
  5. package/docs/technical/Contrato-de-colunas.md +67 -0
  6. package/docs/technical/Default-Components.md +76 -0
  7. package/docs/technical/DefaultForm.md +80 -0
  8. package/docs/technical/DefaultInput-e-DefaultSelect.md +73 -0
  9. package/docs/technical/DefaultTable.md +272 -0
  10. package/docs/wiki.md +1 -0
  11. package/package.json +4 -3
  12. package/scripts/validate-wiki-pointer.cjs +13 -0
  13. package/src/react/components/address/DefaultAddress.js +446 -0
  14. package/src/react/components/address/DefaultAddress.styles.js +133 -0
  15. package/src/react/components/address/DefaultAddressCoordFields.js +59 -0
  16. package/src/react/components/address/DefaultAddressMapPane.js +55 -0
  17. package/src/react/components/address/LatLonReadonlyFields.js +51 -0
  18. package/src/react/components/address/defaultAddressHelpers.js +163 -0
  19. package/src/react/components/address/defaultAddressStyles.js +148 -0
  20. package/src/react/components/address/defaultAddressUtils.js +129 -0
  21. package/src/react/components/extra-data/DefaultExtraData.js +152 -0
  22. package/src/react/components/extra-data/DefaultExtraData.styles.js +61 -0
  23. package/src/react/components/files/DefaultFile.js +231 -46
  24. package/src/react/components/files/DefaultFileColumn.js +123 -0
  25. package/src/react/components/files/DefaultFileColumn.styles.js +52 -0
  26. package/src/react/components/files/DefaultFileColumn.utils.js +104 -0
  27. package/src/react/components/filters/CompactFilterSelector.js +51 -35
  28. package/src/react/components/filters/DefaultColumnFilter.js +60 -48
  29. package/src/react/components/filters/DefaultExternalFilters.js +324 -265
  30. package/src/react/components/filters/filterValue.js +65 -0
  31. package/src/react/components/form/DefaultForm.js +99 -79
  32. package/src/react/components/help/DefaultTooltip.js +48 -12
  33. package/src/react/components/help/DefaultTooltip.styles.js +20 -0
  34. package/src/react/components/inputs/DefaultColorInput.js +153 -0
  35. package/src/react/components/inputs/DefaultFeatherIconPicker.js +146 -0
  36. package/src/react/components/inputs/DefaultFeatherIconPicker.styles.js +113 -0
  37. package/src/react/components/inputs/DefaultInput.js +296 -141
  38. package/src/react/components/inputs/DefaultInput.styles.js +26 -0
  39. package/src/react/components/inputs/DefaultSelect.js +134 -123
  40. package/src/react/components/inputs/defaultInputUtils.js +262 -213
  41. package/src/react/components/map/DefaultMap.native.js +73 -73
  42. package/src/react/components/map/DefaultMap.shared.js +911 -762
  43. package/src/react/components/map/DefaultMap.web.js +124 -68
  44. package/src/react/components/table/DefaultColumnMenu.js +22 -3
  45. package/src/react/components/table/DefaultDebug.js +44 -37
  46. package/src/react/components/table/DefaultSearchModal.js +9 -1
  47. package/src/react/components/table/DefaultTable.grouping.js +98 -0
  48. package/src/react/components/table/DefaultTable.js +217 -111
  49. package/src/react/components/table/DefaultTable.styles.js +369 -310
  50. package/src/react/components/table/DefaultTable.utils.js +38 -4
  51. package/src/react/components/table/DefaultTableBody.js +13 -8
  52. package/src/react/components/table/DefaultTableCards.js +20 -1
  53. package/src/react/components/table/DefaultTableControls.js +180 -205
  54. package/src/react/components/table/DefaultTableImportModal.js +108 -0
  55. package/src/react/components/table/DefaultTableInput.js +1 -0
  56. package/src/react/components/table/DefaultTableRowActions.js +1 -1
  57. package/src/react/components/table/DefaultTableRows.js +357 -325
  58. package/src/react/components/table/DefaultTableSearch.js +28 -5
  59. package/src/react/components/table/DefaultTableToolbar.js +92 -3
  60. package/src/react/components/table/useDefaultTablePagination.js +56 -7
  61. package/src/react/components/table/useDefaultTableStoreSync.js +164 -0
  62. package/src/react/components/upload/DefaultUpload.js +406 -0
  63. package/src/react/components/upload/DefaultUpload.styles.js +153 -0
  64. package/src/react/components/upload/DefaultUploadAttachmentsList.js +146 -0
  65. package/src/react/components/upload/DefaultUploadManagerModal.js +247 -0
  66. package/src/react/components/upload/defaultUploadHelpers.js +108 -0
  67. package/src/react/components/upload/defaultUploadLibrary.js +248 -0
  68. package/src/react/components/upload/fileUpload.js +93 -0
  69. package/src/react/hooks/usePostalCodeLookup.js +159 -0
  70. package/src/react/index.js +6 -0
  71. package/src/react/services/addressFormUtils.js +262 -0
  72. package/src/react/services/addressGeo.js +230 -0
  73. package/src/react/utils/tableVisibleColumnsPreferences.js +113 -15
  74. package/src/tests/react/components/address/defaultAddressHelpers.test.js +104 -0
  75. package/src/tests/react/components/files/DefaultFile.test.js +22 -6
  76. package/src/tests/react/components/files/DefaultFileColumn.company.test.js +24 -0
  77. package/src/tests/react/components/form/DefaultForm.test.js +123 -0
  78. package/src/tests/react/components/help/DefaultTooltip.test.js +93 -0
  79. package/src/tests/react/components/inputs/DefaultSelect.test.js +108 -79
  80. package/src/tests/react/components/table/DefaultTable.grouping.test.js +49 -0
  81. package/src/tests/react/components/table/DefaultTable.test.js +53 -2
  82. package/src/tests/react/components/table/DefaultTable.utils.test.js +10 -0
  83. package/src/tests/react/components/table/DefaultTableControls.filters.test.mjs +21 -0
  84. package/src/tests/react/components/table/DefaultTableRowActions.test.js +23 -0
  85. package/src/tests/react/components/table/DefaultTableRows.rowActions.test.js +152 -0
  86. package/src/tests/react/components/table/resolveRowBackgroundColor.test.mjs +45 -0
  87. package/src/tests/react/components/table/useDefaultTableStoreSync.test.js +161 -0
  88. package/src/tests/react/components/upload/DefaultUpload.test.js +353 -0
  89. package/src/tests/react/components/upload/defaultUploadLibrary.test.js +104 -0
  90. package/src/tests/react/components/upload/peopleMediaLibraryPreview.test.mjs +57 -0
  91. package/src/tests/react/filters/filterValue.test.js +46 -0
  92. package/src/tests/react/hooks/usePostalCodeLookup.test.js +107 -0
  93. package/src/tests/react/inputs/defaultInputUtils.test.js +9 -1
  94. package/src/tests/react/services/SMOKE-637-cep-hydrate.md +45 -0
  95. package/src/tests/react/services/addressFormUtils.test.js +179 -0
  96. package/src/tests/react/services/addressGeoLookup.async.test.js +104 -0
  97. package/src/tests/react/services/addressGeoLookup.test.js +105 -0
  98. package/src/tests/react/services/smoke-637/form-cep-12941040.png +0 -0
  99. package/src/tests/react/services/smoke-637/index.html +132 -0
  100. package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +96 -49
@@ -1,188 +1,215 @@
1
- import React, { useMemo, useState } from 'react';
2
- import { FlatList, ScrollView, Text, TouchableOpacity, View } from 'react-native';
3
- import Icon from 'react-native-vector-icons/Feather';
4
- import { useStore } from '@store';
5
- import { formatStoreColumnLabel } from '@controleonline/ui-common/src/react/utils/storeColumns';
6
- import { getColumnKey } from '../inputs/defaultInputUtils';
7
- import DefaultColumnFilter from '../filters/DefaultColumnFilter';
1
+ import React, { useMemo, useState } from 'react';
2
+ import { FlatList, ScrollView, Text, TouchableOpacity, View } from 'react-native';
3
+ import Icon from 'react-native-vector-icons/Feather';
4
+ import { useStore } from '@store';
5
+ import { formatStoreColumnLabel } from '@controleonline/ui-common/src/react/utils/storeColumns';
6
+ import { getColumnKey } from '../inputs/defaultInputUtils';
7
+ import DefaultColumnFilter from '../filters/DefaultColumnFilter';
8
8
  import DefaultTableEmptyState from './DefaultTableEmptyState';
9
9
  import DefaultTableInput from './DefaultTableInput';
10
10
  import DefaultTableRowActions, {
11
11
  hasDefaultTableRowActionsComponent,
12
12
  resolveDefaultTableRowActionsWidth,
13
13
  } from './DefaultTableRowActions';
14
- import {
15
- END_REACHED_THRESHOLD,
16
- getColumnStyle,
17
- getRowKey,
18
- getSortField,
19
- isSortableColumn,
20
- mergeSortedDataWithLiveItems,
21
- shouldTriggerEndReachedFromScroll,
22
- shouldIncludeColumn,
23
- } from './DefaultTable.utils';
24
- import styles from './DefaultTable.styles';
25
- import useDefaultTableTheme from './useDefaultTableTheme';
26
-
27
- const DefaultTableRows = ({ storeName }) => {
28
- const store = useStore(storeName);
29
- const configs = store?.getters?.configs || {};
14
+ import {
15
+ END_REACHED_THRESHOLD,
16
+ getColumnStyle,
17
+ getColumnMinWidth,
18
+ getRowKey,
19
+ getSortField,
20
+ isSortableColumn,
21
+ mergeSortedDataWithLiveItems,
22
+ shouldTriggerEndReachedFromScroll,
23
+ shouldIncludeColumn,
24
+ } from './DefaultTable.utils';
25
+ import styles from './DefaultTable.styles';
26
+ import useDefaultTableTheme from './useDefaultTableTheme';
27
+
28
+ /** Row stripes: themes-map listItemEvenRow / listItemOddRow — never status.color. */
29
+ export const resolveRowBackgroundColor = ({ index, tableOddColor, tableEvenColor }) => {
30
+ return index % 2 === 0 ? tableOddColor : tableEvenColor;
31
+ };
32
+
33
+ const DefaultTableRows = ({ storeName }) => {
34
+ const store = useStore(storeName);
35
+ const configs = store?.getters?.configs || {};
30
36
  const { palette, resolvedAccentColor, tableBorderColors, themeTokens } = useDefaultTableTheme();
31
- const columns = Array.isArray(store?.getters?.columns) ? store.getters.columns : [];
32
- const visibleColumns = store?.getters?.visibleColumns || {};
33
- const tableColumns = columns.filter(
34
- column => shouldIncludeColumn(column) && visibleColumns[getColumnKey(column)] !== false,
35
- );
36
- const sortedData = mergeSortedDataWithLiveItems({
37
- liveItems: store?.getters?.items,
38
- sortedData: configs.sortedData,
39
- });
40
- const resolvedFilters = store?.getters?.filters || {};
41
- const isLoading = Boolean(store?.getters?.isLoadingList || store?.getters?.isLoading);
42
- const emptyStateLabel = isLoading
43
- ? global.t?.t(storeName, 'label', 'loading')
44
- : global.t?.t(storeName, 'label', 'empty');
37
+ const columns = Array.isArray(store?.getters?.columns) ? store.getters.columns : [];
38
+ const visibleColumns = store?.getters?.visibleColumns || {};
39
+ const tableColumns = columns.filter(
40
+ column => shouldIncludeColumn(column) && visibleColumns[getColumnKey(column)] !== false,
41
+ );
42
+ const sortedData = mergeSortedDataWithLiveItems({
43
+ liveItems: store?.getters?.items,
44
+ sortedData: configs.sortedData,
45
+ });
46
+ const resolvedFilters = store?.getters?.filters || {};
47
+ const isLoading = Boolean(store?.getters?.isLoadingList || store?.getters?.isLoading);
48
+ const emptyStateLabel = isLoading
49
+ ? global.t?.t(storeName, 'label', 'loading')
50
+ : global.t?.t(storeName, 'label', 'empty');
45
51
  const tableBorderColor = tableBorderColors.rowBorderColor;
46
52
  const tableHeaderBorderColor = tableBorderColors.headerBorderColor || tableBorderColor;
47
- const tableEvenColor = themeTokens.listItemEvenRow || themeTokens['bg-even-light'] || palette.background;
48
- const tableHeaderColor = themeTokens['bg-headers-light'] || resolvedAccentColor;
49
- const tableMutedColor = palette.textSecondary;
50
- const tableOddColor = themeTokens.listItemOddRow || themeTokens['bg-odd-light'] || palette.background;
51
- const tableSurfaceColor = palette.background;
52
- const tableTextColor = palette.text;
53
- const rowStyle = configs.rowStyle;
54
- const hasRowPress = typeof configs.onRowPress === 'function';
53
+ const tableEvenColor = themeTokens.listItemEvenRow || themeTokens['bg-even-light'] || palette.background;
54
+ const tableHeaderColor = themeTokens['bg-headers-light'] || resolvedAccentColor;
55
+ const tableMutedColor = palette.textSecondary;
56
+ const tableOddColor = themeTokens.listItemOddRow || themeTokens['bg-odd-light'] || palette.background;
57
+ const tableSurfaceColor = palette.background;
58
+ const tableTextColor = palette.text;
59
+ const rowStyle = configs.rowStyle;
60
+ const hasRowPress = typeof configs.onRowPress === 'function';
55
61
  const hasCustomRowActions = hasDefaultTableRowActionsComponent(configs.rowActionsComponent);
56
62
  const hasEditAction = typeof configs.onEditRow === 'function';
57
63
  const hasRowActions = configs.showRowActions !== false && (hasCustomRowActions || hasEditAction);
58
- const shouldPinRowActions = configs.pinRowActions !== false;
59
- const hasColumnFilters =
60
- configs.showColumnFiltersButton !== false &&
61
- configs.tableFiltersVisible === true;
64
+ const shouldPinRowActions = configs.pinRowActions !== false;
65
+ const hasColumnFilters =
66
+ configs.showColumnFiltersButton !== false &&
67
+ configs.tableFiltersVisible === true;
62
68
  const actionsCellWidth = hasRowActions ? resolveDefaultTableRowActionsWidth(configs) : 0;
63
- const [horizontalMetrics, setHorizontalMetrics] = useState({
64
- contentWidth: 0,
65
- viewportWidth: 0,
66
- x: 0,
67
- });
68
- const updateHorizontalMetrics = patch => {
69
- setHorizontalMetrics(current => {
70
- const next = {
71
- ...current,
72
- ...patch,
73
- };
74
-
75
- return next.contentWidth === current.contentWidth &&
76
- next.viewportWidth === current.viewportWidth &&
77
- next.x === current.x
78
- ? current
79
- : next;
80
- });
81
- };
82
- const stickyBodyTransforms = useMemo(() => {
83
- const viewportWidth = Number(horizontalMetrics.viewportWidth || 0);
84
- const contentWidth = Number(horizontalMetrics.contentWidth || 0);
85
- const x = Number(horizontalMetrics.x || 0);
86
- const canScrollHorizontally = viewportWidth > 0 && contentWidth > viewportWidth;
87
- const actionsTranslateX = canScrollHorizontally
88
- ? x + viewportWidth - contentWidth
89
- : 0;
90
-
91
- return {
92
- actions:
93
- shouldPinRowActions && actionsTranslateX !== 0
94
- ? [{ translateX: actionsTranslateX }]
95
- : null,
96
- identity: x !== 0 ? [{ translateX: x }] : null,
97
- };
98
- }, [horizontalMetrics, shouldPinRowActions]);
99
- const handleHorizontalScroll = event => {
100
- updateHorizontalMetrics({
101
- x: Number(event?.nativeEvent?.contentOffset?.x || 0),
102
- });
103
- };
104
- const handleHorizontalLayout = event => {
105
- updateHorizontalMetrics({
106
- viewportWidth: Number(event?.nativeEvent?.layout?.width || 0),
107
- });
108
- };
109
- const handleHorizontalContentSizeChange = width => {
110
- updateHorizontalMetrics({
111
- contentWidth: Number(width || 0),
112
- });
113
- };
114
- const handleListScroll = event => {
115
- if (shouldTriggerEndReachedFromScroll(event)) {
116
- configs.onEndReached?.();
117
- }
118
- };
119
-
120
- const renderTableItem = ({ item: row, index }) => {
121
- const RowComponent = hasRowPress ? TouchableOpacity : View;
122
- const rowStyleValue = typeof rowStyle === 'function' ? rowStyle(row, index) : rowStyle;
123
- const rowPressProps = hasRowPress
124
- ? {
125
- activeOpacity: 0.84,
126
- onPress: () => configs.onRowPress(row),
127
- }
128
- : {};
129
- const rowBackgroundColor = index % 2 === 0 ? tableOddColor : tableEvenColor;
130
- const RowActionsComponent = configs.rowActionsComponent;
131
-
132
- return (
133
- <RowComponent
134
- key={getRowKey(row)}
135
- style={[
69
+ const tableMinWidth = useMemo(
70
+ () =>
71
+ tableColumns.reduce(
72
+ (totalWidth, column) => totalWidth + getColumnMinWidth(column),
73
+ actionsCellWidth,
74
+ ),
75
+ [actionsCellWidth, tableColumns],
76
+ );
77
+ const [horizontalMetrics, setHorizontalMetrics] = useState({
78
+ contentWidth: 0,
79
+ viewportWidth: 0,
80
+ x: 0,
81
+ });
82
+ const updateHorizontalMetrics = patch => {
83
+ setHorizontalMetrics(current => {
84
+ const next = {
85
+ ...current,
86
+ ...patch,
87
+ };
88
+
89
+ return next.contentWidth === current.contentWidth &&
90
+ next.viewportWidth === current.viewportWidth &&
91
+ next.x === current.x
92
+ ? current
93
+ : next;
94
+ });
95
+ };
96
+ const stickyBodyTransforms = useMemo(() => {
97
+ const viewportWidth = Number(horizontalMetrics.viewportWidth || 0);
98
+ const contentWidth = Number(horizontalMetrics.contentWidth || 0);
99
+ const x = Number(horizontalMetrics.x || 0);
100
+ const canScrollHorizontally = viewportWidth > 0 && contentWidth > viewportWidth;
101
+ const actionsTranslateX = canScrollHorizontally
102
+ ? x + viewportWidth - contentWidth
103
+ : 0;
104
+
105
+ return {
106
+ actions:
107
+ shouldPinRowActions && actionsTranslateX !== 0
108
+ ? [{ translateX: actionsTranslateX }]
109
+ : null,
110
+ };
111
+ }, [horizontalMetrics, shouldPinRowActions]);
112
+ const handleHorizontalScroll = event => {
113
+ updateHorizontalMetrics({
114
+ x: Number(event?.nativeEvent?.contentOffset?.x || 0),
115
+ });
116
+ };
117
+ const handleHorizontalLayout = event => {
118
+ updateHorizontalMetrics({
119
+ viewportWidth: Number(event?.nativeEvent?.layout?.width || 0),
120
+ });
121
+ };
122
+ const handleHorizontalContentSizeChange = width => {
123
+ updateHorizontalMetrics({
124
+ contentWidth: Number(width || 0),
125
+ });
126
+ };
127
+ const handleListScroll = event => {
128
+ if (shouldTriggerEndReachedFromScroll(event)) {
129
+ configs.onEndReached?.();
130
+ }
131
+ };
132
+ const tableWidth = Math.max(
133
+ tableMinWidth,
134
+ Number(horizontalMetrics.viewportWidth || 0),
135
+ );
136
+
137
+ const renderTableItem = ({ item: row, index }) => {
138
+ const RowComponent = hasRowPress ? TouchableOpacity : View;
139
+ const rowStyleValue = typeof rowStyle === 'function' ? rowStyle(row, index) : rowStyle;
140
+ const rowPressProps = hasRowPress
141
+ ? {
142
+ activeOpacity: 0.84,
143
+ onPress: () => configs.onRowPress(row),
144
+ }
145
+ : {};
146
+ const rowBackgroundColor = resolveRowBackgroundColor({
147
+ index,
148
+ tableOddColor,
149
+ tableEvenColor,
150
+ });
151
+ const RowActionsComponent = configs.rowActionsComponent;
152
+
153
+ return (
154
+ <RowComponent
155
+ key={getRowKey(row)}
156
+ style={[
136
157
  styles.row,
137
158
  {
138
159
  backgroundColor: rowBackgroundColor,
139
160
  borderBottomColor: tableBorderColor,
140
161
  borderBottomWidth: tableBorderColor ? 1 : 0,
162
+ minWidth: tableWidth,
163
+ width: tableWidth,
141
164
  },
142
- rowStyleValue,
143
- ]}
144
- {...rowPressProps}
145
- >
146
- {tableColumns.map(column => (
147
- <React.Fragment key={getColumnKey(column)}>
148
- <DefaultTableInput
149
- column={column}
150
- options={column?.isIdentity ? {
151
- cellStyle: [
152
- styles.pinnedIdentityCell,
153
- {
154
- backgroundColor: rowBackgroundColor,
155
- ...(stickyBodyTransforms.identity
156
- ? { transform: stickyBodyTransforms.identity }
157
- : {}),
158
- },
159
- ],
160
- } : {}}
161
- row={row}
162
- storeName={storeName}
163
- variant="cell"
164
- />
165
- </React.Fragment>
166
- ))}
167
- {hasRowActions ? (
168
- <View
169
- style={[
170
- styles.cell,
171
- styles.actionsCell,
172
- shouldPinRowActions ? styles.pinnedActionsCell : null,
173
- {
174
- minWidth: actionsCellWidth,
175
- width: actionsCellWidth,
176
- flexBasis: actionsCellWidth,
177
- maxWidth: actionsCellWidth,
178
- backgroundColor: rowBackgroundColor,
179
- ...(stickyBodyTransforms.actions
180
- ? { transform: stickyBodyTransforms.actions }
181
- : {}),
182
- },
183
- ]}
184
- >
185
- <View style={styles.rowActionsGroup}>
165
+ rowStyleValue,
166
+ ]}
167
+ {...rowPressProps}
168
+ >
169
+ {tableColumns.map(column => (
170
+ <React.Fragment key={getColumnKey(column)}>
171
+ <DefaultTableInput
172
+ column={column}
173
+ options={column?.isIdentity ? {
174
+ cellStyle: [
175
+ styles.pinnedIdentityCell,
176
+ styles.stickyIdentityCell,
177
+ {
178
+ backgroundColor: rowBackgroundColor,
179
+ },
180
+ ],
181
+ } : {
182
+ cellStyle: {
183
+ backgroundColor: rowBackgroundColor,
184
+ },
185
+ }}
186
+ row={row}
187
+ storeName={storeName}
188
+ variant="cell"
189
+ />
190
+ </React.Fragment>
191
+ ))}
192
+ {hasRowActions ? (
193
+ <View
194
+ testID={`default-table-row-actions-${getRowKey(row)}`}
195
+ style={[
196
+ styles.cell,
197
+ styles.actionsCell,
198
+ shouldPinRowActions ? styles.pinnedActionsCell : null,
199
+ shouldPinRowActions ? styles.stickyActionsCell : null,
200
+ {
201
+ minWidth: actionsCellWidth,
202
+ width: actionsCellWidth,
203
+ flexBasis: actionsCellWidth,
204
+ maxWidth: actionsCellWidth,
205
+ ...(stickyBodyTransforms.actions
206
+ ? { transform: stickyBodyTransforms.actions }
207
+ : {}),
208
+ backgroundColor: rowBackgroundColor,
209
+ },
210
+ ]}
211
+ >
212
+ <View style={styles.rowActionsGroup}>
186
213
  {hasCustomRowActions ? (
187
214
  <DefaultTableRowActions
188
215
  component={RowActionsComponent}
@@ -196,34 +223,34 @@ const DefaultTableRows = ({ storeName }) => {
196
223
  storeName={storeName}
197
224
  />
198
225
  ) : null}
199
- {hasEditAction ? (
200
- <TouchableOpacity
201
- style={[styles.iconButton, { borderColor: tableBorderColor, backgroundColor: tableSurfaceColor }]}
202
- activeOpacity={0.82}
203
- onPress={() => configs.onEditRow?.(row)}
204
- >
205
- <Icon name="edit-2" size={14} color={tableMutedColor} />
206
- </TouchableOpacity>
207
- ) : null}
208
- </View>
209
- </View>
210
- ) : null}
211
- </RowComponent>
212
- );
213
- };
214
-
215
- return (
216
- <>
217
- <ScrollView
218
- horizontal
219
- onContentSizeChange={handleHorizontalContentSizeChange}
220
- onLayout={handleHorizontalLayout}
221
- onScroll={handleHorizontalScroll}
222
- scrollEventThrottle={16}
223
- style={styles.scroll}
224
- contentContainerStyle={styles.horizontalScrollContent}
225
- >
226
- <View style={styles.content}>
226
+ {hasEditAction ? (
227
+ <TouchableOpacity
228
+ style={[styles.iconButton, { borderColor: tableBorderColor, backgroundColor: tableSurfaceColor }]}
229
+ activeOpacity={0.82}
230
+ onPress={() => configs.onEditRow?.(row)}
231
+ >
232
+ <Icon name="edit-2" size={14} color={tableMutedColor} />
233
+ </TouchableOpacity>
234
+ ) : null}
235
+ </View>
236
+ </View>
237
+ ) : null}
238
+ </RowComponent>
239
+ );
240
+ };
241
+
242
+ return (
243
+ <>
244
+ <ScrollView
245
+ horizontal
246
+ onContentSizeChange={handleHorizontalContentSizeChange}
247
+ onLayout={handleHorizontalLayout}
248
+ onScroll={handleHorizontalScroll}
249
+ scrollEventThrottle={16}
250
+ style={styles.scroll}
251
+ contentContainerStyle={styles.horizontalScrollContent}
252
+ >
253
+ <View style={[styles.content, { minWidth: tableWidth, width: tableWidth }]}>
227
254
  <View
228
255
  style={[
229
256
  styles.headerRow,
@@ -231,66 +258,69 @@ const DefaultTableRows = ({ storeName }) => {
231
258
  backgroundColor: tableHeaderColor,
232
259
  borderBottomColor: tableHeaderBorderColor,
233
260
  borderBottomWidth: tableHeaderBorderColor ? 1 : 0,
261
+ minWidth: tableWidth,
262
+ width: tableWidth,
234
263
  },
235
264
  ]}
236
265
  >
237
- {tableColumns.map(column => {
238
- const fieldName = getColumnKey(column);
239
- const label = formatStoreColumnLabel({
240
- columns,
241
- fieldName,
242
- fallbackLabel: column?.label || fieldName,
243
- storeName,
244
- });
245
- const sortFieldName = getSortField(column);
246
-
247
- return (
248
- <TouchableOpacity
249
- key={fieldName}
250
- style={[
251
- getColumnStyle(column),
252
- column?.isIdentity
253
- ? [styles.stickyIdentityCell, styles.stickyHeaderCell, { backgroundColor: tableHeaderColor }]
254
- : null,
255
- ]}
256
- activeOpacity={isSortableColumn(column) ? 0.8 : 1}
257
- onPress={() => configs.requestSort?.(column)}
258
- >
259
- <View style={styles.sortableHeader}>
260
- <Text style={[styles.headerText, { color: tableTextColor }]} numberOfLines={1}>{label}</Text>
261
- {isSortableColumn(column) && configs.resolvedSort?.field === sortFieldName ? (
262
- <Icon name={configs.resolvedSort?.direction === 'desc' ? 'chevron-down' : 'chevron-up'} size={12} color={tableTextColor} />
263
- ) : isSortableColumn(column) ? (
264
- <Icon name="chevrons-up" size={12} color={tableBorderColor} />
265
- ) : null}
266
- {resolvedFilters?.[fieldName] ? <Icon name="filter" size={11} color={tableTextColor} /> : null}
267
- </View>
268
- </TouchableOpacity>
269
- );
270
- })}
271
- {hasRowActions ? (
272
- <View
273
- style={[
274
- styles.cell,
275
- styles.actionsCell,
276
- shouldPinRowActions ? styles.stickyActionsCell : null,
277
- shouldPinRowActions ? styles.stickyHeaderCell : null,
278
- {
279
- minWidth: actionsCellWidth,
280
- width: actionsCellWidth,
281
- flexBasis: actionsCellWidth,
282
- maxWidth: actionsCellWidth,
283
- backgroundColor: tableHeaderColor,
284
- },
285
- ]}
286
- >
287
- <Text style={[styles.headerText, { color: tableTextColor }]}>
288
- {global.t?.t(storeName, 'label', 'actions')}
289
- </Text>
290
- </View>
291
- ) : null}
292
- </View>
293
- {hasColumnFilters ? (
266
+ {tableColumns.map(column => {
267
+ const fieldName = getColumnKey(column);
268
+ const label = formatStoreColumnLabel({
269
+ columns,
270
+ fieldName,
271
+ fallbackLabel: column?.label || fieldName,
272
+ storeName,
273
+ });
274
+ const sortFieldName = getSortField(column);
275
+
276
+ return (
277
+ <TouchableOpacity
278
+ key={fieldName}
279
+ style={[
280
+ getColumnStyle(column),
281
+ column?.isIdentity
282
+ ? [styles.stickyIdentityCell, styles.stickyHeaderCell, { backgroundColor: tableHeaderColor }]
283
+ : null,
284
+ ]}
285
+ activeOpacity={isSortableColumn(column) ? 0.8 : 1}
286
+ onPress={() => configs.requestSort?.(column)}
287
+ >
288
+ <View style={styles.sortableHeader}>
289
+ <Text style={[styles.headerText, { color: tableTextColor }]} numberOfLines={1}>{label}</Text>
290
+ {isSortableColumn(column) && configs.resolvedSort?.field === sortFieldName ? (
291
+ <Icon name={configs.resolvedSort?.direction === 'desc' ? 'chevron-down' : 'chevron-up'} size={12} color={tableTextColor} />
292
+ ) : isSortableColumn(column) ? (
293
+ <Icon name="chevrons-up" size={12} color={tableBorderColor} />
294
+ ) : null}
295
+ {resolvedFilters?.[fieldName] ? <Icon name="filter" size={11} color={tableTextColor} /> : null}
296
+ </View>
297
+ </TouchableOpacity>
298
+ );
299
+ })}
300
+ {hasRowActions ? (
301
+ <View
302
+ testID="default-table-row-actions-header"
303
+ style={[
304
+ styles.cell,
305
+ styles.actionsCell,
306
+ shouldPinRowActions ? styles.stickyActionsCell : null,
307
+ shouldPinRowActions ? styles.stickyHeaderCell : null,
308
+ {
309
+ minWidth: actionsCellWidth,
310
+ width: actionsCellWidth,
311
+ flexBasis: actionsCellWidth,
312
+ maxWidth: actionsCellWidth,
313
+ backgroundColor: tableHeaderColor,
314
+ },
315
+ ]}
316
+ >
317
+ <Text style={[styles.headerText, { color: tableTextColor }]}>
318
+ {global.t?.t(storeName, 'label', 'actions')}
319
+ </Text>
320
+ </View>
321
+ ) : null}
322
+ </View>
323
+ {hasColumnFilters ? (
294
324
  <View
295
325
  style={[
296
326
  styles.filterRow,
@@ -301,77 +331,79 @@ const DefaultTableRows = ({ storeName }) => {
301
331
  },
302
332
  ]}
303
333
  >
304
- {tableColumns.map(column => {
305
- const fieldName = getColumnKey(column);
306
-
307
- return (
308
- <View
309
- key={`${fieldName}-filter`}
310
- style={[
311
- getColumnStyle(column),
312
- column?.isIdentity
313
- ? [styles.stickyIdentityCell, { backgroundColor: tableHeaderColor }]
314
- : null,
315
- ]}
316
- >
317
- {column?.filter !== false && column?.filters !== false ? (
318
- <DefaultColumnFilter
319
- column={column}
320
- filters={resolvedFilters}
321
- storeName={storeName}
322
- style={styles.filterCell}
323
- />
324
- ) : null}
325
- </View>
326
- );
327
- })}
328
- {hasRowActions ? (
329
- <View
330
- style={[
331
- styles.cell,
332
- styles.actionsCell,
333
- shouldPinRowActions ? styles.stickyActionsCell : null,
334
- {
335
- minWidth: actionsCellWidth,
336
- width: actionsCellWidth,
337
- flexBasis: actionsCellWidth,
338
- maxWidth: actionsCellWidth,
339
- backgroundColor: tableHeaderColor,
340
- },
341
- ]}
342
- />
343
- ) : null}
344
- </View>
345
- ) : null}
346
-
347
- <FlatList
348
- data={sortedData}
349
- keyExtractor={getRowKey}
350
- renderItem={renderTableItem}
351
- style={styles.tableList}
352
- contentContainerStyle={styles.tableListContent}
353
- ListEmptyComponent={(
354
- <DefaultTableEmptyState
355
- emptyStateLabel={emptyStateLabel}
356
- isLoading={isLoading}
357
- isTable
358
- tableMutedColor={tableMutedColor}
359
- />
360
- )}
361
- ListFooterComponent={null}
362
- nestedScrollEnabled
363
- onMomentumScrollBegin={configs.onMomentumScrollBegin || undefined}
364
- onScroll={handleListScroll}
365
- onScrollBeginDrag={configs.onScrollBeginDrag || undefined}
366
- onEndReached={configs.onEndReached}
367
- onEndReachedThreshold={END_REACHED_THRESHOLD}
368
- scrollEventThrottle={120}
369
- showsVerticalScrollIndicator={false}
370
- />
371
- </View>
372
- </ScrollView>
373
- </>
374
- );
375
- };
376
-
377
- export default DefaultTableRows;
334
+ {tableColumns.map(column => {
335
+ const fieldName = getColumnKey(column);
336
+
337
+ return (
338
+ <View
339
+ key={`${fieldName}-filter`}
340
+ style={[
341
+ getColumnStyle(column),
342
+ column?.isIdentity
343
+ ? [styles.stickyIdentityCell, { backgroundColor: tableHeaderColor }]
344
+ : null,
345
+ ]}
346
+ >
347
+ {column?.filter !== false && column?.filters !== false ? (
348
+ <DefaultColumnFilter
349
+ column={column}
350
+ filters={resolvedFilters}
351
+ storeName={storeName}
352
+ style={styles.filterCell}
353
+ />
354
+ ) : null}
355
+ </View>
356
+ );
357
+ })}
358
+ {hasRowActions ? (
359
+ <View
360
+ style={[
361
+ styles.cell,
362
+ styles.actionsCell,
363
+ shouldPinRowActions ? styles.stickyActionsCell : null,
364
+ {
365
+ minWidth: actionsCellWidth,
366
+ width: actionsCellWidth,
367
+ flexBasis: actionsCellWidth,
368
+ maxWidth: actionsCellWidth,
369
+ backgroundColor: tableHeaderColor,
370
+ },
371
+ ]}
372
+ />
373
+ ) : null}
374
+ </View>
375
+ ) : null}
376
+
377
+ <FlatList
378
+ data={sortedData}
379
+ keyExtractor={getRowKey}
380
+ renderItem={renderTableItem}
381
+ style={styles.tableList}
382
+ contentContainerStyle={styles.tableListContent}
383
+ ListEmptyComponent={(
384
+ <DefaultTableEmptyState
385
+ emptyStateLabel={emptyStateLabel}
386
+ isLoading={isLoading}
387
+ isTable
388
+ tableMutedColor={tableMutedColor}
389
+ />
390
+ )}
391
+ ListFooterComponent={null}
392
+ nestedScrollEnabled
393
+ onMomentumScrollBegin={configs.onMomentumScrollBegin || undefined}
394
+ onScroll={handleListScroll}
395
+ onScrollBeginDrag={configs.onScrollBeginDrag || undefined}
396
+ onEndReached={configs.onEndReached}
397
+ onEndReachedThreshold={END_REACHED_THRESHOLD}
398
+ onRefresh={configs.onRefresh}
399
+ refreshing={Boolean(configs.refreshing)}
400
+ scrollEventThrottle={120}
401
+ showsVerticalScrollIndicator={false}
402
+ />
403
+ </View>
404
+ </ScrollView>
405
+ </>
406
+ );
407
+ };
408
+
409
+ export default DefaultTableRows;