@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.
- package/.github/workflows/publish-npm.yml +91 -0
- package/AGENTS.md +1 -1
- package/docs/technical/DefaultTable.md +70 -1
- package/package.json +22 -22
- package/src/react/components/address/DefaultAddress.js +20 -9
- package/src/react/components/address/DefaultAddressCoordFields.js +59 -0
- package/src/react/components/address/LatLonReadonlyFields.js +2 -2
- package/src/react/components/address/defaultAddressStyles.js +13 -1
- package/src/react/components/files/DefaultFile.js +2 -2
- package/src/react/components/files/DefaultFileColumn.js +1 -1
- package/src/react/components/filters/DefaultColumnFilter.js +2 -7
- package/src/react/components/filters/DefaultExternalFilters.js +4 -11
- package/src/react/components/filters/filterValue.js +65 -0
- package/src/react/components/help/DefaultTooltip.js +73 -63
- package/src/react/components/help/DefaultTooltip.styles.js +23 -23
- package/src/react/components/inputs/DefaultInput.js +301 -210
- package/src/react/components/inputs/defaultInputUtils.js +193 -170
- package/src/react/components/table/DefaultColumnMenu.js +127 -109
- package/src/react/components/table/DefaultTable.js +4 -4
- package/src/react/components/table/DefaultTable.styles.js +10 -4
- package/src/react/components/table/DefaultTable.utils.js +38 -4
- package/src/react/components/table/DefaultTableRows.js +324 -153
- package/src/react/components/upload/DefaultUpload.js +6 -36
- package/src/react/components/upload/DefaultUpload.styles.js +5 -5
- package/src/react/components/upload/DefaultUploadAttachmentsList.js +11 -3
- package/src/react/hooks/usePostalCodeLookup.js +15 -1
- package/src/react/services/addressFormUtils.js +104 -38
- package/src/react/services/addressGeo.js +6 -3
- package/src/react/utils/tableVisibleColumnsPreferences.js +84 -3
- package/src/tests/react/components/files/DefaultFile.test.js +16 -0
- package/src/tests/react/components/files/DefaultFileColumn.company.test.js +24 -0
- package/src/tests/react/components/help/DefaultTooltip.test.js +93 -0
- package/src/tests/react/components/table/DefaultTable.utils.test.js +10 -0
- package/src/tests/react/components/table/DefaultTableRowActions.test.js +23 -0
- package/src/tests/react/components/table/DefaultTableRows.rowActions.test.js +152 -0
- package/src/tests/react/components/upload/defaultUploadLibrary.test.js +31 -9
- package/src/tests/react/filters/filterValue.test.js +46 -0
- package/src/tests/react/hooks/usePostalCodeLookup.test.js +67 -0
- package/src/tests/react/inputs/defaultInputUtils.test.js +9 -1
- package/src/tests/react/services/addressFormUtils.test.js +6 -3
- package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +12 -1
|
@@ -436,10 +436,10 @@ const DefaultTable = ({
|
|
|
436
436
|
],
|
|
437
437
|
);
|
|
438
438
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
439
|
+
// Do not mutate store.configs during render. A new configs object is built
|
|
440
|
+
// every pass; writing it here retriggers subscribers and React #185 when
|
|
441
|
+
// opening Adicionar on DefaultTable (categories). Sync only in the hook
|
|
442
|
+
// when defaultTableConfigsSignature changes.
|
|
443
443
|
useDefaultTableStoreSync({
|
|
444
444
|
columns,
|
|
445
445
|
columnsForTable,
|
|
@@ -470,11 +470,13 @@ const styles = StyleSheet.create({
|
|
|
470
470
|
textTransform: 'uppercase',
|
|
471
471
|
},
|
|
472
472
|
actionsCell: {
|
|
473
|
-
minWidth:
|
|
473
|
+
minWidth: 60,
|
|
474
|
+
width: 60,
|
|
475
|
+
flexBasis: 60,
|
|
474
476
|
flexGrow: 0,
|
|
475
477
|
flexShrink: 0,
|
|
478
|
+
maxWidth: 72,
|
|
476
479
|
alignItems: 'center',
|
|
477
|
-
paddingHorizontal: 8,
|
|
478
480
|
},
|
|
479
481
|
rowActionsGroup: {
|
|
480
482
|
flexDirection: 'row',
|
|
@@ -517,10 +519,11 @@ const styles = StyleSheet.create({
|
|
|
517
519
|
},
|
|
518
520
|
cardsGrid: {
|
|
519
521
|
paddingVertical: 10,
|
|
520
|
-
paddingHorizontal:
|
|
521
|
-
gap:
|
|
522
|
+
paddingHorizontal: 0,
|
|
523
|
+
gap: 8,
|
|
522
524
|
flexGrow: 1,
|
|
523
525
|
width: '100%',
|
|
526
|
+
alignSelf: 'stretch',
|
|
524
527
|
},
|
|
525
528
|
cardsGridWithBottomAdd: {
|
|
526
529
|
paddingBottom: 86,
|
|
@@ -539,8 +542,11 @@ const styles = StyleSheet.create({
|
|
|
539
542
|
cardItem: {
|
|
540
543
|
position: 'relative',
|
|
541
544
|
flex: 1,
|
|
545
|
+
flexBasis: 0,
|
|
546
|
+
flexGrow: 1,
|
|
542
547
|
minWidth: 0,
|
|
543
548
|
width: '100%',
|
|
549
|
+
maxWidth: '100%',
|
|
544
550
|
alignSelf: 'stretch',
|
|
545
551
|
},
|
|
546
552
|
cardItemWithActions: {
|
|
@@ -438,6 +438,42 @@ export const normalizeSortText = value =>
|
|
|
438
438
|
.replace(/[\u0300-\u036f]/g, '')
|
|
439
439
|
.toLowerCase();
|
|
440
440
|
|
|
441
|
+
export const parseSortNumber = value => {
|
|
442
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
443
|
+
if (value == null) return NaN;
|
|
444
|
+
|
|
445
|
+
const raw = String(value).trim();
|
|
446
|
+
if (!raw || !/[0-9]/.test(raw)) return NaN;
|
|
447
|
+
|
|
448
|
+
const compact = raw.replace(/[^0-9,.-]/g, '');
|
|
449
|
+
if (!compact) return NaN;
|
|
450
|
+
|
|
451
|
+
const hasComma = compact.includes(',');
|
|
452
|
+
const hasDot = compact.includes('.');
|
|
453
|
+
let normalized = compact;
|
|
454
|
+
|
|
455
|
+
if (hasComma && hasDot) {
|
|
456
|
+
normalized = compact.lastIndexOf(',') > compact.lastIndexOf('.')
|
|
457
|
+
? compact.replace(/\./g, '').replace(',', '.')
|
|
458
|
+
: compact.replace(/,/g, '');
|
|
459
|
+
} else if (hasComma) {
|
|
460
|
+
const parts = compact.split(',');
|
|
461
|
+
normalized = parts.length === 2 && parts[1].length <= 2
|
|
462
|
+
? `${parts[0].replace(/\./g, '')}.${parts[1]}`
|
|
463
|
+
: compact.replace(/,/g, '');
|
|
464
|
+
} else if (hasDot) {
|
|
465
|
+
const parts = compact.split('.');
|
|
466
|
+
if (parts.length > 2) {
|
|
467
|
+
normalized = compact.replace(/\./g, '');
|
|
468
|
+
} else if (parts.length === 2 && parts[1].length === 3 && parts[0].length <= 3) {
|
|
469
|
+
normalized = compact.replace(/\./g, '');
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const parsed = Number(normalized);
|
|
474
|
+
return Number.isFinite(parsed) ? parsed : NaN;
|
|
475
|
+
};
|
|
476
|
+
|
|
441
477
|
export const resolveSortComparable = ({ column, row, storeName, columns }) => {
|
|
442
478
|
const fieldName = getColumnKey(column);
|
|
443
479
|
const sortField = getSortField(column);
|
|
@@ -472,11 +508,9 @@ export const resolveSortComparable = ({ column, row, storeName, columns }) => {
|
|
|
472
508
|
storeName,
|
|
473
509
|
}));
|
|
474
510
|
|
|
475
|
-
const normalizedNumber =
|
|
476
|
-
String(resolvedValue).replace(/[^0-9,.-]/g, '').replace(',', '.'),
|
|
477
|
-
);
|
|
511
|
+
const normalizedNumber = parseSortNumber(resolvedValue);
|
|
478
512
|
|
|
479
|
-
if (Number.isFinite(normalizedNumber)
|
|
513
|
+
if (Number.isFinite(normalizedNumber)) {
|
|
480
514
|
return normalizedNumber;
|
|
481
515
|
}
|
|
482
516
|
|
|
@@ -4,20 +4,13 @@ import Icon from 'react-native-vector-icons/Feather';
|
|
|
4
4
|
import { useStore } from '@store';
|
|
5
5
|
import { formatStoreColumnLabel } from '@controleonline/ui-common/src/react/utils/storeColumns';
|
|
6
6
|
import { getColumnKey } from '../inputs/defaultInputUtils';
|
|
7
|
+
import DefaultColumnFilter from '../filters/DefaultColumnFilter';
|
|
7
8
|
import DefaultTableEmptyState from './DefaultTableEmptyState';
|
|
8
9
|
import DefaultTableInput from './DefaultTableInput';
|
|
9
10
|
import DefaultTableRowActions, {
|
|
10
11
|
hasDefaultTableRowActionsComponent,
|
|
11
12
|
resolveDefaultTableRowActionsWidth,
|
|
12
13
|
} from './DefaultTableRowActions';
|
|
13
|
-
import {
|
|
14
|
-
persistStoreSelection,
|
|
15
|
-
flattenGroupedTableItems,
|
|
16
|
-
isRowSelected,
|
|
17
|
-
resolveStoreSelectedIds,
|
|
18
|
-
toggleGroupSelection,
|
|
19
|
-
toggleSelectedId,
|
|
20
|
-
} from './DefaultTable.grouping';
|
|
21
14
|
import {
|
|
22
15
|
END_REACHED_THRESHOLD,
|
|
23
16
|
getColumnStyle,
|
|
@@ -32,17 +25,8 @@ import {
|
|
|
32
25
|
import styles from './DefaultTable.styles';
|
|
33
26
|
import useDefaultTableTheme from './useDefaultTableTheme';
|
|
34
27
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/** Row stripe colors from themes-map listItemEvenRow / listItemOddRow — never status.color. */
|
|
38
|
-
export const resolveRowBackgroundColor = ({
|
|
39
|
-
selected,
|
|
40
|
-
index,
|
|
41
|
-
tableOddColor,
|
|
42
|
-
tableEvenColor,
|
|
43
|
-
selectedBackground = '#ECFEFF',
|
|
44
|
-
}) => {
|
|
45
|
-
if (selected) return selectedBackground;
|
|
28
|
+
/** Row stripes: themes-map listItemEvenRow / listItemOddRow — never status.color. */
|
|
29
|
+
export const resolveRowBackgroundColor = ({ index, tableOddColor, tableEvenColor }) => {
|
|
46
30
|
return index % 2 === 0 ? tableOddColor : tableEvenColor;
|
|
47
31
|
};
|
|
48
32
|
|
|
@@ -59,179 +43,366 @@ const DefaultTableRows = ({ storeName }) => {
|
|
|
59
43
|
liveItems: store?.getters?.items,
|
|
60
44
|
sortedData: configs.sortedData,
|
|
61
45
|
});
|
|
62
|
-
const
|
|
63
|
-
const selectable = configs.selectable === true;
|
|
64
|
-
const selectedIds = resolveStoreSelectedIds(store);
|
|
46
|
+
const resolvedFilters = store?.getters?.filters || {};
|
|
65
47
|
const isLoading = Boolean(store?.getters?.isLoadingList || store?.getters?.isLoading);
|
|
66
|
-
const emptyStateLabel = isLoading
|
|
48
|
+
const emptyStateLabel = isLoading
|
|
49
|
+
? global.t?.t(storeName, 'label', 'loading')
|
|
50
|
+
: global.t?.t(storeName, 'label', 'empty');
|
|
67
51
|
const tableBorderColor = tableBorderColors.rowBorderColor;
|
|
68
52
|
const tableHeaderBorderColor = tableBorderColors.headerBorderColor || tableBorderColor;
|
|
69
53
|
const tableEvenColor = themeTokens.listItemEvenRow || themeTokens['bg-even-light'] || palette.background;
|
|
70
54
|
const tableHeaderColor = themeTokens['bg-headers-light'] || resolvedAccentColor;
|
|
71
55
|
const tableMutedColor = palette.textSecondary;
|
|
72
56
|
const tableOddColor = themeTokens.listItemOddRow || themeTokens['bg-odd-light'] || palette.background;
|
|
57
|
+
const tableSurfaceColor = palette.background;
|
|
73
58
|
const tableTextColor = palette.text;
|
|
74
59
|
const rowStyle = configs.rowStyle;
|
|
75
60
|
const hasRowPress = typeof configs.onRowPress === 'function';
|
|
76
|
-
const
|
|
77
|
-
const hasCustomRowActions = hasDefaultTableRowActionsComponent(RowActionsComponent);
|
|
61
|
+
const hasCustomRowActions = hasDefaultTableRowActionsComponent(configs.rowActionsComponent);
|
|
78
62
|
const hasEditAction = typeof configs.onEditRow === 'function';
|
|
79
|
-
const hasRowActions =
|
|
80
|
-
|
|
81
|
-
const
|
|
63
|
+
const hasRowActions = configs.showRowActions !== false && (hasCustomRowActions || hasEditAction);
|
|
64
|
+
const shouldPinRowActions = configs.pinRowActions !== false;
|
|
65
|
+
const hasColumnFilters =
|
|
66
|
+
configs.showColumnFiltersButton !== false &&
|
|
67
|
+
configs.tableFiltersVisible === true;
|
|
82
68
|
const actionsCellWidth = hasRowActions ? resolveDefaultTableRowActionsWidth(configs) : 0;
|
|
83
|
-
const selectionWidth = selectable ? SELECTION_COLUMN_WIDTH : 0;
|
|
84
69
|
const tableMinWidth = useMemo(
|
|
85
|
-
() =>
|
|
86
|
-
|
|
70
|
+
() =>
|
|
71
|
+
tableColumns.reduce(
|
|
72
|
+
(totalWidth, column) => totalWidth + getColumnMinWidth(column),
|
|
73
|
+
actionsCellWidth,
|
|
74
|
+
),
|
|
75
|
+
[actionsCellWidth, tableColumns],
|
|
87
76
|
);
|
|
88
|
-
const [horizontalMetrics, setHorizontalMetrics] = useState({
|
|
77
|
+
const [horizontalMetrics, setHorizontalMetrics] = useState({
|
|
78
|
+
contentWidth: 0,
|
|
79
|
+
viewportWidth: 0,
|
|
80
|
+
x: 0,
|
|
81
|
+
});
|
|
89
82
|
const updateHorizontalMetrics = patch => {
|
|
90
83
|
setHorizontalMetrics(current => {
|
|
91
|
-
const next = {
|
|
92
|
-
|
|
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;
|
|
93
94
|
});
|
|
94
95
|
};
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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;
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
openRow={hasRowPress ? () => configs.onRowPress(row) : null}
|
|
116
|
-
row={row}
|
|
117
|
-
storeName={storeName}
|
|
118
|
-
/>
|
|
119
|
-
) : null;
|
|
120
|
-
|
|
121
|
-
const editButton = hasEditAction ? (
|
|
122
|
-
<Text onPress={() => configs.onEditRow?.(row)} style={{ color: '#0284C7', fontWeight: '800', fontSize: 12 }}>
|
|
123
|
-
{global.t?.t(storeName, 'button', 'edit') || 'Editar'}
|
|
124
|
-
</Text>
|
|
125
|
-
) : null;
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<View
|
|
129
|
-
style={[
|
|
130
|
-
styles.cell,
|
|
131
|
-
styles.actionsCell,
|
|
132
|
-
pinRowActions ? styles.stickyActionsCell : null,
|
|
133
|
-
pinRowActions ? styles.pinnedActionsCell : null,
|
|
134
|
-
{
|
|
135
|
-
minWidth: actionsCellWidth,
|
|
136
|
-
width: actionsCellWidth,
|
|
137
|
-
maxWidth: actionsCellWidth,
|
|
138
|
-
flexBasis: actionsCellWidth,
|
|
139
|
-
backgroundColor: rowBackgroundColor,
|
|
140
|
-
},
|
|
141
|
-
]}
|
|
142
|
-
>
|
|
143
|
-
<View style={styles.rowActionsGroup}>
|
|
144
|
-
{customRowActions}
|
|
145
|
-
{editButton}
|
|
146
|
-
</View>
|
|
147
|
-
</View>
|
|
148
|
-
);
|
|
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
|
+
});
|
|
149
116
|
};
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
<View
|
|
155
|
-
style={[
|
|
156
|
-
styles.cell,
|
|
157
|
-
styles.actionsCell,
|
|
158
|
-
pinRowActions ? styles.stickyActionsCell : null,
|
|
159
|
-
pinRowActions ? styles.stickyHeaderCell : null,
|
|
160
|
-
{
|
|
161
|
-
minWidth: actionsCellWidth,
|
|
162
|
-
width: actionsCellWidth,
|
|
163
|
-
maxWidth: actionsCellWidth,
|
|
164
|
-
flexBasis: actionsCellWidth,
|
|
165
|
-
backgroundColor: tableHeaderColor,
|
|
166
|
-
},
|
|
167
|
-
]}
|
|
168
|
-
>
|
|
169
|
-
<Text style={[styles.headerText, { color: tableTextColor }]} numberOfLines={1}>
|
|
170
|
-
{global.t?.t(storeName, 'label', 'actions') || 'Ações'}
|
|
171
|
-
</Text>
|
|
172
|
-
</View>
|
|
173
|
-
);
|
|
117
|
+
const handleHorizontalLayout = event => {
|
|
118
|
+
updateHorizontalMetrics({
|
|
119
|
+
viewportWidth: Number(event?.nativeEvent?.layout?.width || 0),
|
|
120
|
+
});
|
|
174
121
|
};
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
<View style={{ flex: 1, paddingHorizontal: 8 }}>
|
|
184
|
-
<Text style={[styles.headerText, { color: tableTextColor }]} numberOfLines={2}>{item.group?.label}</Text>
|
|
185
|
-
<Text style={{ color: tableMutedColor, fontSize: 12 }}>{item.group?.count} item(s)</Text>
|
|
186
|
-
</View>
|
|
187
|
-
{hasRowActions ? <View style={{ minWidth: actionsCellWidth, width: actionsCellWidth }} /> : null}
|
|
188
|
-
</View>
|
|
189
|
-
);
|
|
122
|
+
const handleHorizontalContentSizeChange = width => {
|
|
123
|
+
updateHorizontalMetrics({
|
|
124
|
+
contentWidth: Number(width || 0),
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
const handleListScroll = event => {
|
|
128
|
+
if (shouldTriggerEndReachedFromScroll(event)) {
|
|
129
|
+
configs.onEndReached?.();
|
|
190
130
|
}
|
|
131
|
+
};
|
|
132
|
+
const tableWidth = Math.max(
|
|
133
|
+
tableMinWidth,
|
|
134
|
+
Number(horizontalMetrics.viewportWidth || 0),
|
|
135
|
+
);
|
|
191
136
|
|
|
192
|
-
|
|
193
|
-
const RowComponent = hasRowPress
|
|
194
|
-
const
|
|
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
|
+
: {};
|
|
195
146
|
const rowBackgroundColor = resolveRowBackgroundColor({
|
|
196
|
-
selected,
|
|
197
147
|
index,
|
|
198
148
|
tableOddColor,
|
|
199
149
|
tableEvenColor,
|
|
200
150
|
});
|
|
151
|
+
const RowActionsComponent = configs.rowActionsComponent;
|
|
152
|
+
|
|
201
153
|
return (
|
|
202
|
-
<RowComponent
|
|
203
|
-
{
|
|
154
|
+
<RowComponent
|
|
155
|
+
key={getRowKey(row)}
|
|
156
|
+
style={[
|
|
157
|
+
styles.row,
|
|
158
|
+
{
|
|
159
|
+
backgroundColor: rowBackgroundColor,
|
|
160
|
+
borderBottomColor: tableBorderColor,
|
|
161
|
+
borderBottomWidth: tableBorderColor ? 1 : 0,
|
|
162
|
+
minWidth: tableWidth,
|
|
163
|
+
width: tableWidth,
|
|
164
|
+
},
|
|
165
|
+
rowStyleValue,
|
|
166
|
+
]}
|
|
167
|
+
{...rowPressProps}
|
|
168
|
+
>
|
|
204
169
|
{tableColumns.map(column => (
|
|
205
170
|
<React.Fragment key={getColumnKey(column)}>
|
|
206
|
-
<DefaultTableInput
|
|
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
|
+
/>
|
|
207
190
|
</React.Fragment>
|
|
208
191
|
))}
|
|
209
|
-
{
|
|
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}>
|
|
213
|
+
{hasCustomRowActions ? (
|
|
214
|
+
<DefaultTableRowActions
|
|
215
|
+
component={RowActionsComponent}
|
|
216
|
+
helpers={{
|
|
217
|
+
openEdit: () => configs.onEditRow?.(row),
|
|
218
|
+
openRow: hasRowPress ? () => configs.onRowPress(row) : null,
|
|
219
|
+
}}
|
|
220
|
+
openEdit={() => configs.onEditRow?.(row)}
|
|
221
|
+
openRow={hasRowPress ? () => configs.onRowPress(row) : null}
|
|
222
|
+
row={row}
|
|
223
|
+
storeName={storeName}
|
|
224
|
+
/>
|
|
225
|
+
) : null}
|
|
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}
|
|
210
238
|
</RowComponent>
|
|
211
239
|
);
|
|
212
240
|
};
|
|
213
241
|
|
|
214
242
|
return (
|
|
215
|
-
|
|
216
|
-
<
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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 }]}>
|
|
254
|
+
<View
|
|
255
|
+
style={[
|
|
256
|
+
styles.headerRow,
|
|
257
|
+
{
|
|
258
|
+
backgroundColor: tableHeaderColor,
|
|
259
|
+
borderBottomColor: tableHeaderBorderColor,
|
|
260
|
+
borderBottomWidth: tableHeaderBorderColor ? 1 : 0,
|
|
261
|
+
minWidth: tableWidth,
|
|
262
|
+
width: tableWidth,
|
|
263
|
+
},
|
|
264
|
+
]}
|
|
265
|
+
>
|
|
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 ? (
|
|
324
|
+
<View
|
|
325
|
+
style={[
|
|
326
|
+
styles.filterRow,
|
|
327
|
+
{
|
|
328
|
+
backgroundColor: tableHeaderColor,
|
|
329
|
+
borderBottomColor: tableHeaderBorderColor,
|
|
330
|
+
borderBottomWidth: tableHeaderBorderColor ? 1 : 0,
|
|
331
|
+
},
|
|
332
|
+
]}
|
|
333
|
+
>
|
|
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
|
+
/>
|
|
231
403
|
</View>
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
</ScrollView>
|
|
404
|
+
</ScrollView>
|
|
405
|
+
</>
|
|
235
406
|
);
|
|
236
407
|
};
|
|
237
408
|
|