@chaoswise/ui 3.1.7 → 3.1.8

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 (2) hide show
  1. package/lib/CWSelect/index.js +423 -150
  2. package/package.json +1 -1
@@ -33,7 +33,58 @@ import update from "immutability-helper";
33
33
  import PropTypes from "prop-types";
34
34
  import { useIntl } from "../locales";
35
35
  import useStyle from "./index.style";
36
+
37
+ /**
38
+ * 多选模式的默认配置选项
39
+ * @type {Object}
40
+ * @property {boolean} showCheckbox - 是否显示复选框
41
+ * @property {boolean} showSelectAll - 是否显示全选选项
42
+ * @property {boolean} isCheckboxMultipleHasSelectAll - 复选框多选模式是否包含全选功能
43
+ */
44
+ var DEFAULT_MULTIPLE_OPTIONS = {
45
+ showCheckbox: false,
46
+ showSelectAll: true,
47
+ isCheckboxMultipleHasSelectAll: true
48
+ };
49
+
50
+ /**
51
+ * 安全获取嵌套对象属性值的辅助函数
52
+ * @param {Object} obj - 目标对象
53
+ * @param {Array} path - 属性路径数组
54
+ * @param {*} defaultValue - 默认值
55
+ * @returns {*} 获取到的值或默认值
56
+ */
57
+ var getNestedValue = function getNestedValue(obj, path) {
58
+ var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
59
+ try {
60
+ var _path$reduce;
61
+ return (_path$reduce = path.reduce(function (current, key) {
62
+ return current === null || current === void 0 ? void 0 : current[key];
63
+ }, obj)) !== null && _path$reduce !== void 0 ? _path$reduce : defaultValue;
64
+ } catch (_unused) {
65
+ return defaultValue;
66
+ }
67
+ };
68
+
69
+ /**
70
+ * 检查是否为有效数组(非空数组)的辅助函数
71
+ * @param {*} arr - 待检查的值
72
+ * @returns {boolean} 是否为有效数组
73
+ */
74
+ var isValidArray = function isValidArray(arr) {
75
+ return Array.isArray(arr) && arr.length > 0;
76
+ };
77
+
78
+ /**
79
+ * CWSelect 组件 - 增强版的 Select 选择器
80
+ * 支持多选、拖拽排序、复选框模式、标签自定义等功能
81
+ *
82
+ * @param {Object} props - 组件属性
83
+ * @param {React.Ref} ref - 组件引用
84
+ * @returns {React.Component} CWSelect 组件
85
+ */
36
86
  var CWSelect = /*#__PURE__*/forwardRef(function (props, ref) {
87
+ // 解构 props,设置默认值
37
88
  var selectLabel = props.selectLabel,
38
89
  label = props.label,
39
90
  prefix = props.prefix,
@@ -59,14 +110,12 @@ var CWSelect = /*#__PURE__*/forwardRef(function (props, ref) {
59
110
  maxTagTextLength = props.maxTagTextLength,
60
111
  dropdownRender = props.dropdownRender,
61
112
  _props$multipleOption = props.multipleOptions,
62
- multipleOptions = _props$multipleOption === void 0 ? {
63
- showCheckbox: false,
64
- showSelectAll: true,
65
- isCheckboxMultipleHasSelectAll: true
66
- } : _props$multipleOption,
113
+ multipleOptions = _props$multipleOption === void 0 ? DEFAULT_MULTIPLE_OPTIONS : _props$multipleOption,
67
114
  children = props.children,
68
115
  maxCount = props.maxCount,
69
116
  rest = _objectWithoutProperties(props, _excluded);
117
+
118
+ // 获取国际化和样式相关的 hooks
70
119
  var intl = useIntl();
71
120
  var _useConfigContext = useConfigContext(),
72
121
  getPrefixCls = _useConfigContext.getPrefixCls;
@@ -76,124 +125,227 @@ var CWSelect = /*#__PURE__*/forwardRef(function (props, ref) {
76
125
  wrapCSSVar = _useStyle2[0],
77
126
  hashId = _useStyle2[1],
78
127
  cssVarCls = _useStyle2[2];
79
- var keyIndexMap = useRef({});
128
+
129
+ // 状态管理
130
+ var keyIndexMapRef = useRef({}); // 存储键值索引映射,避免重新渲染
80
131
  var _useState = useState([]),
81
132
  _useState2 = _slicedToArray(_useState, 2),
82
133
  allInstance = _useState2[0],
83
- setAllInstance = _useState2[1];
134
+ setAllInstance = _useState2[1]; // 所有实例数组
84
135
  var _useState3 = useState([]),
85
136
  _useState4 = _slicedToArray(_useState3, 2),
86
137
  allOptions = _useState4[0],
87
- setAllOptions = _useState4[1];
138
+ setAllOptions = _useState4[1]; // 所有选项数组
88
139
  var _useState5 = useState([]),
89
140
  _useState6 = _slicedToArray(_useState5, 2),
90
141
  activeKey = _useState6[0],
91
- setActiveKey = _useState6[1];
92
- var _prefix = useMemo(function () {
142
+ setActiveKey = _useState6[1]; // 当前激活的键值数组
143
+
144
+ /**
145
+ * 计算前缀标签,优先级:prefix > label > selectLabel
146
+ */
147
+ var computedPrefix = useMemo(function () {
93
148
  return prefix || label || selectLabel;
94
149
  }, [prefix, label, selectLabel]);
95
- var _showCheckbox = useMemo(function () {
150
+
151
+ /**
152
+ * 判断是否应该显示复选框
153
+ * 条件:多选模式 && (showCheckbox 为 true 或 multipleOptions.showCheckbox 为 true)
154
+ */
155
+ var shouldShowCheckbox = useMemo(function () {
96
156
  return mode === "multiple" && (showCheckbox || multipleOptions.showCheckbox);
97
157
  }, [mode, showCheckbox, multipleOptions.showCheckbox]);
98
- var _showCheckAll = useMemo(function () {
99
- return multipleOptions.isCheckboxMultipleHasSelectAll && !("maxCount" in props);
100
- }, [multipleOptions.isCheckboxMultipleHasSelectAll, maxCount]);
101
- useEffect(function () {
102
- if (mode) {
103
- getMapFromArray(defaultValue);
104
- }
105
- }, [defaultValue]);
106
- useEffect(function () {
107
- if (mode) {
108
- getMapFromArray(value);
109
- }
110
- }, [value]);
158
+
159
+ /**
160
+ * 判断是否应该显示全选功能
161
+ * 条件:配置允许全选 && 没有设置最大选择数量 && 显示复选框
162
+ */
163
+ var shouldShowCheckAll = useMemo(function () {
164
+ return multipleOptions.isCheckboxMultipleHasSelectAll && !("maxCount" in props) && shouldShowCheckbox;
165
+ }, [multipleOptions.isCheckboxMultipleHasSelectAll, props, shouldShowCheckbox]);
166
+
167
+ /**
168
+ * 从数组值构建键值索引映射的函数
169
+ * @param {Array|*} _value - 输入值
170
+ */
111
171
  var getMapFromArray = useCallback(function (_value) {
112
- setActiveKey(_value || []);
113
- if (!_value) return;
114
- var value = Array.isArray(_value) ? _value : [_value];
115
- keyIndexMap.current = {};
116
- var allKeys = value.map(function (item, index) {
117
- var key = labelInValue ? item.value : item;
118
- keyIndexMap.current[key] = {
119
- index: index
120
- };
172
+ var normalizedValue = _value || [];
173
+ setActiveKey(normalizedValue);
174
+
175
+ // 如果没有值,清空映射和实例
176
+ if (!_value) {
177
+ keyIndexMapRef.current = {};
178
+ setAllInstance([]);
179
+ return;
180
+ }
181
+
182
+ // 确保值是数组格式
183
+ var valueArray = Array.isArray(_value) ? _value : [_value];
184
+ var newKeyIndexMap = {};
185
+
186
+ // 构建键值数组和索引映射
187
+ var allKeys = valueArray.map(function (item, index) {
188
+ var key = labelInValue ? item === null || item === void 0 ? void 0 : item.value : item;
189
+ if (key !== undefined && key !== null) {
190
+ newKeyIndexMap[key] = {
191
+ index: index
192
+ };
193
+ }
121
194
  return key;
195
+ }).filter(function (key) {
196
+ return key !== undefined && key !== null;
122
197
  });
198
+ keyIndexMapRef.current = newKeyIndexMap;
199
+ // 使用 setTimeout 避免同步更新导致的性能问题
123
200
  setTimeout(function () {
124
201
  return setAllInstance(allKeys);
125
- });
202
+ }, 0);
126
203
  }, [labelInValue]);
127
- var _onChange = function _onChange(newValue, option) {
204
+
205
+ /**
206
+ * 监听默认值变化,初始化键值映射
207
+ */
208
+ useEffect(function () {
209
+ if (mode && defaultValue !== undefined) {
210
+ getMapFromArray(defaultValue);
211
+ }
212
+ }, [defaultValue, mode, getMapFromArray]);
213
+
214
+ /**
215
+ * 监听值变化,更新键值映射
216
+ */
217
+ useEffect(function () {
218
+ if (mode && value !== undefined) {
219
+ getMapFromArray(value);
220
+ }
221
+ }, [value, mode, getMapFromArray]);
222
+
223
+ /**
224
+ * 处理值变化的回调函数
225
+ * @param {*} newValue - 新值
226
+ * @param {*} option - 选项数据
227
+ */
228
+ var handleChange = useCallback(function (newValue, option) {
128
229
  onChange(newValue, option);
129
- setAllOptions(option);
130
- if (value) return;
131
- if (mode) {
230
+ setAllOptions(Array.isArray(option) ? option : []);
231
+
232
+ // 只有在非受控模式下才更新内部状态
233
+ if (!("value" in props) && mode) {
132
234
  getMapFromArray(newValue);
133
235
  }
134
- };
135
- var onPreventMouseDown = function onPreventMouseDown(e) {
236
+ }, [onChange, props, mode, getMapFromArray]);
237
+
238
+ /**
239
+ * 阻止鼠标按下事件的默认行为和冒泡
240
+ * @param {Event} e - 事件对象
241
+ */
242
+ var handlePreventMouseDown = useCallback(function (e) {
136
243
  e.preventDefault();
137
244
  e.stopPropagation();
138
- };
139
- var renderTag = useCallback(function (props) {
140
- var _disabled = rest.disabled || props.disabled;
245
+ }, []);
246
+
247
+ /**
248
+ * 渲染普通标签的函数
249
+ * @param {Object} tagProps - 标签属性
250
+ * @returns {React.Component|null} 标签组件
251
+ */
252
+ var renderTag = useCallback(function (tagProps) {
253
+ if (!(tagProps !== null && tagProps !== void 0 && tagProps.value)) return null;
254
+ var isDisabled = rest.disabled || tagProps.disabled;
141
255
  return /*#__PURE__*/React.createElement(CustomTag, {
142
- tagProps: props,
143
- key: props.value,
256
+ tagProps: tagProps,
257
+ key: tagProps.value,
144
258
  maxTagTextLength: maxTagTextLength,
145
259
  tagLabelRender: tagLabelRender,
146
- closeIcon: !_disabled
260
+ closeIcon: !isDisabled
147
261
  });
148
262
  }, [maxTagTextLength, tagLabelRender, rest.disabled]);
263
+
264
+ /**
265
+ * 移动节点的函数,用于拖拽排序
266
+ * @param {number} dragIndex - 拖拽项索引
267
+ * @param {number} hoverIndex - 悬停项索引
268
+ */
149
269
  var moveNode = useCallback(function (dragIndex, hoverIndex) {
150
- var dropChange = allInstance[dragIndex];
151
- var dragChange = allInstance[hoverIndex];
152
- var dragSortInstance = update(allInstance, {
153
- $splice: [[dragIndex, 1, dragChange], [hoverIndex, 1, dropChange]]
270
+ // 如果索引相同或实例数组无效,直接返回
271
+ if (dragIndex === hoverIndex || !isValidArray(allInstance)) return;
272
+ var dragItem = allInstance[dragIndex];
273
+ var hoverItem = allInstance[hoverIndex];
274
+ if (!dragItem || !hoverItem) return;
275
+
276
+ // 使用 immutability-helper 进行不可变更新
277
+ var newInstance = update(allInstance, {
278
+ $splice: [[dragIndex, 1], [hoverIndex, 0, dragItem]]
154
279
  });
155
- var dragAllOptions = dragSortInstance.map(function (item) {
156
- var findOption = allOptions.find(function (i) {
157
- return i.value === item;
280
+
281
+ // 重新排序选项数组
282
+ var newOptions = newInstance.map(function (item) {
283
+ return allOptions.find(function (option) {
284
+ return (option === null || option === void 0 ? void 0 : option.value) === item;
158
285
  });
159
- return findOption;
160
- });
161
- onDragEnd(dragSortInstance);
162
- _onChange(dragSortInstance, dragAllOptions, true);
163
- }, [allInstance]);
286
+ }).filter(Boolean);
287
+ onDragEnd(newInstance);
288
+ handleChange(newInstance, newOptions);
289
+ }, [allInstance, allOptions, onDragEnd, handleChange]);
290
+
291
+ /**
292
+ * 标签下拉框覆盖层内容,用于显示超出最大数量的标签
293
+ */
164
294
  var tagDropdownOverlay = useMemo(function () {
295
+ if (!isValidArray(allInstance) || maxTagCount === undefined) {
296
+ return [];
297
+ }
298
+
299
+ // 获取超出最大数量的标签
165
300
  return allInstance.slice(maxTagCount).map(function (key) {
166
- var props = keyIndexMap.current[key];
167
- if (!props) return null;
168
- var _disabled = rest.disabled || props.disabled;
301
+ var tagProps = keyIndexMapRef.current[key];
302
+ if (!tagProps) return null;
303
+ var isDisabled = rest.disabled || tagProps.disabled;
169
304
  return /*#__PURE__*/React.createElement(CustomTag, {
170
- tagProps: props,
305
+ tagProps: tagProps,
171
306
  key: key,
172
307
  maxTagTextLength: maxTagTextLength,
173
308
  tagLabelRender: tagLabelRender,
174
- closeIcon: !_disabled
309
+ closeIcon: !isDisabled
175
310
  });
176
- });
311
+ }).filter(Boolean);
177
312
  }, [allInstance, maxTagCount, tagLabelRender, maxTagTextLength, rest.disabled]);
178
- var _tagRender = function _tagRender(props) {
179
- var node = keyIndexMap.current[props.value];
180
- if (node) {
181
- keyIndexMap.current[props.value] = _objectSpread(_objectSpread({}, keyIndexMap.current[props.value]), props);
313
+
314
+ /**
315
+ * 自定义标签渲染函数
316
+ * 处理拖拽、标签数量限制等逻辑
317
+ * @param {Object} tagProps - 标签属性
318
+ * @returns {React.Component|null} 渲染的标签组件
319
+ */
320
+ var customTagRender = useCallback(function (tagProps) {
321
+ if (!(tagProps !== null && tagProps !== void 0 && tagProps.value)) return null;
322
+ var currentNode = keyIndexMapRef.current[tagProps.value];
323
+
324
+ // 更新键值索引映射
325
+ if (currentNode) {
326
+ keyIndexMapRef.current[tagProps.value] = _objectSpread(_objectSpread({}, currentNode), tagProps);
182
327
  }
328
+
329
+ // 拖拽模式:返回可拖拽的标签
183
330
  if (allowDrag) {
184
- var _disabled = rest.disabled || props.disabled;
331
+ var isDisabled = rest.disabled || tagProps.disabled;
185
332
  return /*#__PURE__*/React.createElement(DragTag, {
186
- tagProps: keyIndexMap.current[props.value],
333
+ tagProps: keyIndexMapRef.current[tagProps.value],
187
334
  maxTagTextLength: maxTagTextLength,
188
335
  tagLabelRender: tagLabelRender,
189
- closeIcon: !_disabled,
336
+ closeIcon: !isDisabled,
190
337
  moveNode: moveNode
191
338
  });
192
339
  }
340
+
341
+ // 无限制标签数量:直接渲染标签
193
342
  if (maxTagCount === undefined) {
194
- return renderTag(props);
343
+ return renderTag(tagProps);
195
344
  }
196
- if (node && node.index === maxTagCount) {
345
+
346
+ // 处理超出数量的标签:显示 "+N" 的聚合标签
347
+ if ((currentNode === null || currentNode === void 0 ? void 0 : currentNode.index) === maxTagCount) {
348
+ var extraCount = Math.max(0, Object.keys(keyIndexMapRef.current).length - maxTagCount);
197
349
  return /*#__PURE__*/React.createElement(_Tooltip, {
198
350
  overlayClassName: "".concat(prefixCls, "-tag-tooltip-wrapper"),
199
351
  overlay: tagDropdownOverlay,
@@ -202,90 +354,142 @@ var CWSelect = /*#__PURE__*/forwardRef(function (props, ref) {
202
354
  className: "".concat(prefixCls, "-custom-tag"),
203
355
  type: "custom_type01",
204
356
  closeIcon: !rest.disabled,
205
- onMouseDown: onPreventMouseDown,
206
- onClose: function onClose(e) {
207
- var filterAllInstance = activeKey.slice(0, maxTagCount);
208
- var filterAllOptions = allOptions.slice(0, maxTagCount);
209
- _onChange(filterAllInstance, filterAllOptions);
210
- onDeselect(activeKey.slice(maxTagCount), allOptions.slice(maxTagCount));
357
+ onMouseDown: handlePreventMouseDown,
358
+ onClose: function onClose() {
359
+ // 关闭聚合标签时,移除所有超出数量的标签
360
+ var filteredInstance = activeKey.slice(0, maxTagCount);
361
+ var filteredOptions = allOptions.slice(0, maxTagCount);
362
+ var removedKeys = activeKey.slice(maxTagCount);
363
+ var removedOptions = allOptions.slice(maxTagCount);
364
+ handleChange(filteredInstance, filteredOptions);
365
+ onDeselect(removedKeys, removedOptions);
211
366
  }
212
- }, "+", Object.keys(keyIndexMap.current).slice(maxTagCount).length));
367
+ }, "+", extraCount));
213
368
  }
214
- if (node && node.index < maxTagCount) {
215
- return renderTag(props);
369
+
370
+ // 正常显示的标签(在最大数量范围内)
371
+ if ((currentNode === null || currentNode === void 0 ? void 0 : currentNode.index) < maxTagCount) {
372
+ return renderTag(tagProps);
216
373
  }
217
- };
218
- var onCheckAllChange = function onCheckAllChange(e, currentDataSource, originDataSource) {
219
- var _activeKey = [];
220
- var activeDataSource = [];
374
+ return null;
375
+ }, [allowDrag, maxTagCount, renderTag, moveNode, tagDropdownOverlay, prefixCls, rest.disabled, handlePreventMouseDown, activeKey, allOptions, handleChange, onDeselect, maxTagTextLength, tagLabelRender]);
376
+
377
+ /**
378
+ * 处理全选/取消全选的回调函数
379
+ * @param {Event} e - 复选框事件
380
+ * @param {Array} currentDataSource - 当前数据源
381
+ * @param {Array} originDataSource - 原始数据源
382
+ */
383
+ var handleCheckAllChange = useCallback(function (e, currentDataSource, originDataSource) {
384
+ if (!isValidArray(currentDataSource)) return;
385
+ var isChecked = e.target.checked;
386
+
387
+ // 获取原始激活的数据源
221
388
  var originActiveDataSource = originDataSource.filter(function (item) {
222
- return activeKey.find(function (i) {
223
- return labelInValue ? i.value === item.value : i === item.value;
389
+ return activeKey.some(function (activeItem) {
390
+ return labelInValue ? (activeItem === null || activeItem === void 0 ? void 0 : activeItem.value) === (item === null || item === void 0 ? void 0 : item.value) : activeItem === (item === null || item === void 0 ? void 0 : item.value);
224
391
  });
225
392
  });
226
- var currentDataSourceKey = currentDataSource.map(function (item) {
393
+
394
+ // 转换当前数据源为键值格式
395
+ var currentDataSourceKeys = currentDataSource.map(function (item) {
227
396
  return labelInValue ? {
228
- value: item.value,
229
- label: item.children
230
- } : item.value;
231
- });
232
- if (e.target.checked) {
233
- activeDataSource = Array.from(new Set([].concat(_toConsumableArray(originActiveDataSource), _toConsumableArray(currentDataSource))));
234
- _activeKey = Array.from(new Set([].concat(_toConsumableArray(activeKey), _toConsumableArray(currentDataSourceKey))));
235
- onSelect(currentDataSourceKey, currentDataSource);
397
+ value: item === null || item === void 0 ? void 0 : item.value,
398
+ label: item === null || item === void 0 ? void 0 : item.children
399
+ } : item === null || item === void 0 ? void 0 : item.value;
400
+ }).filter(Boolean);
401
+ var newActiveKey = [];
402
+ var newActiveDataSource = [];
403
+ if (isChecked) {
404
+ // 全选:合并原有选中项和当前数据源
405
+ newActiveDataSource = Array.from(new Set([].concat(_toConsumableArray(originActiveDataSource), _toConsumableArray(currentDataSource))));
406
+ newActiveKey = Array.from(new Set([].concat(_toConsumableArray(activeKey), _toConsumableArray(currentDataSourceKeys))));
407
+ onSelect(currentDataSourceKeys, currentDataSource);
236
408
  } else {
237
- activeDataSource = originActiveDataSource.filter(function (item) {
238
- return !currentDataSource.find(function (i) {
239
- return i.value === item.value;
409
+ // 取消全选:从原有选中项中移除当前数据源
410
+ newActiveDataSource = originActiveDataSource.filter(function (item) {
411
+ return !currentDataSource.some(function (current) {
412
+ return (current === null || current === void 0 ? void 0 : current.value) === (item === null || item === void 0 ? void 0 : item.value);
240
413
  });
241
414
  });
242
- _activeKey = activeKey.filter(function (item) {
243
- return !currentDataSourceKey.find(function (i) {
244
- return labelInValue ? item.value === i.value : item === i;
415
+ newActiveKey = activeKey.filter(function (item) {
416
+ return !currentDataSourceKeys.some(function (current) {
417
+ return labelInValue ? (item === null || item === void 0 ? void 0 : item.value) === (current === null || current === void 0 ? void 0 : current.value) : item === current;
245
418
  });
246
419
  });
247
- onDeselect(currentDataSourceKey, currentDataSource);
420
+ onDeselect(currentDataSourceKeys, currentDataSource);
248
421
  }
249
- _onChange(_activeKey, activeDataSource);
250
- };
251
- var customDropdownRender = function customDropdownRender(originNode) {
252
- var _originNode$_owner, _originNode$_owner2, _originNode$_owner3;
253
- var flattenOptions = (originNode === null || originNode === void 0 || (_originNode$_owner = originNode._owner) === null || _originNode$_owner === void 0 || (_originNode$_owner = _originNode$_owner.dependencies) === null || _originNode$_owner === void 0 || (_originNode$_owner = _originNode$_owner.firstContext) === null || _originNode$_owner === void 0 || (_originNode$_owner = _originNode$_owner.memoizedValue) === null || _originNode$_owner === void 0 ? void 0 : _originNode$_owner.flattenOptions) || [];
254
- var originDataSource = (originNode === null || originNode === void 0 || (_originNode$_owner2 = originNode._owner) === null || _originNode$_owner2 === void 0 || (_originNode$_owner2 = _originNode$_owner2.dependencies) === null || _originNode$_owner2 === void 0 || (_originNode$_owner2 = _originNode$_owner2.firstContext) === null || _originNode$_owner2 === void 0 || (_originNode$_owner2 = _originNode$_owner2.memoizedValue) === null || _originNode$_owner2 === void 0 ? void 0 : _originNode$_owner2.options) || [];
422
+ handleChange(newActiveKey, newActiveDataSource);
423
+ }, [activeKey, labelInValue, onSelect, onDeselect, handleChange]);
424
+
425
+ /**
426
+ * 自定义下拉框渲染函数
427
+ * 处理复选框模式下的全选功能和空状态显示
428
+ * @param {React.ReactNode} originNode - 原始下拉框节点
429
+ * @returns {React.ReactNode} 自定义渲染的下拉框内容
430
+ */
431
+ var customDropdownRender = useCallback(function (originNode) {
432
+ // 从 React 内部属性中获取下拉框的状态数据
433
+ var memoizedProps = getNestedValue(originNode, ["_owner", "return", "memoizedProps", "value"], {});
434
+ var flattenOptions = memoizedProps.flattenOptions || [];
435
+ var originDataSource = memoizedProps.options || [];
436
+ var activeValues = Array.from(memoizedProps.rawValues || []);
437
+
438
+ // 提取有效的数据源(过滤掉分组选项)
255
439
  var dataSource = flattenOptions.filter(function (item) {
256
- return item.hasOwnProperty("groupOption");
440
+ return item && Object.prototype.hasOwnProperty.call(item, "groupOption");
257
441
  }).map(function (item) {
258
442
  return item.data;
443
+ }).filter(Boolean);
444
+
445
+ // 空状态处理:没有数据时显示空状态提示
446
+ if (!isValidArray(dataSource)) {
447
+ var emptyContent = rest.notFoundContent || /*#__PURE__*/React.createElement("div", {
448
+ className: "".concat(prefixCls, "-show-checkbox-empty")
449
+ }, intl.get("812f44fc-ed3c-4bd3-8ad5-35cd2996c5ae").d("未查找到任何结果"));
450
+ return emptyContent;
451
+ }
452
+
453
+ // 计算全选状态
454
+ var dataSourceValues = dataSource.map(function (item) {
455
+ return item === null || item === void 0 ? void 0 : item.value;
456
+ }).filter(Boolean);
457
+ var filterActiveValues = dataSourceValues.filter(function (value) {
458
+ return activeValues.includes(value);
259
459
  });
260
- var activeValues = Array.from(originNode === null || originNode === void 0 || (_originNode$_owner3 = originNode._owner) === null || _originNode$_owner3 === void 0 || (_originNode$_owner3 = _originNode$_owner3.dependencies) === null || _originNode$_owner3 === void 0 || (_originNode$_owner3 = _originNode$_owner3.firstContext) === null || _originNode$_owner3 === void 0 || (_originNode$_owner3 = _originNode$_owner3.memoizedValue) === null || _originNode$_owner3 === void 0 ? void 0 : _originNode$_owner3.rawValues);
261
- var filterActiveValues = dataSource.map(function (item) {
262
- return item.value;
263
- }).filter(function (item) {
264
- return activeValues.includes(item);
265
- });
266
- var notFoundContent = rest.notFoundContent || /*#__PURE__*/React.createElement("div", {
267
- className: "".concat(prefixCls, "-show-checkbox-empty")
268
- }, intl.get("812f44fc-ed3c-4bd3-8ad5-35cd2996c5ae").d("未查找到任何结果"));
269
- return !dataSource.length ? notFoundContent : /*#__PURE__*/React.createElement(React.Fragment, null, _showCheckAll && /*#__PURE__*/React.createElement("div", {
460
+ var isAllSelected = filterActiveValues.length === dataSource.length && dataSource.length > 0;
461
+ var isIndeterminate = filterActiveValues.length > 0 && !isAllSelected;
462
+ return /*#__PURE__*/React.createElement(React.Fragment, null, shouldShowCheckAll && /*#__PURE__*/React.createElement("div", {
270
463
  className: "".concat(prefixCls, "-check-all"),
271
- onMouseDown: onPreventMouseDown
464
+ onMouseDown: handlePreventMouseDown
272
465
  }, /*#__PURE__*/React.createElement(_Checkbox, {
273
- indeterminate: filterActiveValues.length && filterActiveValues.length !== dataSource.length,
274
- checked: filterActiveValues.length === dataSource.length,
466
+ indeterminate: isIndeterminate // 半选状态
467
+ ,
468
+ checked: isAllSelected // 全选状态
469
+ ,
275
470
  onChange: function onChange(e) {
276
- return onCheckAllChange(e, dataSource, originDataSource);
471
+ return handleCheckAllChange(e, dataSource, originDataSource);
277
472
  },
278
473
  style: {
279
474
  width: "100%"
280
475
  }
281
476
  }, intl.get("3991a1d0-65a8-45ca-94a4-f89fbd9f8555").d("全选"), "\uFF08", filterActiveValues.length, "/", dataSource.length, "\uFF09")), originNode);
282
- };
283
- var renderCheckboxItem = function renderCheckboxItem(child) {
284
- var isChecked = labelInValue ? activeKey.map(function (item) {
285
- return item.value;
286
- }).includes(child.props.value) : activeKey.includes(child.props.value);
477
+ }, [shouldShowCheckAll, prefixCls, handlePreventMouseDown, handleCheckAllChange, intl, rest.notFoundContent]);
478
+
479
+ /**
480
+ * 渲染复选框选项的函数
481
+ * @param {React.ReactElement} child - 子选项元素
482
+ * @returns {React.ReactElement|null} 渲染的复选框选项
483
+ */
484
+ var renderCheckboxItem = useCallback(function (child) {
485
+ if (!(child !== null && child !== void 0 && child.props)) return null;
486
+ var childValue = child.props.value;
487
+ // 判断当前选项是否被选中
488
+ var isChecked = labelInValue ? activeKey.some(function (item) {
489
+ return (item === null || item === void 0 ? void 0 : item.value) === childValue;
490
+ }) : activeKey.includes(childValue);
287
491
  return /*#__PURE__*/React.createElement("div", {
288
- key: child.key,
492
+ key: child.key || childValue,
289
493
  className: "".concat(prefixCls, "-checkbox-item")
290
494
  }, /*#__PURE__*/React.createElement(_Checkbox, {
291
495
  disabled: child.props.disabled,
@@ -293,62 +497,131 @@ var CWSelect = /*#__PURE__*/forwardRef(function (props, ref) {
293
497
  }), /*#__PURE__*/React.createElement("span", {
294
498
  className: "".concat(prefixCls, "-item-children")
295
499
  }, child.props.children));
296
- };
297
- var finalChildren = children;
298
- if (_showCheckbox) {
299
- finalChildren = React.Children.map(children, function (child) {
300
- if (Array.isArray(child.props.children)) {
500
+ }, [activeKey, labelInValue, prefixCls]);
501
+
502
+ /**
503
+ * 处理子元素,在复选框模式下为每个选项添加复选框
504
+ */
505
+ var processedChildren = useMemo(function () {
506
+ if (!shouldShowCheckbox || !children) return children;
507
+ return React.Children.map(children, function (child) {
508
+ var _child$props, _child$props2;
509
+ if (!child) return null;
510
+
511
+ // 处理选项组(OptGroup)
512
+ if (Array.isArray((_child$props = child.props) === null || _child$props === void 0 ? void 0 : _child$props.children)) {
301
513
  var groupChildren = React.Children.map(child.props.children, function (optGroupChild) {
302
- return /*#__PURE__*/React.cloneElement(optGroupChild, {}, renderCheckboxItem(optGroupChild));
303
- });
304
- return /*#__PURE__*/React.cloneElement(child, {}, groupChildren);
514
+ var _optGroupChild$props;
515
+ if (!optGroupChild) return null;
516
+ return /*#__PURE__*/React.cloneElement(optGroupChild, {
517
+ key: optGroupChild.key || ((_optGroupChild$props = optGroupChild.props) === null || _optGroupChild$props === void 0 ? void 0 : _optGroupChild$props.value)
518
+ }, renderCheckboxItem(optGroupChild));
519
+ }).filter(Boolean);
520
+ return /*#__PURE__*/React.cloneElement(child, {
521
+ key: child.key
522
+ }, groupChildren);
305
523
  }
306
- return /*#__PURE__*/React.cloneElement(child, {}, renderCheckboxItem(child));
307
- });
308
- }
524
+
525
+ // 处理普通选项
526
+ return /*#__PURE__*/React.cloneElement(child, {
527
+ key: child.key || ((_child$props2 = child.props) === null || _child$props2 === void 0 ? void 0 : _child$props2.value)
528
+ }, renderCheckboxItem(child));
529
+ }).filter(Boolean);
530
+ }, [shouldShowCheckbox, children, renderCheckboxItem]);
531
+
532
+ /**
533
+ * 计算最终的 value 值
534
+ * 受控模式:使用传入的 value
535
+ * 非受控模式:根据是否显示复选框或允许拖拽来决定使用 activeKey 还是 value
536
+ */
537
+ var finalValue = useMemo(function () {
538
+ if ("value" in props) {
539
+ return value;
540
+ }
541
+ return shouldShowCheckbox || allowDrag ? activeKey : value;
542
+ }, [props, value, shouldShowCheckbox, allowDrag, activeKey]);
543
+
544
+ // 渲染组件
309
545
  return wrapCSSVar(/*#__PURE__*/React.createElement(DndProvider, {
310
546
  backend: HTML5Backend
311
547
  }, /*#__PURE__*/React.createElement(Select, _extends({
312
548
  ref: ref,
313
- prefix: _prefix,
549
+ prefix: computedPrefix,
314
550
  defaultValue: defaultValue,
315
- value: "value" in props ? value : showCheckbox || allowDrag ? activeKey : value,
551
+ value: finalValue,
316
552
  mode: mode,
317
- onChange: _onChange,
318
- tagRender: tagRender || _tagRender,
553
+ onChange: handleChange,
554
+ tagRender: tagRender || customTagRender,
319
555
  menuItemSelectedIcon: null,
320
556
  onSelect: onSelect,
321
557
  onDeselect: onDeselect,
322
- children: finalChildren,
323
- dropdownRender: dropdownRender !== null && dropdownRender !== void 0 ? dropdownRender : _showCheckbox ? customDropdownRender : null,
558
+ dropdownRender: dropdownRender !== null && dropdownRender !== void 0 ? dropdownRender : shouldShowCheckbox ? customDropdownRender : null,
324
559
  labelInValue: labelInValue,
325
560
  maxCount: maxCount
326
- }, rest))));
561
+ }, rest), processedChildren)));
327
562
  });
563
+
564
+ // PropTypes 类型定义
328
565
  CWSelect.propTypes = {
329
566
  allowClear: PropTypes.bool,
567
+ // 是否允许清除
330
568
  disabled: PropTypes.bool,
569
+ // 是否禁用
331
570
  dropdownRender: PropTypes.func,
571
+ // 自定义下拉框渲染函数
332
572
  labelInValue: PropTypes.bool,
573
+ // 是否把 label 包装到 value 中
333
574
  maxTagCount: PropTypes.number,
575
+ // 最大标签显示数量
334
576
  maxTagTextLength: PropTypes.number,
577
+ // 标签文本最大长度
335
578
  mode: PropTypes.oneOf(["multiple", "tags"]),
579
+ // 选择模式
336
580
  notFoundContent: PropTypes.node,
581
+ // 无数据时的内容
337
582
  tagLabelRender: PropTypes.func,
583
+ // 标签文本渲染函数
338
584
  menuItemSelectedIcon: PropTypes.node,
585
+ // 选中项图标
339
586
  onChange: PropTypes.func,
587
+ // 值变化回调
340
588
  onDeselect: PropTypes.func,
589
+ // 取消选中回调
341
590
  onPopupScroll: PropTypes.func,
591
+ // 下拉框滚动回调
342
592
  onSearch: PropTypes.func,
593
+ // 搜索回调
343
594
  onSelect: PropTypes.func,
595
+ // 选中回调
344
596
  loading: PropTypes.bool,
597
+ // 是否加载中
345
598
  tagRender: PropTypes.func,
599
+ // 自定义标签渲染函数
346
600
  allowDrag: PropTypes.bool,
601
+ // 是否允许拖拽
347
602
  onDragEnd: PropTypes.func,
603
+ // 拖拽结束回调
348
604
  showCheckbox: PropTypes.bool,
605
+ // 是否显示复选框
349
606
  listHeight: PropTypes.number,
607
+ // 列表高度
350
608
  selectLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
351
- label: PropTypes.oneOfType([PropTypes.string, PropTypes.node])
609
+ // 选择器标签
610
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
611
+ // 标签
612
+ multipleOptions: PropTypes.shape({
613
+ // 多选配置选项
614
+ showCheckbox: PropTypes.bool,
615
+ // 是否显示复选框
616
+ showSelectAll: PropTypes.bool,
617
+ // 是否显示全选
618
+ isCheckboxMultipleHasSelectAll: PropTypes.bool // 复选框多选是否包含全选
619
+ })
352
620
  };
621
+
622
+ // 设置组件显示名称
623
+ CWSelect.displayName = "CWSelect";
624
+
625
+ // 克隆原始 Select 组件的属性
353
626
  cloneComProps(Select, CWSelect);
354
627
  export default CWSelect;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@chaoswise/ui",
3
3
  "packageName": "chaoswiseUI",
4
- "version": "3.1.7",
4
+ "version": "3.1.8",
5
5
  "description": "公共UI库",
6
6
  "keywords": [
7
7
  "ui",