@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 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
- onCancel,
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
- setTempValue(value);
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 treeSelectVirtualizationProps = {
55
- listHeight: 256,
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
- // Skip transformation if options is empty or undefined
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
- // Skip filtering if search text is empty
198
- if (!searchText) return dataSource;
184
+ const filteredTree = useMemo(
185
+ () => filterTreeData(dataSource, searchText, searchBasedOn),
186
+ [dataSource, searchText, searchBasedOn]
187
+ );
199
188
 
200
- return getFilteredTreeData(dataSource, searchText);
201
- }, [dataSource, searchText, getFilteredTreeData]);
189
+ const treeMaps = useMemo(() => buildTreeMaps(options), [options]);
190
+ const selectedLeafCount = useMemo(() => countSelectedLeaves(treeMaps, tempValue), [treeMaps, tempValue]);
202
191
 
203
- const handleConfirm = useCallback(() => {
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 displayValue = dropdownOpen ? tempValue : value;
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
- {showMore && <span>+{displayValue.length - 1} more </span>}
237
- <CapIcon className="cap-unified-select-suffix-icon" type={dropdownOpen ? 'up' : 'down'} size="s" />
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, value, tempValue, dropdownOpen]);
206
+ }, [isMulti, displayValue, dropdownOpen]);
241
207
 
242
208
  const prefix = useMemo(() => {
243
- if (isMulti) {
244
- if (Array.isArray(tempValue) && tempValue?.length > 0) {
245
- const firstSelectedValue = tempValue[0];
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, value, tempValue, options]);
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 = useCallback(() => {
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 renderDropdown = useCallback(() => {
310
- const currentItems = filteredTree;
311
- const selectedCount = Array.isArray(tempValue)
312
- ? isTree
313
- ? processTreeData(dataSource, tempValue).selectedCount
314
- : tempValue?.length || 0
315
- : tempValue
316
- ? 1
317
- : 0;
318
-
319
- const renderCustomDropdown = useCallback(
320
- (menu) => {
321
- if (!customPopupRender) return menu;
322
-
323
- return (
324
- <div
325
- className={classnames(popoverClassName, `${type}-popup-container`)}
326
- >
327
- {showSearch && (
328
- <CapRow
329
- className={classnames('cap-unified-select-search-container')}
330
- align="middle"
331
- >
332
- <Input
333
- prefix={
334
- <CapIcon
335
- type="search"
336
- size="s"
337
- style={{ color: styledVars.CAP_G06 }}
338
- />
339
- }
340
- placeholder="Search"
341
- variant="borderless"
342
- value={searchText}
343
- onChange={(e) => setSearchText(e.target.value)}
344
- />
345
- </CapRow>
346
- )}
347
- {isMulti && showUpload && (
348
- <CapRow
349
- className={classnames('cap-unified-select-upload-container')}
350
- align="middle"
351
- onClick={onUpload}
352
- >
353
- <CapIcon
354
- type="upload"
355
- size="s"
356
- style={{ color: styledVars.CAP_SECONDARY.base }}
357
- />
358
- <CapLabel
359
- type="label14"
360
- className={classnames('cap-unified-select-upload-label')}
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
- Upload
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
- </div>
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
- return (
472
- <>
473
- <StyledTreeSelect
474
- type={type}
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
- classNames={{
490
- popup: { root: classnames('custom-popup-container', className) },
491
- }}
492
- style={style}
493
- status={isError ? 'error' : ''}
494
- allowClear={allowClear}
495
- multiple={isMulti}
496
- treeCheckable={isMulti}
497
- open={dropdownOpen}
498
- onOpenChange={handleDropdownVisibilityChange}
499
- showCheckedStrategy={TreeSelect.SHOW_PARENT}
500
- virtual
501
- disabled={disabled}
502
- filterTreeNode={false}
503
- {...treeSelectVirtualizationProps}
504
- popupRender={renderCustomDropdown}
505
- {...rest}
506
- />
507
- {isError && (
508
- <CapLabel
509
- className={classnames('cap-unified-select-status')}
510
- style={{ color: 'red' }}
511
- >
512
- {errorMessage}
513
- </CapLabel>
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
- {renderDropdown()}
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
- size: PropTypes.oneOf(['s', 'm', 'l', 'xl']),
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capillarytech/blaze-ui",
3
3
  "author": "Capillary Technologies",
4
- "version": "0.1.6-alpha.60",
4
+ "version": "0.1.6-alpha.62",
5
5
  "description": "Capillary UI component library with Ant Design v5",
6
6
  "main": "./index.js",
7
7
  "sideEffects": [