@capillarytech/blaze-ui 0.1.6-alpha.60 → 0.1.6-alpha.62
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/.DS_Store +0 -0
- package/CapUnifiedSelect/CapUnifiedSelect.js +312 -443
- package/CapUnifiedSelect/styles.js +19 -5
- package/package.json +1 -1
package/.DS_Store
ADDED
|
Binary file
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// Enhanced CapUnifiedSelect supporting 4 scenarios with advanced features in a single TreeSelect
|
|
2
1
|
import React, { useState, useEffect, useMemo, useCallback, memo } from 'react';
|
|
3
2
|
import PropTypes from 'prop-types';
|
|
4
3
|
import classnames from 'classnames';
|
|
@@ -14,6 +13,121 @@ const StyledTreeSelect = styled(TreeSelect)`
|
|
|
14
13
|
${selectStyles}
|
|
15
14
|
`;
|
|
16
15
|
|
|
16
|
+
const NoResult = memo(({ noResultCustomText, className, showUpload, options, noResultCustomIcon }) => (
|
|
17
|
+
<CapRow
|
|
18
|
+
className={classnames(className, 'cap-unified-select-no-result')}
|
|
19
|
+
align="middle"
|
|
20
|
+
gap={8}
|
|
21
|
+
>
|
|
22
|
+
<CapIcon type={noResultCustomIcon} size="m" />
|
|
23
|
+
<CapLabel className="cap-unified-select-no-result-text">
|
|
24
|
+
{showUpload && options?.length === 0
|
|
25
|
+
? noResultCustomText
|
|
26
|
+
: 'No results found'}
|
|
27
|
+
</CapLabel>
|
|
28
|
+
</CapRow>
|
|
29
|
+
));
|
|
30
|
+
|
|
31
|
+
const SelectAllCheckbox = memo(({ currentItems, tempValue, setTempValue, processTreeData }) => {
|
|
32
|
+
const { leafValues } = processTreeData(currentItems);
|
|
33
|
+
const totalAvailable = leafValues.length;
|
|
34
|
+
const leafSet = new Set(leafValues);
|
|
35
|
+
const selectedInScope = Array.isArray(tempValue)
|
|
36
|
+
? tempValue.filter((v) => leafSet.has(v)).length
|
|
37
|
+
: 0;
|
|
38
|
+
|
|
39
|
+
const allChecked = totalAvailable > 0 && selectedInScope === totalAvailable;
|
|
40
|
+
const indeterminate = selectedInScope > 0 && selectedInScope < totalAvailable;
|
|
41
|
+
|
|
42
|
+
const handleChange = (e) => {
|
|
43
|
+
if (e.target.checked) {
|
|
44
|
+
const merged = new Set(Array.isArray(tempValue) ? tempValue : []);
|
|
45
|
+
leafValues.forEach((v) => merged.add(v));
|
|
46
|
+
setTempValue(Array.from(merged));
|
|
47
|
+
} else {
|
|
48
|
+
const toRemove = new Set(leafValues);
|
|
49
|
+
const next = (Array.isArray(tempValue) ? tempValue : []).filter(
|
|
50
|
+
(v) => !toRemove.has(v)
|
|
51
|
+
);
|
|
52
|
+
setTempValue(next);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<CapRow className="cap-unified-select-select-all-container" align="middle">
|
|
58
|
+
<Checkbox
|
|
59
|
+
className="cap-unified-select-select-all-checkbox"
|
|
60
|
+
checked={allChecked}
|
|
61
|
+
indeterminate={indeterminate}
|
|
62
|
+
onChange={handleChange}
|
|
63
|
+
>
|
|
64
|
+
<CapLabel type="label14" className="cap-unified-select-select-all-label">Select all</CapLabel>
|
|
65
|
+
</Checkbox>
|
|
66
|
+
</CapRow>
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const buildTreeMaps = (nodes) => {
|
|
71
|
+
const result = {
|
|
72
|
+
leafValues: [],
|
|
73
|
+
parentChildMap: {},
|
|
74
|
+
nodeMap: {},
|
|
75
|
+
};
|
|
76
|
+
if (!nodes) return result;
|
|
77
|
+
|
|
78
|
+
const traverse = (items) => {
|
|
79
|
+
items.forEach((item) => {
|
|
80
|
+
result.nodeMap[item.value] = item;
|
|
81
|
+
if (item.children && item.children.length > 0) {
|
|
82
|
+
result.parentChildMap[item.value] = item.children.map(child => child.value);
|
|
83
|
+
traverse(item.children);
|
|
84
|
+
} else {
|
|
85
|
+
result.leafValues.push(item.value);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
traverse(nodes);
|
|
90
|
+
return result;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const countSelectedLeaves = (treeMaps, selectedValues) => {
|
|
94
|
+
if (!Array.isArray(selectedValues) || !selectedValues.length) return 0;
|
|
95
|
+
const expandedSet = new Set(selectedValues);
|
|
96
|
+
const processNode = (value) => {
|
|
97
|
+
const children = treeMaps.parentChildMap[value];
|
|
98
|
+
if (!children) return;
|
|
99
|
+
children.forEach(childValue => {
|
|
100
|
+
expandedSet.add(childValue);
|
|
101
|
+
processNode(childValue);
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
selectedValues.forEach(processNode);
|
|
105
|
+
return treeMaps.leafValues.reduce((count, leaf) => expandedSet.has(leaf) ? count + 1 : count, 0);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const filterTreeData = (data, search, searchBasedOn) => {
|
|
109
|
+
if (!data?.length || !search) return data;
|
|
110
|
+
const searchLower = search.toLowerCase();
|
|
111
|
+
const nodeMatchesSearch = (node) => {
|
|
112
|
+
const target = searchBasedOn === 'value'
|
|
113
|
+
? String(node.value ?? '')
|
|
114
|
+
: searchBasedOn === 'key'
|
|
115
|
+
? String(node.key ?? '')
|
|
116
|
+
: String(node.label ?? node.title ?? '');
|
|
117
|
+
return target.toLowerCase().includes(searchLower);
|
|
118
|
+
};
|
|
119
|
+
const loop = (items) =>
|
|
120
|
+
items.reduce((acc, item) => {
|
|
121
|
+
if (!item) return acc;
|
|
122
|
+
const children = item.children?.length ? loop(item.children) : [];
|
|
123
|
+
if (nodeMatchesSearch(item) || children.length) {
|
|
124
|
+
acc.push({ ...item, children });
|
|
125
|
+
}
|
|
126
|
+
return acc;
|
|
127
|
+
}, []);
|
|
128
|
+
return loop(data);
|
|
129
|
+
};
|
|
130
|
+
|
|
17
131
|
const CapUnifiedSelect = ({
|
|
18
132
|
type,
|
|
19
133
|
options = [],
|
|
@@ -37,8 +151,7 @@ const CapUnifiedSelect = ({
|
|
|
37
151
|
showSearch = true,
|
|
38
152
|
searchBasedOn = 'label',
|
|
39
153
|
onConfirm,
|
|
40
|
-
|
|
41
|
-
size = 'm',
|
|
154
|
+
clearText,
|
|
42
155
|
noResultCustomText = 'No results found',
|
|
43
156
|
noResultCustomIcon = 'warning',
|
|
44
157
|
...rest
|
|
@@ -48,142 +161,17 @@ const CapUnifiedSelect = ({
|
|
|
48
161
|
const [dropdownOpen, setDropdownOpen] = useState(false);
|
|
49
162
|
|
|
50
163
|
useEffect(() => {
|
|
51
|
-
|
|
164
|
+
const isEqual = Array.isArray(value) && Array.isArray(tempValue)
|
|
165
|
+
? value.length === tempValue.length && value.every((v) => tempValue.includes(v))
|
|
166
|
+
: value === tempValue;
|
|
167
|
+
if (!isEqual) setTempValue(value);
|
|
52
168
|
}, [value]);
|
|
53
169
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
listItemHeight: 32,
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const NoResult = ({ noResultCustomText, className, showUpload, options }) => (
|
|
60
|
-
<CapRow
|
|
61
|
-
className={classnames(className, 'cap-unified-select-no-result')}
|
|
62
|
-
align="middle"
|
|
63
|
-
gap={8}
|
|
64
|
-
>
|
|
65
|
-
<CapIcon type={noResultCustomIcon} size="m" />
|
|
66
|
-
<CapLabel className="cap-unified-select-no-result-text">
|
|
67
|
-
{showUpload && options?.length === 0
|
|
68
|
-
? noResultCustomText
|
|
69
|
-
: 'No results found'}
|
|
70
|
-
</CapLabel>
|
|
71
|
-
</CapRow>
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
const getFilteredTreeData = useCallback(
|
|
75
|
-
(data, search) => {
|
|
76
|
-
if (!search || !data || data.length === 0) return data;
|
|
77
|
-
const searchLower = search.toLowerCase();
|
|
78
|
-
|
|
79
|
-
const filterNode = (node) => {
|
|
80
|
-
if (!node) return false;
|
|
81
|
-
|
|
82
|
-
let textToSearch = '';
|
|
83
|
-
|
|
84
|
-
if (searchBasedOn === 'value') {
|
|
85
|
-
textToSearch = String(node.value || '').toLowerCase();
|
|
86
|
-
} else if (searchBasedOn === 'key') {
|
|
87
|
-
textToSearch = String(node.key || '').toLowerCase();
|
|
88
|
-
} else {
|
|
89
|
-
textToSearch = String(node.label || node.title || '').toLowerCase();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return textToSearch.includes(searchLower);
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const loop = (data) => {
|
|
96
|
-
if (!data) return [];
|
|
97
|
-
|
|
98
|
-
return data
|
|
99
|
-
.map((item) => {
|
|
100
|
-
if (!item) return null;
|
|
101
|
-
|
|
102
|
-
const children =
|
|
103
|
-
item.children && item.children.length > 0
|
|
104
|
-
? loop(item.children)
|
|
105
|
-
: [];
|
|
106
|
-
|
|
107
|
-
if (filterNode(item) || children.length > 0) {
|
|
108
|
-
return { ...item, children };
|
|
109
|
-
}
|
|
110
|
-
return null;
|
|
111
|
-
})
|
|
112
|
-
.filter(Boolean);
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
return loop(data);
|
|
116
|
-
},
|
|
117
|
-
[searchBasedOn],
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
const processTreeData = useCallback((nodes, selectedValues = null) => {
|
|
121
|
-
const result = {
|
|
122
|
-
leafValues: [],
|
|
123
|
-
parentChildMap: {},
|
|
124
|
-
nodeMap: {},
|
|
125
|
-
selectedCount: 0,
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
if (!nodes) return result;
|
|
129
|
-
|
|
130
|
-
const traverse = (items) => {
|
|
131
|
-
items.forEach((item) => {
|
|
132
|
-
result.nodeMap[item.value] = item;
|
|
133
|
-
|
|
134
|
-
if (item.children && item.children.length > 0) {
|
|
135
|
-
result.parentChildMap[item.value] = item.children.map(
|
|
136
|
-
(child) => child.value,
|
|
137
|
-
);
|
|
138
|
-
traverse(item.children);
|
|
139
|
-
} else {
|
|
140
|
-
result.leafValues.push(item.value);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
};
|
|
144
|
-
traverse(nodes);
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
selectedValues &&
|
|
148
|
-
Array.isArray(selectedValues) &&
|
|
149
|
-
selectedValues.length > 0
|
|
150
|
-
) {
|
|
151
|
-
const expandedSet = new Set(selectedValues);
|
|
152
|
-
|
|
153
|
-
const processNode = (value) => {
|
|
154
|
-
const children = result.parentChildMap[value];
|
|
155
|
-
if (!children) return;
|
|
156
|
-
|
|
157
|
-
children.forEach((childValue) => {
|
|
158
|
-
expandedSet.add(childValue);
|
|
159
|
-
processNode(childValue);
|
|
160
|
-
});
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
selectedValues.forEach(processNode);
|
|
164
|
-
|
|
165
|
-
result.leafValues.forEach((value) => {
|
|
166
|
-
if (expandedSet.has(value)) result.selectedCount++;
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return result;
|
|
171
|
-
}, []);
|
|
172
|
-
|
|
173
|
-
const isMulti = useMemo(
|
|
174
|
-
() => type === 'multiSelect' || type === 'multiTreeSelect',
|
|
175
|
-
[type],
|
|
176
|
-
);
|
|
177
|
-
const isTree = useMemo(
|
|
178
|
-
() => type === 'treeSelect' || type === 'multiTreeSelect',
|
|
179
|
-
[type],
|
|
180
|
-
);
|
|
170
|
+
const isMulti = useMemo(() => type === 'multiSelect' || type === 'multiTreeSelect', [type]);
|
|
171
|
+
const isTree = useMemo(() => type === 'treeSelect' || type === 'multiTreeSelect', [type]);
|
|
181
172
|
|
|
182
173
|
const dataSource = useMemo(() => {
|
|
183
|
-
|
|
184
|
-
if (!options || options.length === 0) return [];
|
|
185
|
-
|
|
186
|
-
// Only transform if not a tree select
|
|
174
|
+
if (!options?.length) return [];
|
|
187
175
|
return isTree
|
|
188
176
|
? options
|
|
189
177
|
: options.map((opt) => ({
|
|
@@ -193,88 +181,70 @@ const CapUnifiedSelect = ({
|
|
|
193
181
|
}));
|
|
194
182
|
}, [isTree, options]);
|
|
195
183
|
|
|
196
|
-
const filteredTree = useMemo(
|
|
197
|
-
|
|
198
|
-
|
|
184
|
+
const filteredTree = useMemo(
|
|
185
|
+
() => filterTreeData(dataSource, searchText, searchBasedOn),
|
|
186
|
+
[dataSource, searchText, searchBasedOn]
|
|
187
|
+
);
|
|
199
188
|
|
|
200
|
-
|
|
201
|
-
|
|
189
|
+
const treeMaps = useMemo(() => buildTreeMaps(options), [options]);
|
|
190
|
+
const selectedLeafCount = useMemo(() => countSelectedLeaves(treeMaps, tempValue), [treeMaps, tempValue]);
|
|
202
191
|
|
|
203
|
-
const
|
|
204
|
-
if (onChange) onChange(tempValue);
|
|
205
|
-
setDropdownOpen(false);
|
|
206
|
-
if (onConfirm) onConfirm(tempValue);
|
|
207
|
-
}, [onChange, onConfirm, tempValue]);
|
|
208
|
-
|
|
209
|
-
const handleCancel = useCallback(() => {
|
|
210
|
-
setTempValue(value);
|
|
211
|
-
setDropdownOpen(false);
|
|
212
|
-
if (onCancel) onCancel();
|
|
213
|
-
}, [value, onCancel]);
|
|
214
|
-
|
|
215
|
-
const handleTempChange = useCallback((newValue) => {
|
|
216
|
-
setTempValue(newValue);
|
|
217
|
-
}, []);
|
|
218
|
-
|
|
219
|
-
const handleDropdownVisibilityChange = useCallback(
|
|
220
|
-
(open) => {
|
|
221
|
-
if (open === false && !customPopupRender) {
|
|
222
|
-
if (onChange) onChange(tempValue);
|
|
223
|
-
} else if (open === false) {
|
|
224
|
-
setTempValue(value);
|
|
225
|
-
}
|
|
226
|
-
setDropdownOpen(open);
|
|
227
|
-
},
|
|
228
|
-
[value, onChange, tempValue, customPopupRender],
|
|
229
|
-
);
|
|
192
|
+
const displayValue = dropdownOpen ? tempValue : value;
|
|
230
193
|
|
|
231
194
|
const suffix = useMemo(() => {
|
|
232
|
-
const
|
|
233
|
-
const showMore = isMulti && Array.isArray(displayValue) && displayValue.length > 1;
|
|
195
|
+
const count = Array.isArray(displayValue) ? displayValue.length : (displayValue ? 1 : 0);
|
|
234
196
|
return (
|
|
235
197
|
<>
|
|
236
|
-
{
|
|
237
|
-
<CapIcon
|
|
198
|
+
{isMulti && count > 1 && <span>+{count - 1} more </span>}
|
|
199
|
+
<CapIcon
|
|
200
|
+
className="cap-unified-select-suffix-icon"
|
|
201
|
+
type={dropdownOpen ? 'up' : 'down'}
|
|
202
|
+
size="s"
|
|
203
|
+
/>
|
|
238
204
|
</>
|
|
239
205
|
);
|
|
240
|
-
}, [isMulti,
|
|
206
|
+
}, [isMulti, displayValue, dropdownOpen]);
|
|
241
207
|
|
|
242
208
|
const prefix = useMemo(() => {
|
|
243
|
-
if (isMulti) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const firstSelectedOption = options?.find(
|
|
247
|
-
(opt) => opt?.value === firstSelectedValue,
|
|
248
|
-
);
|
|
249
|
-
return firstSelectedOption?.label || null;
|
|
250
|
-
} else if (Array.isArray(value) && value?.length > 0) {
|
|
251
|
-
const firstSelectedValue = value[0];
|
|
252
|
-
const firstSelectedOption = options?.find(
|
|
253
|
-
(opt) => opt?.value === firstSelectedValue,
|
|
254
|
-
);
|
|
255
|
-
return firstSelectedOption?.label || null;
|
|
256
|
-
}
|
|
209
|
+
if (isMulti && Array.isArray(displayValue) && displayValue.length > 0) {
|
|
210
|
+
const firstLeafValue = displayValue.find(val => treeMaps.leafValues.includes(val));
|
|
211
|
+
return treeMaps.nodeMap[firstLeafValue]?.label || treeMaps.nodeMap[firstLeafValue]?.title || null;
|
|
257
212
|
}
|
|
258
213
|
return null;
|
|
259
|
-
}, [isMulti,
|
|
214
|
+
}, [isMulti, displayValue, treeMaps]);
|
|
215
|
+
|
|
216
|
+
const handleConfirm = useCallback(() => {
|
|
217
|
+
onChange?.(tempValue);
|
|
218
|
+
setDropdownOpen(false);
|
|
219
|
+
setSearchText('');
|
|
220
|
+
onConfirm?.(tempValue);
|
|
221
|
+
}, [onChange, onConfirm, tempValue]);
|
|
222
|
+
|
|
223
|
+
const handleClearAll = useCallback(() => {
|
|
224
|
+
setTempValue([]);
|
|
225
|
+
onChange?.([]);
|
|
226
|
+
setDropdownOpen(false);
|
|
227
|
+
}, [onChange]);
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
const handleDropdownVisibilityChange = useCallback((open) => {
|
|
231
|
+
if (!open && !customPopupRender) {
|
|
232
|
+
onChange?.(tempValue);
|
|
233
|
+
} else if (!open) {
|
|
234
|
+
setTempValue(value);
|
|
235
|
+
}
|
|
236
|
+
setDropdownOpen(open);
|
|
237
|
+
}, [customPopupRender, value, onChange, tempValue]);
|
|
260
238
|
|
|
261
|
-
const renderHeader =
|
|
239
|
+
const renderHeader = useMemo(() => {
|
|
262
240
|
if (!headerLabel && !tooltip) return null;
|
|
263
241
|
return (
|
|
264
242
|
<>
|
|
265
|
-
<HeaderWrapper
|
|
266
|
-
className={classnames(
|
|
267
|
-
disabled ? 'disabled' : '',
|
|
268
|
-
'cap-unified-select-header',
|
|
269
|
-
)}
|
|
270
|
-
>
|
|
243
|
+
<HeaderWrapper className={classnames(disabled && 'disabled', 'cap-unified-select-header')}>
|
|
271
244
|
{headerLabel && (
|
|
272
245
|
<CapLabel
|
|
273
246
|
type="label16"
|
|
274
|
-
className={classnames(
|
|
275
|
-
disabled ? 'disabled' : '',
|
|
276
|
-
'cap-unified-select-header-label',
|
|
277
|
-
)}
|
|
247
|
+
className={classnames(disabled && 'disabled', 'cap-unified-select-header-label')}
|
|
278
248
|
>
|
|
279
249
|
{headerLabel}
|
|
280
250
|
</CapLabel>
|
|
@@ -282,10 +252,7 @@ const CapUnifiedSelect = ({
|
|
|
282
252
|
{tooltip && (
|
|
283
253
|
<CapTooltipWithInfo
|
|
284
254
|
title={tooltip}
|
|
285
|
-
className={classnames(
|
|
286
|
-
disabled ? 'disabled' : '',
|
|
287
|
-
'cap-unified-select-header-tooltip',
|
|
288
|
-
)}
|
|
255
|
+
className={classnames(disabled && 'disabled', 'cap-unified-select-header-tooltip')}
|
|
289
256
|
iconProps={{ disabled }}
|
|
290
257
|
/>
|
|
291
258
|
)}
|
|
@@ -293,10 +260,7 @@ const CapUnifiedSelect = ({
|
|
|
293
260
|
{bylineText && (
|
|
294
261
|
<div className="cap-unified-select-header-byline-text">
|
|
295
262
|
<CapLabel
|
|
296
|
-
className={classnames(
|
|
297
|
-
disabled ? 'disabled' : '',
|
|
298
|
-
'cap-unified-select-header-byline-text',
|
|
299
|
-
)}
|
|
263
|
+
className={classnames(disabled && 'disabled', 'cap-unified-select-header-byline-text')}
|
|
300
264
|
>
|
|
301
265
|
{bylineText}
|
|
302
266
|
</CapLabel>
|
|
@@ -306,253 +270,159 @@ const CapUnifiedSelect = ({
|
|
|
306
270
|
);
|
|
307
271
|
}, [headerLabel, tooltip, bylineText, disabled]);
|
|
308
272
|
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
273
|
+
const renderCustomDropdown = useCallback(
|
|
274
|
+
(menu) => {
|
|
275
|
+
if (!customPopupRender) return menu;
|
|
276
|
+
const currentItems = filteredTree;
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<div className={classnames(popoverClassName, `${type}-popup-container`)}>
|
|
280
|
+
{showSearch && (
|
|
281
|
+
<CapRow className="cap-unified-select-search-container" align="middle">
|
|
282
|
+
<Input
|
|
283
|
+
prefix={<CapIcon type="search" size="s" style={{ color: styledVars.CAP_G06 }} />}
|
|
284
|
+
placeholder="Search"
|
|
285
|
+
variant="borderless"
|
|
286
|
+
value={searchText}
|
|
287
|
+
onChange={(e) => setSearchText(e.target.value)}
|
|
288
|
+
allowClear
|
|
289
|
+
/>
|
|
290
|
+
</CapRow>
|
|
291
|
+
)}
|
|
292
|
+
|
|
293
|
+
{isMulti && showUpload && (
|
|
294
|
+
<CapRow className="cap-unified-select-upload-container" align="middle" onClick={onUpload}>
|
|
295
|
+
<CapIcon type="upload" size="s" style={{ color: styledVars.CAP_SECONDARY.base }} />
|
|
296
|
+
<CapLabel type="label14" className="cap-unified-select-upload-label">Upload</CapLabel>
|
|
297
|
+
</CapRow>
|
|
298
|
+
)}
|
|
299
|
+
|
|
300
|
+
{isMulti && currentItems.length > 0 && (
|
|
301
|
+
<SelectAllCheckbox
|
|
302
|
+
currentItems={currentItems}
|
|
303
|
+
tempValue={tempValue}
|
|
304
|
+
setTempValue={setTempValue}
|
|
305
|
+
processTreeData={buildTreeMaps}
|
|
306
|
+
/>
|
|
307
|
+
)}
|
|
308
|
+
|
|
309
|
+
{currentItems.length === 0 ? (
|
|
310
|
+
<NoResult
|
|
311
|
+
noResultCustomText={noResultCustomText}
|
|
312
|
+
className={className}
|
|
313
|
+
showUpload={showUpload}
|
|
314
|
+
options={options}
|
|
315
|
+
noResultCustomIcon={noResultCustomIcon}
|
|
316
|
+
/>
|
|
317
|
+
) : (
|
|
318
|
+
menu
|
|
319
|
+
)}
|
|
320
|
+
|
|
321
|
+
{currentItems.length > 0 && isMulti && (
|
|
322
|
+
<div className="cap-unified-select-confirm-container">
|
|
323
|
+
<div className="cap-unified-select-confirm-button-group">
|
|
324
|
+
<Button
|
|
325
|
+
type="primary"
|
|
326
|
+
size="small"
|
|
327
|
+
className="cap-unified-select-confirm-button"
|
|
328
|
+
onClick={handleConfirm}
|
|
361
329
|
>
|
|
362
|
-
|
|
330
|
+
Confirm
|
|
331
|
+
</Button>
|
|
332
|
+
<Button
|
|
333
|
+
type="text"
|
|
334
|
+
className="cap-unified-select-cancel-button"
|
|
335
|
+
size="small"
|
|
336
|
+
onClick={handleClearAll}
|
|
337
|
+
>
|
|
338
|
+
Clear all
|
|
339
|
+
</Button>
|
|
340
|
+
<CapLabel className="cap-unified-select-selected-count">
|
|
341
|
+
{selectedLeafCount} selected
|
|
363
342
|
</CapLabel>
|
|
364
|
-
</CapRow>
|
|
365
|
-
)}
|
|
366
|
-
{isMulti &&
|
|
367
|
-
currentItems.length > 0 &&
|
|
368
|
-
(() => {
|
|
369
|
-
const { leafValues } = processTreeData(currentItems);
|
|
370
|
-
const totalAvailable = leafValues.length;
|
|
371
|
-
const selectedInScope = processTreeData(
|
|
372
|
-
currentItems,
|
|
373
|
-
tempValue,
|
|
374
|
-
).selectedCount;
|
|
375
|
-
|
|
376
|
-
return (
|
|
377
|
-
<CapRow
|
|
378
|
-
className={classnames(
|
|
379
|
-
'cap-unified-select-select-all-container',
|
|
380
|
-
)}
|
|
381
|
-
align="middle"
|
|
382
|
-
>
|
|
383
|
-
<Checkbox
|
|
384
|
-
className={classnames(
|
|
385
|
-
'cap-unified-select-select-all-checkbox',
|
|
386
|
-
)}
|
|
387
|
-
checked={
|
|
388
|
-
totalAvailable > 0 && selectedInScope === totalAvailable
|
|
389
|
-
}
|
|
390
|
-
indeterminate={
|
|
391
|
-
selectedInScope > 0 && selectedInScope < totalAvailable
|
|
392
|
-
}
|
|
393
|
-
onChange={(e) => {
|
|
394
|
-
setTempValue(e.target.checked ? leafValues : []);
|
|
395
|
-
}}
|
|
396
|
-
>
|
|
397
|
-
<CapLabel
|
|
398
|
-
type="label14"
|
|
399
|
-
className={classnames(
|
|
400
|
-
'cap-unified-select-select-all-label',
|
|
401
|
-
)}
|
|
402
|
-
>
|
|
403
|
-
Select all
|
|
404
|
-
</CapLabel>
|
|
405
|
-
</Checkbox>
|
|
406
|
-
</CapRow>
|
|
407
|
-
);
|
|
408
|
-
})()}
|
|
409
|
-
|
|
410
|
-
{currentItems.length === 0 ? (
|
|
411
|
-
<NoResult
|
|
412
|
-
noResultCustomText={noResultCustomText}
|
|
413
|
-
className={classnames(
|
|
414
|
-
className,
|
|
415
|
-
'cap-unified-select-no-result',
|
|
416
|
-
)}
|
|
417
|
-
showUpload={showUpload}
|
|
418
|
-
options={options}
|
|
419
|
-
/>
|
|
420
|
-
) : (
|
|
421
|
-
menu
|
|
422
|
-
)}
|
|
423
|
-
|
|
424
|
-
{currentItems.length > 0 && isMulti && (
|
|
425
|
-
<div className="cap-unified-select-confirm-container">
|
|
426
|
-
<div className="cap-unified-select-confirm-button-group">
|
|
427
|
-
<Button
|
|
428
|
-
type="primary"
|
|
429
|
-
size="small"
|
|
430
|
-
className="cap-unified-select-confirm-button"
|
|
431
|
-
onClick={handleConfirm}
|
|
432
|
-
>
|
|
433
|
-
Confirm
|
|
434
|
-
</Button>
|
|
435
|
-
<Button
|
|
436
|
-
type="text"
|
|
437
|
-
className="cap-unified-select-cancel-button"
|
|
438
|
-
size="small"
|
|
439
|
-
onClick={handleCancel}
|
|
440
|
-
>
|
|
441
|
-
Cancel
|
|
442
|
-
</Button>
|
|
443
|
-
<CapLabel className="cap-unified-select-selected-count">
|
|
444
|
-
{selectedCount} selected
|
|
445
|
-
</CapLabel>
|
|
446
|
-
</div>
|
|
447
343
|
</div>
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
);
|
|
451
|
-
},
|
|
452
|
-
[
|
|
453
|
-
customPopupRender,
|
|
454
|
-
popoverClassName,
|
|
455
|
-
type,
|
|
456
|
-
showSearch,
|
|
457
|
-
searchText,
|
|
458
|
-
isMulti,
|
|
459
|
-
showUpload,
|
|
460
|
-
currentItems,
|
|
461
|
-
tempValue,
|
|
462
|
-
className,
|
|
463
|
-
noResultCustomText,
|
|
464
|
-
onUpload,
|
|
465
|
-
handleConfirm,
|
|
466
|
-
handleCancel,
|
|
467
|
-
processTreeData,
|
|
468
|
-
],
|
|
469
|
-
);
|
|
344
|
+
</div>
|
|
345
|
+
)}
|
|
470
346
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
treeData={filteredTree}
|
|
476
|
-
value={customPopupRender ? tempValue : value}
|
|
477
|
-
onChange={onChange}
|
|
478
|
-
placeholder={placeholder}
|
|
479
|
-
showSearch={false}
|
|
480
|
-
maxTagCount={0}
|
|
481
|
-
maxTagPlaceholder={() => null}
|
|
482
|
-
prefix={tempValue?.length > 0 ? prefix : undefined}
|
|
483
|
-
suffixIcon={suffix}
|
|
484
|
-
className={classnames(
|
|
485
|
-
containerClassName,
|
|
486
|
-
`cap-unified-tree-select`,
|
|
487
|
-
className,
|
|
347
|
+
{(type === 'select' || type === 'treeSelect') && (
|
|
348
|
+
<CapRow className="cap-unified-select-tree-clear-container">
|
|
349
|
+
<CapLabel className="cap-unified-select-tree-clear-label" onClick={handleClearAll}>{clearText}</CapLabel>
|
|
350
|
+
</CapRow>
|
|
488
351
|
)}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
);
|
|
517
|
-
}, [
|
|
518
|
-
filteredTree,
|
|
519
|
-
tempValue,
|
|
520
|
-
value,
|
|
521
|
-
prefix,
|
|
522
|
-
suffix,
|
|
523
|
-
className,
|
|
524
|
-
style,
|
|
525
|
-
isError,
|
|
526
|
-
errorMessage,
|
|
527
|
-
allowClear,
|
|
528
|
-
isMulti,
|
|
529
|
-
isTree,
|
|
530
|
-
dropdownOpen,
|
|
531
|
-
customPopupRender,
|
|
532
|
-
handleTempChange,
|
|
533
|
-
onChange,
|
|
534
|
-
disabled,
|
|
535
|
-
handleDropdownVisibilityChange,
|
|
536
|
-
treeSelectVirtualizationProps,
|
|
537
|
-
dataSource,
|
|
538
|
-
processTreeData,
|
|
539
|
-
]);
|
|
352
|
+
</div>
|
|
353
|
+
);
|
|
354
|
+
},
|
|
355
|
+
[
|
|
356
|
+
customPopupRender,
|
|
357
|
+
filteredTree,
|
|
358
|
+
searchText,
|
|
359
|
+
isMulti,
|
|
360
|
+
showUpload,
|
|
361
|
+
onUpload,
|
|
362
|
+
noResultCustomText,
|
|
363
|
+
noResultCustomIcon,
|
|
364
|
+
options,
|
|
365
|
+
type,
|
|
366
|
+
tempValue,
|
|
367
|
+
handleConfirm,
|
|
368
|
+
handleClearAll,
|
|
369
|
+
popoverClassName,
|
|
370
|
+
className,
|
|
371
|
+
selectedLeafCount,
|
|
372
|
+
]
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
const combinedClassName = useMemo(
|
|
376
|
+
() => classnames(containerClassName, 'cap-unified-tree-select', className),
|
|
377
|
+
[containerClassName, className]
|
|
378
|
+
);
|
|
540
379
|
|
|
541
380
|
return (
|
|
542
381
|
<CapRow className={classnames(className, 'cap-unified-select-container')}>
|
|
543
|
-
{renderHeader
|
|
544
|
-
|
|
382
|
+
{renderHeader}
|
|
383
|
+
<StyledTreeSelect
|
|
384
|
+
type={type}
|
|
385
|
+
treeData={filteredTree}
|
|
386
|
+
value={customPopupRender ? tempValue : value}
|
|
387
|
+
onChange={isMulti ? setTempValue : onChange}
|
|
388
|
+
placeholder={placeholder}
|
|
389
|
+
showSearch={false}
|
|
390
|
+
maxTagCount={0}
|
|
391
|
+
maxTagPlaceholder={() => null}
|
|
392
|
+
prefix={prefix || undefined}
|
|
393
|
+
suffixIcon={suffix}
|
|
394
|
+
className={combinedClassName}
|
|
395
|
+
classNames={{
|
|
396
|
+
popup: { root: classnames('custom-popup-container', className) },
|
|
397
|
+
}}
|
|
398
|
+
style={style}
|
|
399
|
+
status={isError ? 'error' : ''}
|
|
400
|
+
allowClear={allowClear}
|
|
401
|
+
multiple={isMulti}
|
|
402
|
+
treeCheckable={isMulti}
|
|
403
|
+
treeCheckStrictly={false}
|
|
404
|
+
showCheckedStrategy={TreeSelect.SHOW_CHILD}
|
|
405
|
+
open={dropdownOpen}
|
|
406
|
+
onOpenChange={handleDropdownVisibilityChange}
|
|
407
|
+
virtual
|
|
408
|
+
disabled={disabled}
|
|
409
|
+
filterTreeNode={false}
|
|
410
|
+
listHeight={256}
|
|
411
|
+
listItemHeight={32}
|
|
412
|
+
popupRender={renderCustomDropdown}
|
|
413
|
+
{...rest}
|
|
414
|
+
/>
|
|
415
|
+
{isError && (
|
|
416
|
+
<CapLabel className="cap-unified-select-status" style={{ color: 'red' }}>
|
|
417
|
+
{errorMessage}
|
|
418
|
+
</CapLabel>
|
|
419
|
+
)}
|
|
545
420
|
</CapRow>
|
|
546
421
|
);
|
|
547
422
|
};
|
|
548
423
|
|
|
549
424
|
CapUnifiedSelect.propTypes = {
|
|
550
|
-
type: PropTypes.oneOf([
|
|
551
|
-
'select',
|
|
552
|
-
'multiSelect',
|
|
553
|
-
'treeSelect',
|
|
554
|
-
'multiTreeSelect',
|
|
555
|
-
]),
|
|
425
|
+
type: PropTypes.oneOf(['select', 'multiSelect', 'treeSelect', 'multiTreeSelect']),
|
|
556
426
|
options: PropTypes.array,
|
|
557
427
|
value: PropTypes.any,
|
|
558
428
|
onChange: PropTypes.func,
|
|
@@ -567,13 +437,12 @@ CapUnifiedSelect.propTypes = {
|
|
|
567
437
|
showSearch: PropTypes.bool,
|
|
568
438
|
searchBasedOn: PropTypes.oneOf(['label', 'value', 'key']),
|
|
569
439
|
onConfirm: PropTypes.func,
|
|
570
|
-
onCancel: PropTypes.func,
|
|
571
440
|
isError: PropTypes.bool,
|
|
572
441
|
errorMessage: PropTypes.string,
|
|
573
442
|
popupClassName: PropTypes.string,
|
|
574
443
|
showUpload: PropTypes.bool,
|
|
575
444
|
onUpload: PropTypes.func,
|
|
576
|
-
|
|
445
|
+
clearText: PropTypes.string,
|
|
577
446
|
};
|
|
578
447
|
|
|
579
448
|
CapUnifiedSelect.defaultProps = {
|
|
@@ -582,8 +451,8 @@ CapUnifiedSelect.defaultProps = {
|
|
|
582
451
|
searchBasedOn: 'label',
|
|
583
452
|
noResultCustomText: 'No results found',
|
|
584
453
|
noResultCustomIcon: 'warning',
|
|
454
|
+
clearText: 'Clear',
|
|
585
455
|
options: [],
|
|
586
|
-
size: 'm',
|
|
587
456
|
allowClear: false,
|
|
588
457
|
customPopupRender: true,
|
|
589
458
|
showSearch: true,
|
|
@@ -278,17 +278,15 @@ export const selectStyles = css`
|
|
|
278
278
|
display: flex;
|
|
279
279
|
padding-left: 8px;
|
|
280
280
|
align-items: center;
|
|
281
|
-
width: 100%; /* so it can push the label */
|
|
282
|
-
button {
|
|
283
|
-
height: 32px;
|
|
284
|
-
width: 94px;
|
|
285
|
-
}
|
|
286
281
|
.cap-unified-select-confirm-button {
|
|
287
282
|
background-color: #6ebd6e;
|
|
283
|
+
height: 32px;
|
|
284
|
+
width: 94px;
|
|
288
285
|
}
|
|
289
286
|
.cap-unified-select-cancel-button {
|
|
290
287
|
border: transparent;
|
|
291
288
|
box-shadow: none;
|
|
289
|
+
width: 80px;
|
|
292
290
|
}
|
|
293
291
|
}
|
|
294
292
|
.cap-unified-select-selected-count {
|
|
@@ -324,6 +322,22 @@ export const selectStyles = css`
|
|
|
324
322
|
box-shadow: none !important;
|
|
325
323
|
}
|
|
326
324
|
}
|
|
325
|
+
.cap-unified-select-tree-clear-container{
|
|
326
|
+
display: flex;
|
|
327
|
+
justify-content: center;
|
|
328
|
+
align-items: center;
|
|
329
|
+
height: 40px;
|
|
330
|
+
border-top: 1px solid #EBECF0;
|
|
331
|
+
cursor: pointer;
|
|
332
|
+
color: #091E42;
|
|
333
|
+
&:hover{
|
|
334
|
+
background-color: #EBECF0;
|
|
335
|
+
}
|
|
336
|
+
.cap-unified-select-tree-clear-label{
|
|
337
|
+
font-size: 14px;
|
|
338
|
+
font-weight: 400;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
327
341
|
}
|
|
328
342
|
|
|
329
343
|
&.custom-popup-container {
|
package/package.json
CHANGED