@capillarytech/blaze-ui 0.1.6-alpha.5 → 0.1.6-alpha.50

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.
Files changed (101) hide show
  1. package/.DS_Store +0 -0
  2. package/CapIcon/CapIcon.js +183 -0
  3. package/CapIcon/index.js +3 -0
  4. package/CapIcon/styles.js +76 -0
  5. package/CapInput/CapInput.js +1 -1
  6. package/CapInput/Number.js +1 -1
  7. package/CapInput/Search.js +1 -1
  8. package/CapInput/TextArea.js +1 -1
  9. package/CapLabel/CapLabel.js +101 -81
  10. package/CapLabel/index.js +3 -1
  11. package/CapLabel/styles.js +250 -212
  12. package/CapRow/CapRow.js +111 -10
  13. package/CapRow/index.js +3 -1
  14. package/CapRow/styles.js +47 -6
  15. package/CapSkeleton/CapSkeleton.js +17 -0
  16. package/CapSkeleton/index.js +1 -0
  17. package/CapSpin/CapSpin.js +23 -0
  18. package/CapSpin/index.js +1 -0
  19. package/CapTable/loadable.js +4 -4
  20. package/CapTable/styles.js +1 -1
  21. package/CapTestSelect/CapTestSelect.js +47 -0
  22. package/CapTestSelect/index.js +1 -0
  23. package/CapTooltip/CapTooltip.js +87 -25
  24. package/CapTooltip/index.js +3 -1
  25. package/CapTooltip/styles.js +19 -27
  26. package/CapTooltipWithInfo/CapTooltipWithInfo.js +82 -0
  27. package/CapTooltipWithInfo/index.js +3 -0
  28. package/CapTooltipWithInfo/styles.js +22 -0
  29. package/CapUnifiedSelect/CapUnifiedSelect.js +308 -66
  30. package/CapUnifiedSelect/index.js +1 -4
  31. package/CapUnifiedSelect/styles.js +258 -151
  32. package/assets/upload.svg +3 -0
  33. package/index.js +12 -1
  34. package/package.json +5 -11
  35. package/utils/index.js +1 -0
  36. package/utils/withStyles.js +24 -0
  37. package/CapHeading/CapHeading.js +0 -71
  38. package/CapHeading/index.js +0 -1
  39. package/CapHeading/styles.js +0 -125
  40. package/CapInfoNote/CapInfoNote.js +0 -54
  41. package/CapInfoNote/index.js +0 -1
  42. package/CapInfoNote/styles.js +0 -63
  43. package/CapInput/loadable.js +0 -9
  44. package/CapUnifiedSelect/loadable.js +0 -3
  45. package/dist/235.index.js +0 -2
  46. package/dist/235.index.js.LICENSE.txt +0 -29
  47. package/dist/603.index.js +0 -1
  48. package/dist/CapInput/CapInput.js +0 -66
  49. package/dist/CapInput/Number.js +0 -42
  50. package/dist/CapInput/Search.js +0 -35
  51. package/dist/CapInput/TextArea.js +0 -42
  52. package/dist/CapInput/index.js +0 -15
  53. package/dist/CapInput/messages.js +0 -32
  54. package/dist/CapInput/styles.js +0 -11
  55. package/dist/CapTable/CapTable.js +0 -148
  56. package/dist/CapTable/index.js +0 -9
  57. package/dist/CapTable/loadable.js +0 -23
  58. package/dist/CapTable/styles.js +0 -26
  59. package/dist/LocaleHoc/index.js +0 -40
  60. package/dist/esm/CapHeading/CapHeading.js +0 -41
  61. package/dist/esm/CapHeading/index.js +0 -1
  62. package/dist/esm/CapHeading/styles.js +0 -123
  63. package/dist/esm/CapInfoNote/CapInfoNote.js +0 -62
  64. package/dist/esm/CapInfoNote/index.js +0 -1
  65. package/dist/esm/CapInfoNote/styles.js +0 -6
  66. package/dist/esm/CapInput/CapInput.js +0 -57
  67. package/dist/esm/CapInput/Number.js +0 -35
  68. package/dist/esm/CapInput/Search.js +0 -28
  69. package/dist/esm/CapInput/TextArea.js +0 -35
  70. package/dist/esm/CapInput/index.js +0 -8
  71. package/dist/esm/CapInput/loadable.js +0 -9
  72. package/dist/esm/CapInput/messages.js +0 -25
  73. package/dist/esm/CapInput/styles.js +0 -3
  74. package/dist/esm/CapLabel/CapLabel.js +0 -50
  75. package/dist/esm/CapLabel/index.js +0 -1
  76. package/dist/esm/CapLabel/styles.js +0 -219
  77. package/dist/esm/CapRow/CapRow.js +0 -22
  78. package/dist/esm/CapRow/index.js +0 -1
  79. package/dist/esm/CapRow/styles.js +0 -5
  80. package/dist/esm/CapTable/CapTable.js +0 -140
  81. package/dist/esm/CapTable/index.js +0 -2
  82. package/dist/esm/CapTable/loadable.js +0 -12
  83. package/dist/esm/CapTable/styles.js +0 -17
  84. package/dist/esm/CapTooltip/CapTooltip.js +0 -34
  85. package/dist/esm/CapTooltip/index.js +0 -1
  86. package/dist/esm/CapTooltip/styles.js +0 -6
  87. package/dist/esm/CapUnifiedSelect/CapUnifiedSelect.js +0 -101
  88. package/dist/esm/CapUnifiedSelect/index.js +0 -3
  89. package/dist/esm/CapUnifiedSelect/loadable.js +0 -4
  90. package/dist/esm/CapUnifiedSelect/messages.js +0 -23
  91. package/dist/esm/CapUnifiedSelect/styles.js +0 -15
  92. package/dist/esm/LocaleHoc/index.js +0 -31
  93. package/dist/esm/index.js +0 -11
  94. package/dist/esm/styled/index.js +0 -5
  95. package/dist/esm/styled/variables.js +0 -88
  96. package/dist/esm/translations/en.js +0 -329
  97. package/dist/index.js +0 -39
  98. package/dist/index.js.LICENSE.txt +0 -7
  99. package/dist/styled/index.js +0 -22
  100. package/dist/styled/variables.js +0 -94
  101. package/dist/translations/en.js +0 -335
@@ -1,121 +1,363 @@
1
- // CapUnifiedSelect component using Ant Design v5 Select and TreeSelect directly
2
- import React from 'react';
1
+ // Enhanced CapUnifiedSelect supporting 4 scenarios with advanced features in a single TreeSelect
2
+ import React, { useState, useEffect, useMemo, useCallback, memo } from 'react';
3
3
  import PropTypes from 'prop-types';
4
- import { Select, TreeSelect } from 'antd';
5
- import { SelectWrapper, HeaderWrapper, StyledInfoIcon } from './styles';
6
- import CapLabel from '../CapLabel';
7
- import CapTooltip from '../CapTooltip';
4
+ import classnames from 'classnames';
5
+ import { TreeSelect, Input, Button } from 'antd-v5';
6
+ import styled from 'styled-components';
7
+ import * as styledVars from '../styled/variables';
8
+ import { CapLabel, CapTooltipWithInfo, CapRow, CapIcon } from '../';
9
+ import withStyles from '../utils/withStyles';
10
+ import { SelectWrapper, HeaderWrapper, selectStyles } from './styles';
8
11
 
9
- function CapUnifiedSelect({
12
+ const StyledTreeSelect = styled(TreeSelect)`
13
+ ${selectStyles}
14
+ `;
15
+
16
+ const CapUnifiedSelect = ({
10
17
  type,
11
18
  options = [],
12
- treeData,
13
19
  value,
14
20
  onChange,
15
21
  placeholder = 'Select an option',
16
22
  className,
17
23
  style,
24
+ isError = false,
25
+ errorMessage,
26
+ popOverClassName,
18
27
  allowClear = false,
19
- showSearch = false,
20
- label,
28
+ headerLabel,
29
+ onUpload,
21
30
  tooltip,
31
+ bylineText,
22
32
  disabled = false,
23
- }) {
24
- const selectVirtualizationProps = {
25
- listHeight: 256,
26
- };
33
+ showUpload = false,
34
+ customPopupRender = true,
35
+ showSearch = true,
36
+ searchBasedOn = 'label',
37
+ onConfirm,
38
+ onCancel,
39
+ size = 'm',
40
+ noResultCustomText = 'No results found',
41
+ noResultCustomIcon = 'warning',
42
+ ...rest
43
+ }) => {
44
+
45
+ const [searchText, setSearchText] = useState('');
46
+ const [tempValue, setTempValue] = useState(value);
47
+ const [allSelected, setAllSelected] = useState(false);
48
+ const [dropdownOpen, setDropdownOpen] = useState(false);
49
+
27
50
 
28
- const treeSelectVirtualizationProps = {
29
- listHeight: 256,
51
+ useEffect(() => {
52
+ setTempValue(value);
53
+ }, [value]);
54
+
55
+ const treeSelectVirtualizationProps = useMemo(() => ({
56
+ listHeight: 256,
30
57
  listItemHeight: 32,
31
- };
58
+ }), []);
59
+
60
+ const NoResult = memo(({ noResultCustomText, className, showUpload, options }) => (
61
+ <CapRow
62
+ className={classnames(className, "cap-unified-select-no-result")}
63
+ align="middle"
64
+ gap={8}
65
+ >
66
+ <CapIcon type={noResultCustomIcon} size="m" />
67
+ <CapLabel className="cap-unified-select-no-result-text">{showUpload && options?.length === 0 ? noResultCustomText : 'No results found'}</CapLabel>
68
+ </CapRow>
69
+ ));
70
+
71
+ const getFilteredTreeData = useCallback((data, search) => {
72
+ if (!search) return data;
73
+
74
+ const filterNode = node => {
75
+ if (!node) return false;
76
+ if (searchBasedOn === 'value') {
77
+ const valueText = String(node?.value || '').toLowerCase();
78
+ return valueText.includes(search.toLowerCase());
79
+ } else if (searchBasedOn === 'key') {
80
+ const keyText = String(node?.key || '').toLowerCase();
81
+ return keyText.includes(search.toLowerCase());
82
+ } else {
83
+ const labelText = node?.label?.toLowerCase() || '';
84
+ return labelText.includes(search.toLowerCase());
85
+ }
86
+ };
87
+
88
+ const loop = data =>
89
+ data.map(item => {
90
+ const children = item.children ? loop(item.children) : [];
91
+ if (filterNode(item) || children.length) {
92
+ return { ...item, children };
93
+ }
94
+ return null;
95
+ }).filter(Boolean);
96
+
97
+ return loop(data);
98
+ }, [searchBasedOn]);
99
+
100
+ const flattenLeafValues = useCallback(nodes =>
101
+ nodes?.flatMap(node => node?.children ? flattenLeafValues(node.children) : [node?.value]) || [], []);
102
+
103
+ const isMulti = useMemo(() => type === 'multiSelect' || type === 'multiTreeSelect', [type]);
104
+ const isTree = useMemo(() => type === 'treeSelect' || type === 'multiTreeSelect', [type]);
105
+
106
+ const dataSource = useMemo(() => {
107
+ return isTree ? options : options?.map(opt => ({ title: opt?.label, value: opt?.value, key: opt?.key || opt?.value }));
108
+ }, [isTree, options]);
109
+
110
+ const filteredTree = useMemo(() => getFilteredTreeData(dataSource, searchText), [dataSource, searchText]);
111
+ const leafValues = useMemo(() => flattenLeafValues(filteredTree), [filteredTree]);
112
+
113
+ const handleSelectAll = useCallback(() => {
114
+ const availableValues = leafValues;
115
+ if (allSelected) {
116
+ setTempValue([]);
117
+ setAllSelected(false);
118
+ } else {
119
+ setTempValue(availableValues);
120
+ setAllSelected(true);
121
+ }
122
+ }, [allSelected, leafValues]);
123
+
124
+ useEffect(() => {
125
+ if (isMulti && Array.isArray(tempValue)) {
126
+ const allItemsSelected = tempValue.length > 0 && leafValues.length > 0 && tempValue.length === leafValues.length;
127
+ setAllSelected(allItemsSelected);
128
+ }
129
+ }, [tempValue, leafValues, isMulti]);
130
+
131
+ const handleConfirm = useCallback(() => {
132
+ if (onChange) onChange(tempValue);
133
+ setDropdownOpen(false);
134
+ }, [onChange, tempValue]);
135
+
136
+ const handleCancel = useCallback(() => {
137
+ setTempValue(value);
138
+ setDropdownOpen(false);
139
+ }, [value]);
140
+
141
+ const handleTempChange = useCallback(newValue => {
142
+ setTempValue(newValue);
143
+ }, []);
144
+
145
+ const handleDropdownVisibilityChange = useCallback(open => {
146
+ if (open === false) {
147
+ setTempValue(value);
148
+ }
149
+ setDropdownOpen(open);
150
+ }, [value]);
151
+
152
+ const suffix = useMemo(() => {
153
+ const displayValue = dropdownOpen ? tempValue : value;
154
+ return isMulti && Array.isArray(displayValue) && displayValue?.length > 1 ? (
155
+ <>
156
+ <span>+{displayValue.length - 1} more <CapIcon type="down" size="s" /></span>
157
+ </>
158
+ ) : (
159
+ <CapIcon type="down" size="s" />
160
+ );
161
+ }, [isMulti, value, tempValue, dropdownOpen]);
162
+
163
+ const prefix = useMemo(() => {
164
+ if (isMulti) {
165
+ if (Array.isArray(tempValue) && tempValue?.length > 0) {
166
+ const firstSelectedValue = tempValue[0];
167
+ const firstSelectedOption = options?.find(opt => opt?.value === firstSelectedValue);
168
+ return firstSelectedOption?.label || null;
169
+ }
170
+ else if (Array.isArray(value) && value?.length > 0) {
171
+ const firstSelectedValue = value[0];
172
+ const firstSelectedOption = options?.find(opt => opt?.value === firstSelectedValue);
173
+ return firstSelectedOption?.label || null;
174
+ }
175
+ }
176
+ return null;
177
+ }, [isMulti, value, tempValue, options]);
32
178
 
33
- const renderHeader = () => {
34
- if (!label && !tooltip) return null;
35
-
179
+ const renderHeader = useCallback(() => {
180
+ if (!headerLabel && !tooltip) return null;
36
181
  return (
37
- <HeaderWrapper className={disabled ? 'disabled' : ''}>
38
- {label && (
39
- <CapLabel type="label16" className={disabled ? 'disabled' : ''}>
40
- {label}
41
- </CapLabel>
42
- )}
182
+ <>
183
+ <HeaderWrapper className={classnames(disabled ? 'disabled' : '', 'cap-unified-select-header')}>
184
+ {headerLabel && <CapLabel type="label16" className={classnames(disabled ? 'disabled' : '', 'cap-unified-select-header-label')}>{headerLabel}</CapLabel>}
43
185
  {tooltip && (
44
- <CapTooltip title={tooltip}>
45
- <StyledInfoIcon className={disabled ? 'disabled' : ''} />
46
- </CapTooltip>
186
+ <CapTooltipWithInfo
187
+ title={tooltip}
188
+ className={classnames(disabled ? 'disabled' : '', 'cap-unified-select-header-tooltip')}
189
+ iconProps={{ disabled }}
190
+ />
47
191
  )}
48
192
  </HeaderWrapper>
193
+ <div className="cap-unified-select-header-byline-text">
194
+ {bylineText && <CapLabel className={classnames(disabled ? 'disabled' : '', 'cap-unified-select-header-byline-text')}>{bylineText}</CapLabel>}
195
+ </div>
196
+ </>
49
197
  );
50
- };
198
+ }, [headerLabel, tooltip, bylineText, disabled]);
199
+
200
+ const renderDropdown = useCallback(() => {
201
+ const currentItems = filteredTree;
202
+ const selectedCount = Array.isArray(tempValue)
203
+ ? isTree
204
+ ? tempValue?.filter(val => leafValues?.includes(val))?.length || 0
205
+ : tempValue?.length || 0
206
+ : (tempValue ? 1 : 0);
207
+
208
+ const renderCustomDropdown = useCallback(menu => {
209
+ if (!customPopupRender) return menu;
51
210
 
52
- const renderDropdown = () => {
53
- if (type === 'treeSelect' || type === 'multiTreeSelect') {
54
211
  return (
55
- <TreeSelect
56
- treeData={treeData || options}
57
- value={value}
58
- onChange={onChange}
212
+ <div className={classnames(popOverClassName, `${type}-popup-container`)}>
213
+ {showSearch && (
214
+ <CapRow className={classnames("cap-unified-select-search-container")} align="middle">
215
+ <Input
216
+ prefix={<CapIcon type="search" size="s" style={{ color: styledVars.CAP_G06 }} />}
217
+ placeholder="Search"
218
+ variant="borderless"
219
+ value={searchText}
220
+ onChange={e => setSearchText(e.target.value)}
221
+ />
222
+ </CapRow>
223
+ )}
224
+ {isMulti && showUpload && (
225
+ <CapRow className={classnames("cap-unified-select-upload-container")} align="middle" onClick={onUpload}>
226
+ <CapIcon type="upload" size="s" style={{ color: styledVars.CAP_SECONDARY.base }}/>
227
+ <CapLabel type="label14" className={classnames("cap-unified-select-upload-label")}>Upload</CapLabel>
228
+ </CapRow>
229
+ )}
230
+ {isMulti && currentItems.length > 0 && (
231
+ <CapRow
232
+ className={classnames("cap-unified-select-select-all-container")}
233
+ onClick={handleSelectAll}
234
+ align="middle"
235
+ gap={7}
236
+ >
237
+ <input readOnly type="checkbox" checked={allSelected} className={classnames("cap-unified-select-select-all-checkbox")} onClick={handleSelectAll} />
238
+ <CapLabel type="label14" className={classnames("cap-unified-select-select-all-label")}>Select all</CapLabel>
239
+ </CapRow>
240
+ )}
241
+
242
+ {currentItems.length === 0 ? <NoResult noResultCustomText={noResultCustomText} className={classnames(className, "cap-unified-select-no-result")} showUpload={showUpload} options={options}/> : menu}
243
+
244
+ {currentItems.length > 0 && isMulti && (
245
+ <div className="cap-unified-select-confirm-container">
246
+ <div className="cap-unified-select-confirm-button-group">
247
+ <Button
248
+ type="primary"
249
+ size="small"
250
+ className="cap-unified-select-confirm-button"
251
+ onClick={handleConfirm}
252
+ >
253
+ Confirm
254
+ </Button>
255
+ <Button
256
+ type="text"
257
+ className="cap-unified-select-cancel-button"
258
+ size="small"
259
+ onClick={handleCancel}
260
+ >
261
+ Cancel
262
+ </Button>
263
+ <CapLabel className="cap-unified-select-selected-count">
264
+ {selectedCount} selected
265
+ </CapLabel>
266
+ </div>
267
+ </div>
268
+
269
+ )}
270
+ </div>
271
+ );
272
+ }, [customPopupRender, popOverClassName, type, showSearch, searchText, isMulti, showUpload, currentItems?.length, allSelected, className, noResultCustomText, onUpload, handleSelectAll, handleConfirm, handleCancel]);
273
+
274
+ return (
275
+ <>
276
+ <StyledTreeSelect
277
+ {...rest}
278
+ type={type}
279
+ treeData={filteredTree}
280
+ value={customPopupRender ? tempValue : value}
281
+ onChange={customPopupRender ? handleTempChange : onChange}
59
282
  placeholder={placeholder}
60
- className={className}
283
+ showSearch={false}
284
+ maxTagCount={0}
285
+ maxTagPlaceholder={() => null}
286
+ prefix={tempValue?.length > 0 ? prefix : undefined}
287
+ suffixIcon={suffix}
288
+ className={classnames(`cap-unified-tree-select cap-unified-tree-select-${size}`, className)}
61
289
  style={style}
290
+ status={isError ? 'error' : ''}
62
291
  allowClear={allowClear}
63
- showSearch={showSearch}
64
- multiple={type === 'multiTreeSelect' ? true : false}
292
+ multiple={isMulti}
293
+ treeCheckable={isMulti}
294
+ open={dropdownOpen}
295
+ onOpenChange={handleDropdownVisibilityChange}
296
+ showCheckedStrategy={TreeSelect.SHOW_PARENT}
65
297
  virtual
66
- treeDefaultExpandAll
67
298
  disabled={disabled}
299
+ filterTreeNode={false}
68
300
  {...treeSelectVirtualizationProps}
301
+ popupRender={renderCustomDropdown}
69
302
  />
70
- );
71
- }
72
-
73
- return (
74
- <Select
75
- value={value}
76
- onChange={onChange}
77
- placeholder={placeholder}
78
- className={className}
79
- style={style}
80
- allowClear={allowClear}
81
- showSearch={showSearch}
82
- options={options}
83
- mode={type === 'multiSelect' ? 'multiple' : undefined}
84
- virtual
85
- disabled={disabled}
86
- {...selectVirtualizationProps}
87
- />
303
+ {isError && <CapLabel className={classnames("cap-unified-select-status")} style={{ color: 'red' }}>{errorMessage}</CapLabel>}
304
+ </>
88
305
  );
89
- };
306
+ }, [filteredTree, tempValue, value, prefix, suffix, className, style, isError, errorMessage, allowClear, isMulti, dropdownOpen, customPopupRender, handleTempChange, onChange, disabled, handleDropdownVisibilityChange, treeSelectVirtualizationProps]);
90
307
 
91
308
  return (
92
- <SelectWrapper>
309
+ <SelectWrapper className={classnames(className, 'cap-unified-select-container')}>
93
310
  {renderHeader()}
94
311
  {renderDropdown()}
95
312
  </SelectWrapper>
96
313
  );
97
- }
314
+ };
98
315
 
99
316
  CapUnifiedSelect.propTypes = {
100
317
  type: PropTypes.oneOf(['select', 'multiSelect', 'treeSelect', 'multiTreeSelect']),
101
318
  options: PropTypes.array,
102
- treeData: PropTypes.array,
103
319
  value: PropTypes.any,
104
320
  onChange: PropTypes.func,
105
321
  placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
106
322
  className: PropTypes.string,
107
323
  style: PropTypes.object,
108
324
  allowClear: PropTypes.bool,
109
- showSearch: PropTypes.bool,
110
- label: PropTypes.string,
325
+ headerLabel: PropTypes.string,
111
326
  tooltip: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
112
327
  disabled: PropTypes.bool,
328
+ customPopupRender: PropTypes.bool,
329
+ showSearch: PropTypes.bool,
330
+ searchBasedOn: PropTypes.oneOf(['label', 'value', 'key']),
331
+ onConfirm: PropTypes.func,
332
+ onCancel: PropTypes.func,
333
+ isError: PropTypes.bool,
334
+ errorMessage: PropTypes.string,
335
+ popupClassName: PropTypes.string,
336
+ showUpload: PropTypes.bool,
337
+ onUpload: PropTypes.func,
338
+ size: PropTypes.oneOf(['s', 'm', 'l', 'xl']),
113
339
  };
114
340
 
115
341
  CapUnifiedSelect.defaultProps = {
116
342
  type: 'select',
343
+ placeholder: 'Select an option',
344
+ searchBasedOn: 'label',
345
+ noResultCustomText: 'No results found',
346
+ noResultCustomIcon: 'warning',
347
+ options: [],
348
+ size: 'm',
117
349
  allowClear: false,
118
- showSearch: false,
350
+ customPopupRender: true,
351
+ showSearch: true,
352
+ className: '',
353
+ popupClassName: '',
354
+ disabled: false,
355
+ showUpload: false,
356
+ isError: false,
357
+ onUpload: () => {},
358
+ onChange: () => {},
359
+ onConfirm: () => {},
360
+ onCancel: () => {}
119
361
  };
120
362
 
121
- export default CapUnifiedSelect;
363
+ export default withStyles(CapUnifiedSelect, selectStyles);
@@ -1,4 +1 @@
1
- import CapUnifiedSelect from './CapUnifiedSelect';
2
- import CapUnifiedSelectLoadable from './loadable';
3
-
4
- export default CapUnifiedSelectLoadable;
1
+ export { default } from './CapUnifiedSelect';