@controleonline/ui-default 1.0.265 → 1.0.267

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.
@@ -1,340 +1,367 @@
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
- import DefaultTableEmptyState from './DefaultTableEmptyState';
9
- import DefaultTableInput from './DefaultTableInput';
10
- import {
11
- END_REACHED_THRESHOLD,
12
- getColumnStyle,
13
- getRowKey,
14
- getSortField,
15
- isSortableColumn,
16
- mergeSortedDataWithLiveItems,
17
- shouldTriggerEndReachedFromScroll,
18
- shouldIncludeColumn,
19
- } from './DefaultTable.utils';
20
- import styles from './DefaultTable.styles';
21
- import useDefaultTableTheme from './useDefaultTableTheme';
22
-
23
- const DefaultTableRows = ({ storeName }) => {
24
- const store = useStore(storeName);
25
- const configs = store?.getters?.configs || {};
26
- const { palette, resolvedAccentColor, themeTokens } = useDefaultTableTheme();
27
- const columns = Array.isArray(store?.getters?.columns) ? store.getters.columns : [];
28
- const visibleColumns = store?.getters?.visibleColumns || {};
29
- const tableColumns = columns.filter(
30
- column => shouldIncludeColumn(column) && visibleColumns[getColumnKey(column)] !== false,
31
- );
32
- const sortedData = mergeSortedDataWithLiveItems({
33
- liveItems: store?.getters?.items,
34
- sortedData: configs.sortedData,
35
- });
36
- const resolvedFilters = store?.getters?.filters || {};
37
- const isLoading = Boolean(store?.getters?.isLoadingList || store?.getters?.isLoading);
38
- const emptyStateLabel = isLoading
39
- ? global.t?.t(storeName, 'label', 'loading')
40
- : global.t?.t(storeName, 'label', 'empty');
41
- const tableBorderColor = palette.border;
42
- const tableEvenColor = themeTokens.listItemEvenRow || themeTokens['bg-even-light'] || palette.background;
43
- const tableHeaderColor = themeTokens['bg-headers-light'] || resolvedAccentColor;
44
- const tableMutedColor = palette.textSecondary;
45
- const tableOddColor = themeTokens.listItemOddRow || themeTokens['bg-odd-light'] || palette.background;
46
- const tableSurfaceColor = palette.background;
47
- const tableTextColor = palette.text;
48
- const rowStyle = configs.rowStyle;
49
- const hasRowPress = typeof configs.onRowPress === 'function';
50
- const hasCustomRowActions = typeof configs.rowActionsComponent === 'function';
51
- const hasEditAction = typeof configs.onEditRow === 'function';
52
- const hasRowActions = configs.showRowActions !== false && (hasCustomRowActions || hasEditAction);
53
- const hasColumnFilters =
54
- configs.showColumnFiltersButton !== false &&
55
- configs.tableFiltersVisible === true;
56
- const actionsCellWidth = hasRowActions ? 96 : 0;
57
- const [horizontalMetrics, setHorizontalMetrics] = useState({
58
- contentWidth: 0,
59
- viewportWidth: 0,
60
- x: 0,
61
- });
62
- const updateHorizontalMetrics = patch => {
63
- setHorizontalMetrics(current => {
64
- const next = {
65
- ...current,
66
- ...patch,
67
- };
68
-
69
- return next.contentWidth === current.contentWidth &&
70
- next.viewportWidth === current.viewportWidth &&
71
- next.x === current.x
72
- ? current
73
- : next;
74
- });
75
- };
76
- const stickyBodyTransforms = useMemo(() => {
77
- const viewportWidth = Number(horizontalMetrics.viewportWidth || 0);
78
- const contentWidth = Number(horizontalMetrics.contentWidth || 0);
79
- const x = Number(horizontalMetrics.x || 0);
80
- const canScrollHorizontally = viewportWidth > 0 && contentWidth > viewportWidth;
81
- const actionsTranslateX = canScrollHorizontally
82
- ? x + viewportWidth - contentWidth
83
- : 0;
84
-
85
- return {
86
- actions: actionsTranslateX !== 0 ? [{ translateX: actionsTranslateX }] : null,
87
- identity: x !== 0 ? [{ translateX: x }] : null,
88
- };
89
- }, [horizontalMetrics]);
90
- const handleHorizontalScroll = event => {
91
- updateHorizontalMetrics({
92
- x: Number(event?.nativeEvent?.contentOffset?.x || 0),
93
- });
94
- };
95
- const handleHorizontalLayout = event => {
96
- updateHorizontalMetrics({
97
- viewportWidth: Number(event?.nativeEvent?.layout?.width || 0),
98
- });
99
- };
100
- const handleHorizontalContentSizeChange = width => {
101
- updateHorizontalMetrics({
102
- contentWidth: Number(width || 0),
103
- });
104
- };
105
- const handleListScroll = event => {
106
- if (shouldTriggerEndReachedFromScroll(event)) {
107
- configs.onEndReached?.();
108
- }
109
- };
110
-
111
- const renderTableItem = ({ item: row, index }) => {
112
- const RowComponent = hasRowPress ? TouchableOpacity : View;
113
- const rowStyleValue = typeof rowStyle === 'function' ? rowStyle(row, index) : rowStyle;
114
- const rowPressProps = hasRowPress
115
- ? {
116
- activeOpacity: 0.84,
117
- onPress: () => configs.onRowPress(row),
118
- }
119
- : {};
120
- const rowBackgroundColor = index % 2 === 0 ? tableOddColor : tableEvenColor;
121
- const RowActionsComponent = configs.rowActionsComponent;
122
-
123
- return (
124
- <RowComponent
125
- key={getRowKey(row)}
126
- style={[
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
+ import DefaultTableEmptyState from './DefaultTableEmptyState';
9
+ import DefaultTableInput from './DefaultTableInput';
10
+ import {
11
+ END_REACHED_THRESHOLD,
12
+ getColumnStyle,
13
+ getRowKey,
14
+ getSortField,
15
+ isSortableColumn,
16
+ mergeSortedDataWithLiveItems,
17
+ shouldTriggerEndReachedFromScroll,
18
+ shouldIncludeColumn,
19
+ } from './DefaultTable.utils';
20
+ import styles from './DefaultTable.styles';
21
+ import useDefaultTableTheme from './useDefaultTableTheme';
22
+
23
+ const DefaultTableRows = ({ storeName }) => {
24
+ const store = useStore(storeName);
25
+ const configs = store?.getters?.configs || {};
26
+ const { palette, resolvedAccentColor, tableBorderColors, themeTokens } = useDefaultTableTheme();
27
+ const columns = Array.isArray(store?.getters?.columns) ? store.getters.columns : [];
28
+ const visibleColumns = store?.getters?.visibleColumns || {};
29
+ const tableColumns = columns.filter(
30
+ column => shouldIncludeColumn(column) && visibleColumns[getColumnKey(column)] !== false,
31
+ );
32
+ const sortedData = mergeSortedDataWithLiveItems({
33
+ liveItems: store?.getters?.items,
34
+ sortedData: configs.sortedData,
35
+ });
36
+ const resolvedFilters = store?.getters?.filters || {};
37
+ const isLoading = Boolean(store?.getters?.isLoadingList || store?.getters?.isLoading);
38
+ const emptyStateLabel = isLoading
39
+ ? global.t?.t(storeName, 'label', 'loading')
40
+ : global.t?.t(storeName, 'label', 'empty');
41
+ const tableBorderColor = tableBorderColors.rowBorderColor;
42
+ const tableHeaderBorderColor = tableBorderColors.headerBorderColor || tableBorderColor;
43
+ const tableEvenColor = themeTokens.listItemEvenRow || themeTokens['bg-even-light'] || palette.background;
44
+ const tableHeaderColor = themeTokens['bg-headers-light'] || resolvedAccentColor;
45
+ const tableMutedColor = palette.textSecondary;
46
+ const tableOddColor = themeTokens.listItemOddRow || themeTokens['bg-odd-light'] || palette.background;
47
+ const tableSurfaceColor = palette.background;
48
+ const tableTextColor = palette.text;
49
+ const rowStyle = configs.rowStyle;
50
+ const hasRowPress = typeof configs.onRowPress === 'function';
51
+ const hasCustomRowActions = typeof configs.rowActionsComponent === 'function';
52
+ const hasEditAction = typeof configs.onEditRow === 'function';
53
+ const hasRowActions = configs.showRowActions !== false && (hasCustomRowActions || hasEditAction);
54
+ const shouldPinRowActions = configs.pinRowActions !== false;
55
+ const hasColumnFilters =
56
+ configs.showColumnFiltersButton !== false &&
57
+ configs.tableFiltersVisible === true;
58
+ const actionsCellWidth = hasRowActions ? 96 : 0;
59
+ const [horizontalMetrics, setHorizontalMetrics] = useState({
60
+ contentWidth: 0,
61
+ viewportWidth: 0,
62
+ x: 0,
63
+ });
64
+ const updateHorizontalMetrics = patch => {
65
+ setHorizontalMetrics(current => {
66
+ const next = {
67
+ ...current,
68
+ ...patch,
69
+ };
70
+
71
+ return next.contentWidth === current.contentWidth &&
72
+ next.viewportWidth === current.viewportWidth &&
73
+ next.x === current.x
74
+ ? current
75
+ : next;
76
+ });
77
+ };
78
+ const stickyBodyTransforms = useMemo(() => {
79
+ const viewportWidth = Number(horizontalMetrics.viewportWidth || 0);
80
+ const contentWidth = Number(horizontalMetrics.contentWidth || 0);
81
+ const x = Number(horizontalMetrics.x || 0);
82
+ const canScrollHorizontally = viewportWidth > 0 && contentWidth > viewportWidth;
83
+ const actionsTranslateX = canScrollHorizontally
84
+ ? x + viewportWidth - contentWidth
85
+ : 0;
86
+
87
+ return {
88
+ actions:
89
+ shouldPinRowActions && actionsTranslateX !== 0
90
+ ? [{ translateX: actionsTranslateX }]
91
+ : null,
92
+ identity: x !== 0 ? [{ translateX: x }] : null,
93
+ };
94
+ }, [horizontalMetrics, shouldPinRowActions]);
95
+ const handleHorizontalScroll = event => {
96
+ updateHorizontalMetrics({
97
+ x: Number(event?.nativeEvent?.contentOffset?.x || 0),
98
+ });
99
+ };
100
+ const handleHorizontalLayout = event => {
101
+ updateHorizontalMetrics({
102
+ viewportWidth: Number(event?.nativeEvent?.layout?.width || 0),
103
+ });
104
+ };
105
+ const handleHorizontalContentSizeChange = width => {
106
+ updateHorizontalMetrics({
107
+ contentWidth: Number(width || 0),
108
+ });
109
+ };
110
+ const handleListScroll = event => {
111
+ if (shouldTriggerEndReachedFromScroll(event)) {
112
+ configs.onEndReached?.();
113
+ }
114
+ };
115
+
116
+ const renderTableItem = ({ item: row, index }) => {
117
+ const RowComponent = hasRowPress ? TouchableOpacity : View;
118
+ const rowStyleValue = typeof rowStyle === 'function' ? rowStyle(row, index) : rowStyle;
119
+ const rowPressProps = hasRowPress
120
+ ? {
121
+ activeOpacity: 0.84,
122
+ onPress: () => configs.onRowPress(row),
123
+ }
124
+ : {};
125
+ const rowBackgroundColor = index % 2 === 0 ? tableOddColor : tableEvenColor;
126
+ const RowActionsComponent = configs.rowActionsComponent;
127
+
128
+ return (
129
+ <RowComponent
130
+ key={getRowKey(row)}
131
+ style={[
127
132
  styles.row,
128
- { backgroundColor: rowBackgroundColor, borderBottomColor: tableBorderColor },
129
- rowStyleValue,
130
- ]}
131
- {...rowPressProps}
132
- >
133
- {tableColumns.map(column => (
134
- <React.Fragment key={getColumnKey(column)}>
135
- <DefaultTableInput
136
- column={column}
137
- options={column?.isIdentity ? {
138
- cellStyle: [
139
- styles.pinnedIdentityCell,
140
- {
141
- backgroundColor: rowBackgroundColor,
142
- ...(stickyBodyTransforms.identity
143
- ? { transform: stickyBodyTransforms.identity }
144
- : {}),
145
- },
146
- ],
147
- } : {}}
148
- row={row}
149
- storeName={storeName}
150
- variant="cell"
151
- />
152
- </React.Fragment>
153
- ))}
154
- {hasRowActions ? (
133
+ {
134
+ backgroundColor: rowBackgroundColor,
135
+ borderBottomColor: tableBorderColor,
136
+ borderBottomWidth: tableBorderColor ? 1 : 0,
137
+ },
138
+ rowStyleValue,
139
+ ]}
140
+ {...rowPressProps}
141
+ >
142
+ {tableColumns.map(column => (
143
+ <React.Fragment key={getColumnKey(column)}>
144
+ <DefaultTableInput
145
+ column={column}
146
+ options={column?.isIdentity ? {
147
+ cellStyle: [
148
+ styles.pinnedIdentityCell,
149
+ {
150
+ backgroundColor: rowBackgroundColor,
151
+ ...(stickyBodyTransforms.identity
152
+ ? { transform: stickyBodyTransforms.identity }
153
+ : {}),
154
+ },
155
+ ],
156
+ } : {}}
157
+ row={row}
158
+ storeName={storeName}
159
+ variant="cell"
160
+ />
161
+ </React.Fragment>
162
+ ))}
163
+ {hasRowActions ? (
164
+ <View
165
+ style={[
166
+ styles.cell,
167
+ styles.actionsCell,
168
+ shouldPinRowActions ? styles.pinnedActionsCell : null,
169
+ {
170
+ minWidth: actionsCellWidth,
171
+ width: actionsCellWidth,
172
+ flexBasis: actionsCellWidth,
173
+ maxWidth: actionsCellWidth,
174
+ backgroundColor: rowBackgroundColor,
175
+ ...(stickyBodyTransforms.actions
176
+ ? { transform: stickyBodyTransforms.actions }
177
+ : {}),
178
+ },
179
+ ]}
180
+ >
181
+ <View style={styles.rowActionsGroup}>
182
+ {hasCustomRowActions ? (
183
+ <RowActionsComponent
184
+ openEdit={() => configs.onEditRow?.(row)}
185
+ openRow={hasRowPress ? () => configs.onRowPress(row) : null}
186
+ row={row}
187
+ />
188
+ ) : null}
189
+ {hasEditAction ? (
190
+ <TouchableOpacity
191
+ style={[styles.iconButton, { borderColor: tableBorderColor, backgroundColor: tableSurfaceColor }]}
192
+ activeOpacity={0.82}
193
+ onPress={() => configs.onEditRow?.(row)}
194
+ >
195
+ <Icon name="edit-2" size={14} color={tableMutedColor} />
196
+ </TouchableOpacity>
197
+ ) : null}
198
+ </View>
199
+ </View>
200
+ ) : null}
201
+ </RowComponent>
202
+ );
203
+ };
204
+
205
+ return (
206
+ <>
207
+ <ScrollView
208
+ horizontal
209
+ onContentSizeChange={handleHorizontalContentSizeChange}
210
+ onLayout={handleHorizontalLayout}
211
+ onScroll={handleHorizontalScroll}
212
+ scrollEventThrottle={16}
213
+ style={styles.scroll}
214
+ contentContainerStyle={styles.horizontalScrollContent}
215
+ >
216
+ <View style={styles.content}>
155
217
  <View
156
218
  style={[
157
- styles.cell,
158
- styles.actionsCell,
159
- styles.pinnedActionsCell,
219
+ styles.headerRow,
160
220
  {
161
- minWidth: actionsCellWidth,
162
- width: actionsCellWidth,
163
- flexBasis: actionsCellWidth,
164
- maxWidth: actionsCellWidth,
165
- backgroundColor: rowBackgroundColor,
166
- ...(stickyBodyTransforms.actions
167
- ? { transform: stickyBodyTransforms.actions }
168
- : {}),
221
+ backgroundColor: tableHeaderColor,
222
+ borderBottomColor: tableHeaderBorderColor,
223
+ borderBottomWidth: tableHeaderBorderColor ? 1 : 0,
169
224
  },
170
225
  ]}
171
226
  >
172
- <View style={styles.rowActionsGroup}>
173
- {hasCustomRowActions ? (
174
- <RowActionsComponent
175
- openEdit={() => configs.onEditRow?.(row)}
176
- openRow={hasRowPress ? () => configs.onRowPress(row) : null}
177
- row={row}
178
- />
179
- ) : null}
180
- {hasEditAction ? (
181
- <TouchableOpacity
182
- style={[styles.iconButton, { borderColor: tableBorderColor, backgroundColor: tableSurfaceColor }]}
183
- activeOpacity={0.82}
184
- onPress={() => configs.onEditRow?.(row)}
185
- >
186
- <Icon name="edit-2" size={14} color={tableMutedColor} />
187
- </TouchableOpacity>
188
- ) : null}
189
- </View>
190
- </View>
191
- ) : null}
192
- </RowComponent>
193
- );
194
- };
195
-
196
- return (
197
- <>
198
- <ScrollView
199
- horizontal
200
- onContentSizeChange={handleHorizontalContentSizeChange}
201
- onLayout={handleHorizontalLayout}
202
- onScroll={handleHorizontalScroll}
203
- scrollEventThrottle={16}
204
- style={styles.scroll}
205
- contentContainerStyle={styles.horizontalScrollContent}
206
- >
207
- <View style={styles.content}>
208
- <View style={[styles.headerRow, { backgroundColor: tableHeaderColor, borderBottomColor: tableBorderColor }]}>
209
- {tableColumns.map(column => {
210
- const fieldName = getColumnKey(column);
211
- const label = formatStoreColumnLabel({
212
- columns,
213
- fieldName,
214
- fallbackLabel: column?.label || fieldName,
215
- storeName,
216
- });
217
- const sortFieldName = getSortField(column);
218
-
219
- return (
220
- <TouchableOpacity
221
- key={fieldName}
222
- style={[
223
- getColumnStyle(column),
224
- column?.isIdentity
225
- ? [styles.stickyIdentityCell, styles.stickyHeaderCell, { backgroundColor: tableHeaderColor }]
226
- : null,
227
- ]}
228
- activeOpacity={isSortableColumn(column) ? 0.8 : 1}
229
- onPress={() => configs.requestSort?.(column)}
230
- >
231
- <View style={styles.sortableHeader}>
232
- <Text style={[styles.headerText, { color: tableTextColor }]} numberOfLines={1}>{label}</Text>
233
- {isSortableColumn(column) && configs.resolvedSort?.field === sortFieldName ? (
234
- <Icon name={configs.resolvedSort?.direction === 'desc' ? 'chevron-down' : 'chevron-up'} size={12} color={tableTextColor} />
235
- ) : isSortableColumn(column) ? (
236
- <Icon name="chevrons-up" size={12} color={tableBorderColor} />
237
- ) : null}
238
- {resolvedFilters?.[fieldName] ? <Icon name="filter" size={11} color={tableTextColor} /> : null}
239
- </View>
240
- </TouchableOpacity>
241
- );
242
- })}
243
- {hasRowActions ? (
244
- <View
245
- style={[
246
- styles.cell,
247
- styles.actionsCell,
248
- styles.stickyActionsCell,
249
- styles.stickyHeaderCell,
250
- {
251
- minWidth: actionsCellWidth,
252
- width: actionsCellWidth,
253
- flexBasis: actionsCellWidth,
254
- maxWidth: actionsCellWidth,
255
- backgroundColor: tableHeaderColor,
256
- },
257
- ]}
258
- >
259
- <Text style={[styles.headerText, { color: tableTextColor }]}>
260
- {global.t?.t(storeName, 'label', 'actions')}
261
- </Text>
262
- </View>
263
- ) : null}
264
- </View>
265
- {hasColumnFilters ? (
266
- <View style={[styles.filterRow, { backgroundColor: tableHeaderColor, borderBottomColor: tableBorderColor }]}>
267
- {tableColumns.map(column => {
268
- const fieldName = getColumnKey(column);
269
-
270
- return (
271
- <View
272
- key={`${fieldName}-filter`}
273
- style={[
274
- getColumnStyle(column),
275
- column?.isIdentity
276
- ? [styles.stickyIdentityCell, { backgroundColor: tableHeaderColor }]
277
- : null,
278
- ]}
279
- >
280
- {column?.filter !== false && column?.filters !== false ? (
281
- <DefaultColumnFilter
282
- column={column}
283
- filters={resolvedFilters}
284
- storeName={storeName}
285
- style={styles.filterCell}
286
- />
287
- ) : null}
288
- </View>
289
- );
290
- })}
291
- {hasRowActions ? (
292
- <View
293
- style={[
294
- styles.cell,
295
- styles.actionsCell,
296
- styles.stickyActionsCell,
297
- {
298
- minWidth: actionsCellWidth,
299
- width: actionsCellWidth,
300
- flexBasis: actionsCellWidth,
301
- maxWidth: actionsCellWidth,
302
- backgroundColor: tableHeaderColor,
303
- },
304
- ]}
305
- />
306
- ) : null}
307
- </View>
308
- ) : null}
309
-
310
- <FlatList
311
- data={sortedData}
312
- keyExtractor={getRowKey}
313
- renderItem={renderTableItem}
314
- style={styles.tableList}
315
- contentContainerStyle={styles.tableListContent}
316
- ListEmptyComponent={(
317
- <DefaultTableEmptyState
318
- emptyStateLabel={emptyStateLabel}
319
- isLoading={isLoading}
320
- isTable
321
- tableMutedColor={tableMutedColor}
322
- />
323
- )}
324
- ListFooterComponent={null}
325
- nestedScrollEnabled
326
- onMomentumScrollBegin={configs.onMomentumScrollBegin || undefined}
327
- onScroll={handleListScroll}
328
- onScrollBeginDrag={configs.onScrollBeginDrag || undefined}
329
- onEndReached={configs.onEndReached}
330
- onEndReachedThreshold={END_REACHED_THRESHOLD}
331
- scrollEventThrottle={120}
332
- showsVerticalScrollIndicator={false}
333
- />
334
- </View>
335
- </ScrollView>
336
- </>
337
- );
338
- };
339
-
340
- export default DefaultTableRows;
227
+ {tableColumns.map(column => {
228
+ const fieldName = getColumnKey(column);
229
+ const label = formatStoreColumnLabel({
230
+ columns,
231
+ fieldName,
232
+ fallbackLabel: column?.label || fieldName,
233
+ storeName,
234
+ });
235
+ const sortFieldName = getSortField(column);
236
+
237
+ return (
238
+ <TouchableOpacity
239
+ key={fieldName}
240
+ style={[
241
+ getColumnStyle(column),
242
+ column?.isIdentity
243
+ ? [styles.stickyIdentityCell, styles.stickyHeaderCell, { backgroundColor: tableHeaderColor }]
244
+ : null,
245
+ ]}
246
+ activeOpacity={isSortableColumn(column) ? 0.8 : 1}
247
+ onPress={() => configs.requestSort?.(column)}
248
+ >
249
+ <View style={styles.sortableHeader}>
250
+ <Text style={[styles.headerText, { color: tableTextColor }]} numberOfLines={1}>{label}</Text>
251
+ {isSortableColumn(column) && configs.resolvedSort?.field === sortFieldName ? (
252
+ <Icon name={configs.resolvedSort?.direction === 'desc' ? 'chevron-down' : 'chevron-up'} size={12} color={tableTextColor} />
253
+ ) : isSortableColumn(column) ? (
254
+ <Icon name="chevrons-up" size={12} color={tableBorderColor} />
255
+ ) : null}
256
+ {resolvedFilters?.[fieldName] ? <Icon name="filter" size={11} color={tableTextColor} /> : null}
257
+ </View>
258
+ </TouchableOpacity>
259
+ );
260
+ })}
261
+ {hasRowActions ? (
262
+ <View
263
+ style={[
264
+ styles.cell,
265
+ styles.actionsCell,
266
+ shouldPinRowActions ? styles.stickyActionsCell : null,
267
+ shouldPinRowActions ? styles.stickyHeaderCell : null,
268
+ {
269
+ minWidth: actionsCellWidth,
270
+ width: actionsCellWidth,
271
+ flexBasis: actionsCellWidth,
272
+ maxWidth: actionsCellWidth,
273
+ backgroundColor: tableHeaderColor,
274
+ },
275
+ ]}
276
+ >
277
+ <Text style={[styles.headerText, { color: tableTextColor }]}>
278
+ {global.t?.t(storeName, 'label', 'actions')}
279
+ </Text>
280
+ </View>
281
+ ) : null}
282
+ </View>
283
+ {hasColumnFilters ? (
284
+ <View
285
+ style={[
286
+ styles.filterRow,
287
+ {
288
+ backgroundColor: tableHeaderColor,
289
+ borderBottomColor: tableHeaderBorderColor,
290
+ borderBottomWidth: tableHeaderBorderColor ? 1 : 0,
291
+ },
292
+ ]}
293
+ >
294
+ {tableColumns.map(column => {
295
+ const fieldName = getColumnKey(column);
296
+
297
+ return (
298
+ <View
299
+ key={`${fieldName}-filter`}
300
+ style={[
301
+ getColumnStyle(column),
302
+ column?.isIdentity
303
+ ? [styles.stickyIdentityCell, { backgroundColor: tableHeaderColor }]
304
+ : null,
305
+ ]}
306
+ >
307
+ {column?.filter !== false && column?.filters !== false ? (
308
+ <DefaultColumnFilter
309
+ column={column}
310
+ filters={resolvedFilters}
311
+ storeName={storeName}
312
+ style={styles.filterCell}
313
+ />
314
+ ) : null}
315
+ </View>
316
+ );
317
+ })}
318
+ {hasRowActions ? (
319
+ <View
320
+ style={[
321
+ styles.cell,
322
+ styles.actionsCell,
323
+ shouldPinRowActions ? styles.stickyActionsCell : null,
324
+ {
325
+ minWidth: actionsCellWidth,
326
+ width: actionsCellWidth,
327
+ flexBasis: actionsCellWidth,
328
+ maxWidth: actionsCellWidth,
329
+ backgroundColor: tableHeaderColor,
330
+ },
331
+ ]}
332
+ />
333
+ ) : null}
334
+ </View>
335
+ ) : null}
336
+
337
+ <FlatList
338
+ data={sortedData}
339
+ keyExtractor={getRowKey}
340
+ renderItem={renderTableItem}
341
+ style={styles.tableList}
342
+ contentContainerStyle={styles.tableListContent}
343
+ ListEmptyComponent={(
344
+ <DefaultTableEmptyState
345
+ emptyStateLabel={emptyStateLabel}
346
+ isLoading={isLoading}
347
+ isTable
348
+ tableMutedColor={tableMutedColor}
349
+ />
350
+ )}
351
+ ListFooterComponent={null}
352
+ nestedScrollEnabled
353
+ onMomentumScrollBegin={configs.onMomentumScrollBegin || undefined}
354
+ onScroll={handleListScroll}
355
+ onScrollBeginDrag={configs.onScrollBeginDrag || undefined}
356
+ onEndReached={configs.onEndReached}
357
+ onEndReachedThreshold={END_REACHED_THRESHOLD}
358
+ scrollEventThrottle={120}
359
+ showsVerticalScrollIndicator={false}
360
+ />
361
+ </View>
362
+ </ScrollView>
363
+ </>
364
+ );
365
+ };
366
+
367
+ export default DefaultTableRows;