@controleonline/ui-default 1.0.263 → 1.0.265
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +47 -45
- package/package.json +20 -20
- package/src/react/components/errors/DefaultErrors.js +307 -325
- package/src/react/components/files/DefaultFile.js +46 -46
- package/src/react/components/filters/CompactFilterSelector.js +78 -24
- package/src/react/components/filters/CompactFilterSelector.styles.js +17 -5
- package/src/react/components/filters/DefaultColumnFilter.js +191 -57
- package/src/react/components/filters/DefaultExternalFilters.js +245 -182
- package/src/react/components/filters/DefaultSearch.js +131 -92
- package/src/react/components/filters/DefaultSearch.styles.js +65 -70
- package/src/react/components/form/DefaultForm.js +172 -151
- package/src/react/components/form/DefaultForm.styles.js +71 -66
- package/src/react/components/help/DefaultTooltip.js +87 -87
- package/src/react/components/help/DefaultTooltip.styles.js +61 -61
- package/src/react/components/inputs/DefaultDateInput.js +150 -0
- package/src/react/components/inputs/DefaultInput.js +162 -127
- package/src/react/components/inputs/DefaultInput.styles.js +6 -0
- package/src/react/components/inputs/DefaultSelect.js +320 -152
- package/src/react/components/inputs/defaultInputUtils.js +40 -19
- package/src/react/components/map/DefaultGoogleMap.styles.js +115 -9
- package/src/react/components/map/DefaultGoogleMap.web.js +560 -446
- package/src/react/components/map/DefaultMap.native.js +73 -71
- package/src/react/components/map/DefaultMap.styles.js +16 -16
- package/src/react/components/map/DefaultMap.web.js +68 -66
- package/src/react/components/map/DefaultNativeMap.native.js +307 -183
- package/src/react/components/map/DefaultNativeMap.shared.js +439 -340
- package/src/react/components/map/DefaultNativeMap.styles.js +156 -122
- package/src/react/components/table/DefaultColumnMenu.js +108 -0
- package/src/react/components/table/DefaultDebug.js +233 -0
- package/src/react/components/table/DefaultFiltersModal.js +142 -0
- package/src/react/components/table/DefaultModalButton.js +18 -0
- package/src/react/components/table/DefaultSearchModal.js +49 -0
- package/src/react/components/table/DefaultTable.js +345 -1846
- package/src/react/components/table/DefaultTable.styles.js +247 -136
- package/src/react/components/table/DefaultTable.utils.js +502 -0
- package/src/react/components/table/DefaultTableBody.js +26 -0
- package/src/react/components/table/DefaultTableCards.js +171 -0
- package/src/react/components/table/DefaultTableControls.js +208 -0
- package/src/react/components/table/DefaultTableEmptyState.js +22 -0
- package/src/react/components/table/DefaultTableFooter.js +172 -0
- package/src/react/components/table/DefaultTableInput.js +145 -0
- package/src/react/components/table/DefaultTableRows.js +340 -0
- package/src/react/components/table/DefaultTableSearch.js +100 -0
- package/src/react/components/table/DefaultTableToolbar.js +98 -0
- package/src/react/components/table/DefaultToolbarAction.js +56 -0
- package/src/react/components/table/useDefaultTablePagination.js +370 -0
- package/src/react/components/table/useDefaultTableSorting.js +126 -0
- package/src/react/components/table/useDefaultTableTheme.js +58 -0
- package/src/react/index.js +10 -10
- package/src/react/utils/tableVisibleColumnsPreferences.js +239 -211
- package/src/store/default/actions.js +51 -13
- package/src/store/default/getters.js +10 -8
- package/src/store/default/mutation_types.js +4 -2
- package/src/store/default/mutations.js +10 -1
- package/src/tests/react/components/errors/DefaultErrors.test.js +162 -162
- package/src/tests/react/components/files/DefaultFile.test.js +80 -80
- package/src/tests/react/components/form/DefaultForm.test.js +81 -0
- package/src/tests/react/components/inputs/DefaultSelect.test.js +185 -0
- package/src/tests/react/components/table/DefaultTable.test.js +1183 -0
- package/src/tests/react/store/defaultActions.test.js +59 -0
- package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +247 -168
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import React, {memo, useMemo} from 'react';
|
|
2
|
-
import {Image} from 'react-native';
|
|
3
|
-
import {useStore} from '@store';
|
|
4
|
-
import {resolveDefaultFileSource} from '@controleonline/ui-common/src/react/utils/fileUrl';
|
|
5
|
-
|
|
6
|
-
const resolveCompanyFromStore = peopleGetters => {
|
|
7
|
-
if (peopleGetters?.defaultCompany?.id) {
|
|
8
|
-
return peopleGetters.defaultCompany;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
if (peopleGetters?.currentCompany?.id) {
|
|
12
|
-
return peopleGetters.currentCompany;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return null;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const DefaultFile = ({
|
|
19
|
-
file,
|
|
20
|
-
source,
|
|
21
|
-
company,
|
|
22
|
-
appDomain = '',
|
|
23
|
-
headers = {},
|
|
24
|
-
...imageProps
|
|
25
|
-
}) => {
|
|
26
|
-
const peopleStore = useStore('people');
|
|
27
|
-
const peopleGetters = peopleStore?.getters || {};
|
|
28
|
-
const resolvedCompany = company || resolveCompanyFromStore(peopleGetters);
|
|
29
|
-
const resolvedSource = useMemo(
|
|
30
|
-
() =>
|
|
31
|
-
resolveDefaultFileSource(source ?? file, {
|
|
32
|
-
company: resolvedCompany,
|
|
33
|
-
appDomain,
|
|
34
|
-
headers,
|
|
35
|
-
}),
|
|
36
|
-
[appDomain, company, file, headers, resolvedCompany, source],
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
if (!resolvedSource) {
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return <Image {...imageProps} source={resolvedSource} />;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export default memo(DefaultFile);
|
|
1
|
+
import React, {memo, useMemo} from 'react';
|
|
2
|
+
import {Image} from 'react-native';
|
|
3
|
+
import {useStore} from '@store';
|
|
4
|
+
import {resolveDefaultFileSource} from '@controleonline/ui-common/src/react/utils/fileUrl';
|
|
5
|
+
|
|
6
|
+
const resolveCompanyFromStore = peopleGetters => {
|
|
7
|
+
if (peopleGetters?.defaultCompany?.id) {
|
|
8
|
+
return peopleGetters.defaultCompany;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (peopleGetters?.currentCompany?.id) {
|
|
12
|
+
return peopleGetters.currentCompany;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const DefaultFile = ({
|
|
19
|
+
file,
|
|
20
|
+
source,
|
|
21
|
+
company,
|
|
22
|
+
appDomain = '',
|
|
23
|
+
headers = {},
|
|
24
|
+
...imageProps
|
|
25
|
+
}) => {
|
|
26
|
+
const peopleStore = useStore('people');
|
|
27
|
+
const peopleGetters = peopleStore?.getters || {};
|
|
28
|
+
const resolvedCompany = company || resolveCompanyFromStore(peopleGetters);
|
|
29
|
+
const resolvedSource = useMemo(
|
|
30
|
+
() =>
|
|
31
|
+
resolveDefaultFileSource(source ?? file, {
|
|
32
|
+
company: resolvedCompany,
|
|
33
|
+
appDomain,
|
|
34
|
+
headers,
|
|
35
|
+
}),
|
|
36
|
+
[appDomain, company, file, headers, resolvedCompany, source],
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
if (!resolvedSource) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return <Image {...imageProps} source={resolvedSource} />;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default memo(DefaultFile);
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React, { useCallback, useMemo, useState } from 'react';
|
|
2
2
|
import {
|
|
3
|
-
Modal,
|
|
4
|
-
ScrollView,
|
|
5
|
-
Text,
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
Modal,
|
|
4
|
+
ScrollView,
|
|
5
|
+
Text,
|
|
6
|
+
TextInput,
|
|
7
|
+
TouchableOpacity,
|
|
8
|
+
TouchableWithoutFeedback,
|
|
8
9
|
useWindowDimensions,
|
|
9
10
|
View,
|
|
10
11
|
} from 'react-native';
|
|
@@ -88,16 +89,20 @@ const CompactFilterSelector = ({
|
|
|
88
89
|
icon = 'sliders',
|
|
89
90
|
label = '',
|
|
90
91
|
labelCaption = '',
|
|
91
|
-
onClose = null,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
onClose = null,
|
|
93
|
+
onBeforeOpen = null,
|
|
94
|
+
onSearchChange = null,
|
|
95
|
+
onSelect = null,
|
|
96
|
+
options = [],
|
|
97
|
+
searchable = false,
|
|
98
|
+
selectedKey = '',
|
|
95
99
|
field = '',
|
|
96
100
|
store = '',
|
|
97
101
|
themeColors = {},
|
|
98
102
|
title = '',
|
|
99
103
|
}) => {
|
|
100
|
-
const [visible, setVisible] = useState(false);
|
|
104
|
+
const [visible, setVisible] = useState(false);
|
|
105
|
+
const [searchText, setSearchText] = useState('');
|
|
101
106
|
const { width: windowWidth } = useWindowDimensions();
|
|
102
107
|
const resolvedTheme = useMemo(
|
|
103
108
|
() => buildTheme({ accentColor, themeColors }),
|
|
@@ -115,16 +120,56 @@ const CompactFilterSelector = ({
|
|
|
115
120
|
const resolvedLabelCaption = labelCaption || storeFieldLabel;
|
|
116
121
|
const resolvedTitle = title || storeFieldLabel;
|
|
117
122
|
|
|
118
|
-
const closeModal = useCallback(() => {
|
|
119
|
-
setVisible(false);
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
const closeModal = useCallback(() => {
|
|
124
|
+
setVisible(false);
|
|
125
|
+
setSearchText('');
|
|
126
|
+
onClose?.();
|
|
127
|
+
}, [onClose]);
|
|
122
128
|
|
|
123
|
-
const openModal = useCallback(() => {
|
|
124
|
-
if (
|
|
125
|
-
|
|
129
|
+
const openModal = useCallback(() => {
|
|
130
|
+
if (disabled) {
|
|
131
|
+
return;
|
|
126
132
|
}
|
|
127
|
-
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const maybePromise = onBeforeOpen?.();
|
|
136
|
+
if (maybePromise && typeof maybePromise.then === 'function') {
|
|
137
|
+
maybePromise
|
|
138
|
+
.then(() => {
|
|
139
|
+
setVisible(true);
|
|
140
|
+
})
|
|
141
|
+
.catch(() => {
|
|
142
|
+
// Keep the modal closed if the lazy load fails.
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
setVisible(true);
|
|
151
|
+
}, [disabled, onBeforeOpen]);
|
|
152
|
+
|
|
153
|
+
/*
|
|
154
|
+
* @agents
|
|
155
|
+
* List filters must keep the column/store contract in the default component:
|
|
156
|
+
* open loads the initial page, typing delegates autocomplete to the caller
|
|
157
|
+
* that knows the column list store/action.
|
|
158
|
+
*/
|
|
159
|
+
const handleSearchChange = useCallback(value => {
|
|
160
|
+
setSearchText(value);
|
|
161
|
+
onSearchChange?.(value);
|
|
162
|
+
}, [onSearchChange]);
|
|
163
|
+
|
|
164
|
+
const filteredOptions = useMemo(() => {
|
|
165
|
+
const normalizedSearch = normalizeText(searchText).toLowerCase();
|
|
166
|
+
if (!searchable || onSearchChange || !normalizedSearch) return options;
|
|
167
|
+
|
|
168
|
+
return options.filter(option =>
|
|
169
|
+
normalizeText(option?.label).toLowerCase().includes(normalizedSearch) ||
|
|
170
|
+
normalizeText(option?.key).toLowerCase().includes(normalizedSearch),
|
|
171
|
+
);
|
|
172
|
+
}, [onSearchChange, options, searchable, searchText]);
|
|
128
173
|
|
|
129
174
|
const handleSelect = useCallback(optionKey => {
|
|
130
175
|
const shouldClose = onSelect?.(optionKey, {
|
|
@@ -210,12 +255,21 @@ const CompactFilterSelector = ({
|
|
|
210
255
|
</TouchableOpacity>
|
|
211
256
|
</View>
|
|
212
257
|
|
|
213
|
-
<ScrollView
|
|
214
|
-
style={styles.modalScroll}
|
|
215
|
-
contentContainerStyle={styles.modalContent}
|
|
216
|
-
keyboardShouldPersistTaps="handled"
|
|
217
|
-
>
|
|
218
|
-
{
|
|
258
|
+
<ScrollView
|
|
259
|
+
style={styles.modalScroll}
|
|
260
|
+
contentContainerStyle={styles.modalContent}
|
|
261
|
+
keyboardShouldPersistTaps="handled"
|
|
262
|
+
>
|
|
263
|
+
{searchable ? (
|
|
264
|
+
<TextInput
|
|
265
|
+
style={styles.searchInput}
|
|
266
|
+
value={searchText}
|
|
267
|
+
placeholder={global.t?.t(resolveStoreName(store), 'input', 'search')}
|
|
268
|
+
onChangeText={handleSearchChange}
|
|
269
|
+
/>
|
|
270
|
+
) : null}
|
|
271
|
+
|
|
272
|
+
{filteredOptions.map(option => {
|
|
219
273
|
const isSelected = option.key === selectedKey;
|
|
220
274
|
|
|
221
275
|
return (
|
|
@@ -88,11 +88,23 @@ const createStyles = (theme, dense = false, windowWidth = 0) => {
|
|
|
88
88
|
modalScroll: {
|
|
89
89
|
flexGrow: 0,
|
|
90
90
|
},
|
|
91
|
-
modalContent: {
|
|
92
|
-
paddingBottom: 10,
|
|
93
|
-
gap: 10,
|
|
94
|
-
},
|
|
95
|
-
|
|
91
|
+
modalContent: {
|
|
92
|
+
paddingBottom: 10,
|
|
93
|
+
gap: 10,
|
|
94
|
+
},
|
|
95
|
+
searchInput: {
|
|
96
|
+
minHeight: 42,
|
|
97
|
+
borderWidth: 1,
|
|
98
|
+
borderColor: theme.optionBorderColor,
|
|
99
|
+
borderRadius: 14,
|
|
100
|
+
paddingHorizontal: 12,
|
|
101
|
+
paddingVertical: 8,
|
|
102
|
+
backgroundColor: theme.backgroundColor,
|
|
103
|
+
color: theme.textColor,
|
|
104
|
+
fontSize: 13,
|
|
105
|
+
fontWeight: '700',
|
|
106
|
+
},
|
|
107
|
+
modalOption: {
|
|
96
108
|
minHeight: 50,
|
|
97
109
|
borderWidth: 1,
|
|
98
110
|
borderColor: theme.optionBorderColor,
|
|
@@ -1,61 +1,192 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { TextInput, View } from 'react-native';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import { TextInput, View } from 'react-native';
|
|
3
|
+
import { getAllStores, useStore } from '@store';
|
|
4
|
+
import {
|
|
5
|
+
buildOptionsFromColumn,
|
|
6
|
+
getColumnKey,
|
|
7
|
+
isDateLikeColumn,
|
|
8
|
+
mapOptions,
|
|
9
|
+
normalizeOptionKey,
|
|
10
|
+
normalizeText,
|
|
11
|
+
resolveStoreNameFromList,
|
|
12
|
+
} from '../inputs/defaultInputUtils';
|
|
13
|
+
import CompactFilterSelector from './CompactFilterSelector';
|
|
14
|
+
import DateShortcutFilter from './DateShortcutFilter';
|
|
15
|
+
import { resolveNextDateFilterValue } from './dateFilterSelection';
|
|
16
|
+
import {
|
|
17
|
+
normalizeCollectionItems,
|
|
18
|
+
resolveColumnListLoadParams,
|
|
19
|
+
resolveListActionName,
|
|
20
|
+
stableSerialize,
|
|
21
|
+
} from '../table/DefaultTable.utils';
|
|
22
|
+
import {
|
|
23
|
+
persistTableFiltersPreference,
|
|
24
|
+
resolveDefaultTablePreferenceScope,
|
|
25
|
+
sanitizeTableFiltersPreference,
|
|
26
|
+
} from '../../utils/tableVisibleColumnsPreferences';
|
|
27
|
+
import styles from './DefaultColumnFilter.styles';
|
|
13
28
|
|
|
14
29
|
const DefaultColumnFilter = ({
|
|
15
30
|
accentColor = '#2563EB',
|
|
16
|
-
column,
|
|
17
|
-
filters = {},
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
31
|
+
column,
|
|
32
|
+
filters = {},
|
|
33
|
+
onBeforeOpen = null,
|
|
34
|
+
onSearchChange = null,
|
|
35
|
+
getOptionsForColumn = null,
|
|
36
|
+
onChange = null,
|
|
37
|
+
storeName = '',
|
|
21
38
|
style = null,
|
|
22
|
-
}) => {
|
|
23
|
-
const
|
|
39
|
+
}) => {
|
|
40
|
+
const store = useStore(storeName);
|
|
41
|
+
const peopleStore = useStore('people');
|
|
42
|
+
const configs = store?.getters?.configs || {};
|
|
43
|
+
const [loadedItems, setLoadedItems] = useState([]);
|
|
44
|
+
const [loadedKey, setLoadedKey] = useState('');
|
|
45
|
+
const fieldName = getColumnKey(column);
|
|
46
|
+
const currentCompanyId = peopleStore?.getters?.currentCompany?.id;
|
|
47
|
+
const requestChange = useCallback(
|
|
48
|
+
(nextFieldName, value) => {
|
|
49
|
+
if (typeof onChange === 'function') {
|
|
50
|
+
onChange(nextFieldName, value);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const currentFilters = store?.getters?.filters || {};
|
|
55
|
+
const nextFilters = { ...(currentFilters || {}) };
|
|
56
|
+
const isEmpty =
|
|
57
|
+
value === null ||
|
|
58
|
+
value === undefined ||
|
|
59
|
+
normalizeText(value) === '' ||
|
|
60
|
+
(Array.isArray(value) && value.length === 0);
|
|
61
|
+
|
|
62
|
+
if (isEmpty) delete nextFilters[nextFieldName];
|
|
63
|
+
else nextFilters[nextFieldName] = value;
|
|
64
|
+
|
|
65
|
+
const columns = Array.isArray(store?.getters?.columns) ? store.getters.columns : [];
|
|
66
|
+
const tablePreferenceScope =
|
|
67
|
+
configs.tablePreferenceScope ||
|
|
68
|
+
resolveDefaultTablePreferenceScope({ storeName });
|
|
69
|
+
const persistedFilters = sanitizeTableFiltersPreference({
|
|
70
|
+
columns,
|
|
71
|
+
filters: nextFilters,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
persistTableFiltersPreference(tablePreferenceScope, persistedFilters);
|
|
75
|
+
|
|
76
|
+
if (typeof store?.actions?.setFilters === 'function') {
|
|
77
|
+
store.actions.setFilters(nextFilters);
|
|
78
|
+
} else if (store?.getters) {
|
|
79
|
+
store.getters.filters = nextFilters;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
configs.onFilterChange?.(nextFilters);
|
|
83
|
+
},
|
|
84
|
+
[configs, onChange, store, storeName],
|
|
85
|
+
);
|
|
86
|
+
const resolveOptions = useCallback(
|
|
87
|
+
targetColumn => {
|
|
88
|
+
const configOptions = configs.getOptionsForColumn?.(targetColumn);
|
|
89
|
+
const propOptions = getOptionsForColumn?.(targetColumn);
|
|
90
|
+
|
|
91
|
+
if (Array.isArray(configOptions)) return configOptions;
|
|
92
|
+
if (Array.isArray(propOptions)) return propOptions;
|
|
93
|
+
if (targetColumn === column && loadedItems.length > 0) return loadedItems;
|
|
94
|
+
|
|
95
|
+
return undefined;
|
|
96
|
+
},
|
|
97
|
+
[column, configs, getOptionsForColumn, loadedItems],
|
|
98
|
+
);
|
|
99
|
+
const loadListOptions = useCallback(
|
|
100
|
+
(searchValue = '') => {
|
|
101
|
+
const listStoreName = resolveStoreNameFromList(column?.list);
|
|
102
|
+
const actionName = resolveListActionName(column?.list);
|
|
103
|
+
const listStore = getAllStores?.()?.[listStoreName];
|
|
104
|
+
const listAction = listStore?.actions?.[actionName];
|
|
105
|
+
|
|
106
|
+
if (!listStoreName || typeof listAction !== 'function') {
|
|
107
|
+
return Promise.resolve([]);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const listLoadParams = resolveColumnListLoadParams({
|
|
111
|
+
column,
|
|
112
|
+
currentCompanyId,
|
|
113
|
+
requestParams: configs.requestParams || {},
|
|
114
|
+
searchValue,
|
|
115
|
+
});
|
|
116
|
+
const nextLoadedKey = stableSerialize({
|
|
117
|
+
actionName,
|
|
118
|
+
listLoadParams,
|
|
119
|
+
listStoreName,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (!normalizeText(searchValue) && loadedKey === nextLoadedKey && loadedItems.length > 0) {
|
|
123
|
+
return Promise.resolve(loadedItems);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return Promise.resolve(
|
|
127
|
+
listAction({
|
|
128
|
+
...listLoadParams,
|
|
129
|
+
__storeMeta: {
|
|
130
|
+
dedupeKey: `default-column-filter-list:${fieldName}:${nextLoadedKey}`,
|
|
131
|
+
skipSystemError: true,
|
|
132
|
+
},
|
|
133
|
+
}),
|
|
134
|
+
)
|
|
135
|
+
.then(response => {
|
|
136
|
+
const items = normalizeCollectionItems(response);
|
|
137
|
+
setLoadedItems(items);
|
|
138
|
+
setLoadedKey(nextLoadedKey);
|
|
139
|
+
return items;
|
|
140
|
+
})
|
|
141
|
+
.catch(() => []);
|
|
142
|
+
},
|
|
143
|
+
[
|
|
144
|
+
column,
|
|
145
|
+
configs.requestParams,
|
|
146
|
+
currentCompanyId,
|
|
147
|
+
fieldName,
|
|
148
|
+
loadedItems,
|
|
149
|
+
loadedKey,
|
|
150
|
+
],
|
|
151
|
+
);
|
|
24
152
|
|
|
25
153
|
if (column?.filter === false || column?.filters === false) {
|
|
26
154
|
return <View style={style} />;
|
|
27
155
|
}
|
|
28
156
|
|
|
29
|
-
if (column
|
|
157
|
+
if (isDateLikeColumn(column)) {
|
|
30
158
|
const filterValue = filters?.[fieldName] || {};
|
|
31
159
|
return (
|
|
32
160
|
<View style={[style, styles.filterCell]}>
|
|
33
|
-
<DateShortcutFilter
|
|
34
|
-
dense
|
|
35
|
-
store={storeName}
|
|
36
|
-
field={fieldName}
|
|
37
|
-
|
|
161
|
+
<DateShortcutFilter
|
|
162
|
+
dense
|
|
163
|
+
store={storeName}
|
|
164
|
+
field={fieldName}
|
|
165
|
+
labelCaption={global.t?.t(storeName, 'label', column?.label || fieldName)}
|
|
166
|
+
value={filterValue.shortcut || 'all'}
|
|
38
167
|
customRange={filterValue.customRange || { from: '', to: '' }}
|
|
39
|
-
onChange={optionKey =>
|
|
40
|
-
|
|
41
|
-
fieldName,
|
|
42
|
-
resolveNextDateFilterValue(filterValue, optionKey),
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
onCustomRangeChange={range =>
|
|
46
|
-
...(filterValue || {}),
|
|
47
|
-
shortcut: 'custom',
|
|
48
|
-
customRange: range,
|
|
168
|
+
onChange={optionKey =>
|
|
169
|
+
requestChange(
|
|
170
|
+
fieldName,
|
|
171
|
+
resolveNextDateFilterValue(filterValue, optionKey),
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
onCustomRangeChange={range => requestChange(fieldName, {
|
|
175
|
+
...(filterValue || {}),
|
|
176
|
+
shortcut: 'custom',
|
|
177
|
+
customRange: range,
|
|
49
178
|
})}
|
|
50
179
|
/>
|
|
51
180
|
</View>
|
|
52
181
|
);
|
|
53
182
|
}
|
|
54
183
|
|
|
55
|
-
if (column?.list) {
|
|
56
|
-
const rawOptions =
|
|
184
|
+
if (column?.list) {
|
|
185
|
+
const rawOptions = loadedItems.length > 0
|
|
186
|
+
? mapOptions(column, loadedItems, storeName)
|
|
187
|
+
: buildOptionsFromColumn(column, resolveOptions, storeName);
|
|
57
188
|
const options = [
|
|
58
|
-
{ key: '', label: global.t?.t(storeName, 'label', 'select')
|
|
189
|
+
{ key: '', label: global.t?.t(storeName, 'label', 'select') },
|
|
59
190
|
...rawOptions,
|
|
60
191
|
];
|
|
61
192
|
const selectedKey = normalizeOptionKey(filters?.[fieldName]);
|
|
@@ -63,20 +194,23 @@ const DefaultColumnFilter = ({
|
|
|
63
194
|
|
|
64
195
|
return (
|
|
65
196
|
<View style={[style, styles.filterCell]}>
|
|
66
|
-
<CompactFilterSelector
|
|
67
|
-
dense
|
|
68
|
-
store={storeName}
|
|
69
|
-
field={fieldName}
|
|
70
|
-
icon="filter"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
197
|
+
<CompactFilterSelector
|
|
198
|
+
dense
|
|
199
|
+
store={storeName}
|
|
200
|
+
field={fieldName}
|
|
201
|
+
icon="filter"
|
|
202
|
+
onBeforeOpen={onBeforeOpen || (() => loadListOptions())}
|
|
203
|
+
onSearchChange={onSearchChange || (value => loadListOptions(value))}
|
|
204
|
+
accentColor={accentColor}
|
|
205
|
+
active={Boolean(selectedKey)}
|
|
206
|
+
label={selected?.label || options[0]?.label || ''}
|
|
207
|
+
options={options}
|
|
208
|
+
searchable
|
|
209
|
+
selectedKey={selectedKey}
|
|
210
|
+
onSelect={optionKey => {
|
|
211
|
+
requestChange(fieldName, optionKey);
|
|
212
|
+
return true;
|
|
213
|
+
}}
|
|
80
214
|
/>
|
|
81
215
|
</View>
|
|
82
216
|
);
|
|
@@ -85,11 +219,11 @@ const DefaultColumnFilter = ({
|
|
|
85
219
|
return (
|
|
86
220
|
<View style={[style, styles.filterCell]}>
|
|
87
221
|
<TextInput
|
|
88
|
-
style={styles.filterInput}
|
|
89
|
-
value={normalizeText(filters?.[fieldName])}
|
|
90
|
-
placeholder={global.t?.t(storeName, 'input', column?.label || fieldName)}
|
|
91
|
-
onChangeText={value =>
|
|
92
|
-
/>
|
|
222
|
+
style={styles.filterInput}
|
|
223
|
+
value={normalizeText(filters?.[fieldName])}
|
|
224
|
+
placeholder={global.t?.t(storeName, 'input', column?.label || fieldName)}
|
|
225
|
+
onChangeText={value => requestChange(fieldName, value)}
|
|
226
|
+
/>
|
|
93
227
|
</View>
|
|
94
228
|
);
|
|
95
229
|
};
|