@oceanbase/design 1.0.0-alpha.15 → 1.0.0-alpha.17
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/dist/design.min.js +1 -1
- package/es/alert/index.d.ts +1 -1
- package/es/alert/index.js +4 -2
- package/es/filter/components/CountNumber.js +2 -1
- package/es/filter/components/FilterButton.d.ts +2 -0
- package/es/filter/components/FilterButton.js +6 -3
- package/es/filter/components/FilterCascader/components/CascaderOption/OptionCheckbox.d.ts +20 -0
- package/es/filter/components/FilterCascader/components/CascaderOption/OptionCheckbox.js +101 -0
- package/es/filter/components/FilterCascader/components/CascaderOption/OptionItem.d.ts +13 -0
- package/es/filter/components/FilterCascader/components/CascaderOption/OptionItem.js +44 -0
- package/es/filter/components/FilterCascader/components/FlatCascaderContent/index.d.ts +16 -0
- package/es/filter/components/FilterCascader/components/FlatCascaderContent/index.js +64 -0
- package/es/filter/components/FilterCascader/components/NormalCascaderContent.d.ts +24 -0
- package/es/filter/components/FilterCascader/components/NormalCascaderContent.js +284 -0
- package/es/filter/components/FilterCascader/constants.d.ts +7 -0
- package/es/filter/components/FilterCascader/constants.js +8 -0
- package/es/filter/components/FilterCascader/hooks/useCascaderCallbacks.d.ts +12 -0
- package/es/filter/components/FilterCascader/hooks/useCascaderCallbacks.js +101 -0
- package/es/filter/components/FilterCascader/hooks/useCascaderLabels.d.ts +9 -0
- package/es/filter/components/FilterCascader/hooks/useCascaderLabels.js +50 -0
- package/es/filter/components/FilterCascader/hooks/useNormalizedValue.d.ts +7 -0
- package/es/filter/components/FilterCascader/hooks/useNormalizedValue.js +53 -0
- package/es/filter/components/FilterCascader/index.d.ts +5 -0
- package/es/filter/components/FilterCascader/index.js +317 -0
- package/es/filter/components/FilterCascader/types.d.ts +56 -0
- package/es/filter/components/FilterCascader/types.js +1 -0
- package/es/filter/components/FilterCascader/utils/countUtils.d.ts +13 -0
- package/es/filter/components/FilterCascader/utils/countUtils.js +48 -0
- package/es/filter/components/FilterCascader/utils/pathUtils.d.ts +17 -0
- package/es/filter/components/FilterCascader/utils/pathUtils.js +91 -0
- package/es/filter/components/FilterCheckbox.js +29 -7
- package/es/filter/components/FilterWrap.js +30 -1
- package/es/filter/components/ResponsiveFilterGroup.js +252 -359
- package/es/filter/index.d.ts +1 -1
- package/es/filter/index.js +11 -7
- package/es/filter/style/index.js +9 -2
- package/es/filter/type.d.ts +5 -0
- package/es/table/index.d.ts +2 -2
- package/lib/alert/index.d.ts +1 -1
- package/lib/alert/index.js +3 -1
- package/lib/filter/components/CountNumber.js +2 -1
- package/lib/filter/components/FilterButton.d.ts +2 -0
- package/lib/filter/components/FilterButton.js +4 -2
- package/lib/filter/components/FilterCascader/components/CascaderOption/OptionCheckbox.d.ts +20 -0
- package/lib/filter/components/FilterCascader/components/CascaderOption/OptionCheckbox.js +91 -0
- package/lib/filter/components/FilterCascader/components/CascaderOption/OptionItem.d.ts +13 -0
- package/lib/filter/components/FilterCascader/components/CascaderOption/OptionItem.js +51 -0
- package/lib/filter/components/FilterCascader/components/FlatCascaderContent/index.d.ts +16 -0
- package/lib/filter/components/FilterCascader/components/FlatCascaderContent/index.js +70 -0
- package/lib/filter/components/FilterCascader/components/NormalCascaderContent.d.ts +24 -0
- package/lib/filter/components/FilterCascader/components/NormalCascaderContent.js +263 -0
- package/lib/filter/components/FilterCascader/constants.d.ts +7 -0
- package/lib/filter/components/FilterCascader/constants.js +14 -0
- package/lib/filter/components/FilterCascader/hooks/useCascaderCallbacks.d.ts +12 -0
- package/lib/filter/components/FilterCascader/hooks/useCascaderCallbacks.js +81 -0
- package/lib/filter/components/FilterCascader/hooks/useCascaderLabels.d.ts +9 -0
- package/lib/filter/components/FilterCascader/hooks/useCascaderLabels.js +56 -0
- package/lib/filter/components/FilterCascader/hooks/useNormalizedValue.d.ts +7 -0
- package/lib/filter/components/FilterCascader/hooks/useNormalizedValue.js +48 -0
- package/lib/filter/components/FilterCascader/index.d.ts +5 -0
- package/lib/filter/components/FilterCascader/index.js +298 -0
- package/lib/filter/components/FilterCascader/types.d.ts +56 -0
- package/lib/filter/components/FilterCascader/types.js +5 -0
- package/lib/filter/components/FilterCascader/utils/countUtils.d.ts +13 -0
- package/lib/filter/components/FilterCascader/utils/countUtils.js +49 -0
- package/lib/filter/components/FilterCascader/utils/pathUtils.d.ts +17 -0
- package/lib/filter/components/FilterCascader/utils/pathUtils.js +56 -0
- package/lib/filter/components/FilterCheckbox.js +29 -7
- package/lib/filter/components/FilterWrap.js +28 -1
- package/lib/filter/components/ResponsiveFilterGroup.js +214 -340
- package/lib/filter/index.d.ts +1 -1
- package/lib/filter/index.js +11 -7
- package/lib/filter/style/index.js +10 -1
- package/lib/filter/type.d.ts +5 -0
- package/package.json +3 -3
- package/es/filter/components/FilterCascader.d.ts +0 -31
- package/es/filter/components/FilterCascader.js +0 -529
- package/lib/filter/components/FilterCascader.d.ts +0 -31
- package/lib/filter/components/FilterCascader.js +0 -449
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _theme = _interopRequireDefault(require("../../../theme"));
|
|
9
|
+
var _FilterContext = require("../../FilterContext");
|
|
10
|
+
var _useFilterCollapsed = require("../../hooks/useFilterCollapsed");
|
|
11
|
+
var _useFilterTooltip = require("../../hooks/useFilterTooltip");
|
|
12
|
+
var _style = _interopRequireWildcard(require("../../style"));
|
|
13
|
+
var _utils = require("../../utils");
|
|
14
|
+
var _CountNumber = _interopRequireDefault(require("../CountNumber"));
|
|
15
|
+
var _FilterButton = _interopRequireDefault(require("../FilterButton"));
|
|
16
|
+
var _WrappedTagsDisplay = _interopRequireDefault(require("../WrappedTagsDisplay"));
|
|
17
|
+
var _useNormalizedValue = require("./hooks/useNormalizedValue");
|
|
18
|
+
var _useCascaderLabels = require("./hooks/useCascaderLabels");
|
|
19
|
+
var _useCascaderCallbacks = require("./hooks/useCascaderCallbacks");
|
|
20
|
+
var _countUtils = require("./utils/countUtils");
|
|
21
|
+
var _FlatCascaderContent = require("./components/FlatCascaderContent");
|
|
22
|
+
var _NormalCascaderContent = require("./components/NormalCascaderContent");
|
|
23
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
24
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
25
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
26
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
27
|
+
const FilterCascader = ({
|
|
28
|
+
options = [],
|
|
29
|
+
value,
|
|
30
|
+
onChange,
|
|
31
|
+
icon,
|
|
32
|
+
label,
|
|
33
|
+
bordered = true,
|
|
34
|
+
multiple = false,
|
|
35
|
+
count = false,
|
|
36
|
+
showSearch = false,
|
|
37
|
+
_isCollapsed = false,
|
|
38
|
+
flat = false,
|
|
39
|
+
...restProps
|
|
40
|
+
}) => {
|
|
41
|
+
const isCollapsed = (0, _useFilterCollapsed.useFilterCollapsed)(_isCollapsed);
|
|
42
|
+
const {
|
|
43
|
+
prefixCls
|
|
44
|
+
} = (0, _style.default)();
|
|
45
|
+
const {
|
|
46
|
+
token
|
|
47
|
+
} = _theme.default.useToken();
|
|
48
|
+
const filterButtonRef = (0, _react.useRef)(null);
|
|
49
|
+
const {
|
|
50
|
+
updateFilterValue
|
|
51
|
+
} = (0, _FilterContext.useFilterContext)();
|
|
52
|
+
const filterId = (0, _react.useMemo)(() => (0, _utils.generateFilterId)('cascader', label), [label]);
|
|
53
|
+
|
|
54
|
+
// 用于控制二级 Popover 的打开状态(仅在单选模式下使用)
|
|
55
|
+
const [openPopoverKey, setOpenPopoverKey] = (0, _react.useState)(null);
|
|
56
|
+
// 用于 flat 模式下的多列路径(支持多层级)
|
|
57
|
+
const [flatColumnsPath, setFlatColumnsPath] = (0, _react.useState)([]);
|
|
58
|
+
|
|
59
|
+
// 搜索关键词状态
|
|
60
|
+
const [searchKeyword, setSearchKeyword] = (0, _react.useState)('');
|
|
61
|
+
|
|
62
|
+
// 从 restProps 中排除 onOpenChange,避免类型冲突
|
|
63
|
+
const {
|
|
64
|
+
onOpenChange: externalOnOpenChange,
|
|
65
|
+
...filterButtonProps
|
|
66
|
+
} = restProps;
|
|
67
|
+
|
|
68
|
+
// 解析 count 配置
|
|
69
|
+
const showCount = !!count;
|
|
70
|
+
const showTotal = typeof count === 'object' ? count.showTotal ?? false : false;
|
|
71
|
+
|
|
72
|
+
// 值管理
|
|
73
|
+
const {
|
|
74
|
+
currentValue,
|
|
75
|
+
setValue
|
|
76
|
+
} = (0, _useNormalizedValue.useNormalizedValue)(value, onChange, multiple);
|
|
77
|
+
|
|
78
|
+
// 标签显示
|
|
79
|
+
const {
|
|
80
|
+
getSelectedLabel,
|
|
81
|
+
getSelectedTags
|
|
82
|
+
} = (0, _useCascaderLabels.useCascaderLabels)(currentValue, options, label, multiple, isCollapsed);
|
|
83
|
+
|
|
84
|
+
// 回调函数
|
|
85
|
+
const {
|
|
86
|
+
handleChange,
|
|
87
|
+
clearByParent,
|
|
88
|
+
handleClear,
|
|
89
|
+
selectAllChildren,
|
|
90
|
+
handleRemoveTag
|
|
91
|
+
} = (0, _useCascaderCallbacks.useCascaderCallbacks)(currentValue, setValue, options, multiple, filterButtonRef, setOpenPopoverKey);
|
|
92
|
+
|
|
93
|
+
// 使用 Tooltip hook
|
|
94
|
+
const {
|
|
95
|
+
onPopoverOpenChange,
|
|
96
|
+
wrapWithTooltip
|
|
97
|
+
} = (0, _useFilterTooltip.useFilterTooltip)({
|
|
98
|
+
hasValue: currentValue.length > 0,
|
|
99
|
+
label,
|
|
100
|
+
content: currentValue.length > 0 ? getSelectedTags().map(i => i.label).join(', ') : null,
|
|
101
|
+
disabled: isCollapsed
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// 处理主弹窗状态变化
|
|
105
|
+
const handleMainPopoverOpenChange = (0, _react.useCallback)(open => {
|
|
106
|
+
onPopoverOpenChange(open);
|
|
107
|
+
// 当主弹窗关闭时,同步关闭二级弹窗(单选模式)
|
|
108
|
+
if (!open && !multiple) {
|
|
109
|
+
setOpenPopoverKey(null);
|
|
110
|
+
}
|
|
111
|
+
// flat 模式下,关闭弹窗时重置列路径,下次打开时只显示最顶层
|
|
112
|
+
if (!open && flat) {
|
|
113
|
+
setFlatColumnsPath([]);
|
|
114
|
+
}
|
|
115
|
+
// 弹窗关闭时清空搜索关键词
|
|
116
|
+
if (!open) {
|
|
117
|
+
setSearchKeyword('');
|
|
118
|
+
}
|
|
119
|
+
externalOnOpenChange?.(open);
|
|
120
|
+
}, [onPopoverOpenChange, externalOnOpenChange, multiple, flat]);
|
|
121
|
+
|
|
122
|
+
// 根据搜索关键词过滤选项(同时搜索父级和子级)
|
|
123
|
+
const filteredOptions = (0, _react.useMemo)(() => {
|
|
124
|
+
if (!showSearch || !searchKeyword) return options;
|
|
125
|
+
const lowerKeyword = searchKeyword.toLowerCase().trim();
|
|
126
|
+
return options.map(option => {
|
|
127
|
+
const parentLabel = (0, _utils.normalizeLabel)(option.label).toLowerCase();
|
|
128
|
+
const parentMatches = parentLabel.includes(lowerKeyword);
|
|
129
|
+
|
|
130
|
+
// 递归过滤子级选项
|
|
131
|
+
const filterChildren = children => {
|
|
132
|
+
return children.map(child => {
|
|
133
|
+
const childLabel = (0, _utils.normalizeLabel)(child.label).toLowerCase();
|
|
134
|
+
const childMatches = childLabel.includes(lowerKeyword);
|
|
135
|
+
|
|
136
|
+
// 如果有子级,递归过滤
|
|
137
|
+
if (child.children && child.children.length > 0) {
|
|
138
|
+
const filteredChildren = filterChildren(child.children);
|
|
139
|
+
// 如果当前节点匹配或有匹配的子级,则保留
|
|
140
|
+
if (childMatches || filteredChildren.length > 0) {
|
|
141
|
+
return {
|
|
142
|
+
...child,
|
|
143
|
+
children: childMatches ? child.children : filteredChildren
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 叶子节点,如果匹配则保留
|
|
150
|
+
return childMatches ? child : null;
|
|
151
|
+
}).filter(child => child !== null);
|
|
152
|
+
};
|
|
153
|
+
const filteredChildren = option.children ? filterChildren(option.children) : [];
|
|
154
|
+
|
|
155
|
+
// 如果父级匹配或有匹配的子级,则保留该选项
|
|
156
|
+
if (parentMatches || filteredChildren.length > 0) {
|
|
157
|
+
return {
|
|
158
|
+
...option,
|
|
159
|
+
// 如果父级不匹配但子级匹配,只显示匹配的子级
|
|
160
|
+
children: parentMatches ? option.children : filteredChildren
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}).filter(option => option !== null);
|
|
165
|
+
}, [showSearch, searchKeyword, options]);
|
|
166
|
+
|
|
167
|
+
// 当值变化时,更新 context 中的值
|
|
168
|
+
(0, _react.useEffect)(() => {
|
|
169
|
+
if (isCollapsed && updateFilterValue) {
|
|
170
|
+
updateFilterValue(filterId, label, currentValue, options, 'cascader');
|
|
171
|
+
}
|
|
172
|
+
}, [isCollapsed, updateFilterValue, filterId, label, currentValue, options]);
|
|
173
|
+
|
|
174
|
+
// 渲染内容
|
|
175
|
+
const renderContent = flat ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_FlatCascaderContent.FlatCascaderContent, {
|
|
176
|
+
options: filteredOptions,
|
|
177
|
+
currentValue: currentValue,
|
|
178
|
+
multiple: multiple,
|
|
179
|
+
filterButtonRef: filterButtonRef,
|
|
180
|
+
onValueChange: setValue
|
|
181
|
+
}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_NormalCascaderContent.NormalCascaderContent, {
|
|
182
|
+
options: filteredOptions,
|
|
183
|
+
currentValue: currentValue,
|
|
184
|
+
multiple: multiple,
|
|
185
|
+
prefixCls: prefixCls,
|
|
186
|
+
openPopoverKey: openPopoverKey,
|
|
187
|
+
filterButtonRef: filterButtonRef,
|
|
188
|
+
onOpenPopoverKeyChange: setOpenPopoverKey,
|
|
189
|
+
onValueChange: setValue,
|
|
190
|
+
onHandleChange: handleChange,
|
|
191
|
+
onClearByParent: clearByParent,
|
|
192
|
+
onSelectAllChildren: selectAllChildren,
|
|
193
|
+
showSearch: showSearch,
|
|
194
|
+
searchKeyword: searchKeyword,
|
|
195
|
+
onSearchChange: setSearchKeyword
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// flat 模式下的 Popover 样式(动态宽度)
|
|
199
|
+
const flatPopoverStyles = flat ? {
|
|
200
|
+
body: {
|
|
201
|
+
padding: 0
|
|
202
|
+
}
|
|
203
|
+
} : undefined;
|
|
204
|
+
|
|
205
|
+
// 计算总数(用于显示计数)
|
|
206
|
+
const totalCount = showTotal ? options.reduce((acc, curr) => {
|
|
207
|
+
return acc + (curr.children ? (0, _countUtils.countLeafNodes)(curr.children) : 0);
|
|
208
|
+
}, 0) : 0;
|
|
209
|
+
|
|
210
|
+
// Wrapped 模式
|
|
211
|
+
if (isCollapsed) {
|
|
212
|
+
const hasValue = currentValue.length > 0;
|
|
213
|
+
if (multiple) {
|
|
214
|
+
const selectedTags = getSelectedTags();
|
|
215
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
216
|
+
style: {
|
|
217
|
+
paddingBlock: token.paddingXXS
|
|
218
|
+
},
|
|
219
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
220
|
+
style: {
|
|
221
|
+
marginBottom: 8
|
|
222
|
+
},
|
|
223
|
+
children: label
|
|
224
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_FilterButton.default, {
|
|
225
|
+
ref: filterButtonRef,
|
|
226
|
+
icon: icon,
|
|
227
|
+
label: label,
|
|
228
|
+
bordered: bordered,
|
|
229
|
+
onClear: handleClear,
|
|
230
|
+
content: renderContent,
|
|
231
|
+
selected: hasValue,
|
|
232
|
+
onOpenChange: handleMainPopoverOpenChange,
|
|
233
|
+
_isFlat: flat,
|
|
234
|
+
...(flat ? {
|
|
235
|
+
styles: flatPopoverStyles
|
|
236
|
+
} : {}),
|
|
237
|
+
...filterButtonProps,
|
|
238
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_WrappedTagsDisplay.default, {
|
|
239
|
+
tags: selectedTags,
|
|
240
|
+
onRemove: handleRemoveTag
|
|
241
|
+
})
|
|
242
|
+
})]
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
246
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
247
|
+
style: {
|
|
248
|
+
marginBottom: 8
|
|
249
|
+
},
|
|
250
|
+
children: label
|
|
251
|
+
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_FilterButton.default, {
|
|
252
|
+
ref: filterButtonRef,
|
|
253
|
+
icon: icon,
|
|
254
|
+
label: label,
|
|
255
|
+
bordered: bordered,
|
|
256
|
+
onClear: handleClear,
|
|
257
|
+
content: renderContent,
|
|
258
|
+
selected: hasValue,
|
|
259
|
+
onOpenChange: handleMainPopoverOpenChange,
|
|
260
|
+
...(flat ? {
|
|
261
|
+
styles: flatPopoverStyles
|
|
262
|
+
} : {}),
|
|
263
|
+
...filterButtonProps,
|
|
264
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
265
|
+
className: (0, _style.getFilterCls)(prefixCls, 'text-ellipsis'),
|
|
266
|
+
style: (0, _utils.getWrappedValueStyle)(hasValue),
|
|
267
|
+
children: hasValue ? getSelectedLabel() : (0, _utils.getPlaceholder)()
|
|
268
|
+
})
|
|
269
|
+
})]
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 正常模式
|
|
274
|
+
const filterButton = /*#__PURE__*/(0, _jsxRuntime.jsxs)(_FilterButton.default, {
|
|
275
|
+
ref: filterButtonRef,
|
|
276
|
+
icon: icon,
|
|
277
|
+
label: label,
|
|
278
|
+
bordered: bordered,
|
|
279
|
+
onClear: handleClear,
|
|
280
|
+
content: renderContent,
|
|
281
|
+
selected: !!currentValue.length,
|
|
282
|
+
onOpenChange: handleMainPopoverOpenChange,
|
|
283
|
+
_isFlat: flat,
|
|
284
|
+
...(flat ? {
|
|
285
|
+
styles: flatPopoverStyles
|
|
286
|
+
} : {}),
|
|
287
|
+
...filterButtonProps,
|
|
288
|
+
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
289
|
+
className: (0, _style.getFilterCls)(prefixCls, 'text-ellipsis'),
|
|
290
|
+
children: getSelectedLabel()
|
|
291
|
+
}), multiple && showCount && currentValue.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_CountNumber.default, {
|
|
292
|
+
count: currentValue.length,
|
|
293
|
+
total: totalCount
|
|
294
|
+
})]
|
|
295
|
+
});
|
|
296
|
+
return wrapWithTooltip(filterButton);
|
|
297
|
+
};
|
|
298
|
+
var _default = exports.default = FilterCascader;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { BaseFilterProps, InternalFilterProps } from '../../type';
|
|
3
|
+
export interface CascaderOption {
|
|
4
|
+
label?: ReactNode;
|
|
5
|
+
value: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
children?: CascaderOption[];
|
|
8
|
+
}
|
|
9
|
+
interface BaseFilterCascaderProps extends BaseFilterProps, InternalFilterProps {
|
|
10
|
+
/** 选项列表 */
|
|
11
|
+
options?: CascaderOption[];
|
|
12
|
+
/** 是否显示计数,可传入 { showTotal: true } 同时显示总数 */
|
|
13
|
+
count?: boolean | {
|
|
14
|
+
showTotal?: boolean;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
type FilterCascaderSingleBase = BaseFilterCascaderProps & {
|
|
18
|
+
/** 是否多选 */
|
|
19
|
+
multiple?: false;
|
|
20
|
+
/** 当前选中值,格式为 [parentValue, childValue] */
|
|
21
|
+
value?: string[];
|
|
22
|
+
/** 值变化回调 */
|
|
23
|
+
onChange?: (value: string[]) => void;
|
|
24
|
+
};
|
|
25
|
+
type FilterCascaderMultipleBase = BaseFilterCascaderProps & {
|
|
26
|
+
/** 是否多选 */
|
|
27
|
+
multiple?: true;
|
|
28
|
+
/** 当前选中值,格式为 [[parentValue, childValue], ...] */
|
|
29
|
+
value?: string[][];
|
|
30
|
+
/** 值变化回调 */
|
|
31
|
+
onChange?: (value: string[][]) => void;
|
|
32
|
+
};
|
|
33
|
+
type FilterCascaderWithSearch<T extends FilterCascaderSingleBase | FilterCascaderMultipleBase> = T & {
|
|
34
|
+
/** 是否显示搜索框,默认为 false */
|
|
35
|
+
showSearch: true;
|
|
36
|
+
flat?: never;
|
|
37
|
+
};
|
|
38
|
+
type FilterCascaderWithFlat<T extends FilterCascaderSingleBase | FilterCascaderMultipleBase> = T & {
|
|
39
|
+
/** 扁平化展示 */
|
|
40
|
+
flat: true;
|
|
41
|
+
showSearch?: never;
|
|
42
|
+
};
|
|
43
|
+
type FilterCascaderNormal<T extends FilterCascaderSingleBase | FilterCascaderMultipleBase> = T & {
|
|
44
|
+
flat?: false;
|
|
45
|
+
showSearch?: false;
|
|
46
|
+
};
|
|
47
|
+
export type FilterCascaderSingleProps = FilterCascaderWithSearch<FilterCascaderSingleBase> | FilterCascaderWithFlat<FilterCascaderSingleBase> | FilterCascaderNormal<FilterCascaderSingleBase>;
|
|
48
|
+
export type FilterCascaderMultipleProps = FilterCascaderWithSearch<FilterCascaderMultipleBase> | FilterCascaderWithFlat<FilterCascaderMultipleBase> | FilterCascaderNormal<FilterCascaderMultipleBase>;
|
|
49
|
+
export type FilterCascaderProps = FilterCascaderSingleProps | FilterCascaderMultipleProps;
|
|
50
|
+
export interface SelectedTag {
|
|
51
|
+
label: ReactNode;
|
|
52
|
+
value: string;
|
|
53
|
+
parentValue: string;
|
|
54
|
+
childValue: string;
|
|
55
|
+
}
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CascaderOption } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* 递归统计所有叶子节点数量(只统计没有children的节点)
|
|
4
|
+
*/
|
|
5
|
+
export declare const countLeafNodes: (options: CascaderOption[]) => number;
|
|
6
|
+
/**
|
|
7
|
+
* 收集所有叶子节点的完整路径
|
|
8
|
+
*/
|
|
9
|
+
export declare const collectLeafPaths: (options: CascaderOption[], basePath: string[]) => string[][];
|
|
10
|
+
/**
|
|
11
|
+
* 统计选中的子节点数量
|
|
12
|
+
*/
|
|
13
|
+
export declare const countSelectedChildren: (currentValue: string[][], currentPath: string[]) => number;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.countSelectedChildren = exports.countLeafNodes = exports.collectLeafPaths = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* 递归统计所有叶子节点数量(只统计没有children的节点)
|
|
9
|
+
*/
|
|
10
|
+
const countLeafNodes = options => {
|
|
11
|
+
let count = 0;
|
|
12
|
+
options.forEach(opt => {
|
|
13
|
+
if (opt.children && opt.children.length > 0) {
|
|
14
|
+
count += countLeafNodes(opt.children);
|
|
15
|
+
} else {
|
|
16
|
+
count += 1;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
return count;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 收集所有叶子节点的完整路径
|
|
24
|
+
*/
|
|
25
|
+
exports.countLeafNodes = countLeafNodes;
|
|
26
|
+
const collectLeafPaths = (options, basePath) => {
|
|
27
|
+
const paths = [];
|
|
28
|
+
options.forEach(opt => {
|
|
29
|
+
const optPath = [...basePath, opt.value];
|
|
30
|
+
if (opt.children && opt.children.length > 0) {
|
|
31
|
+
paths.push(...collectLeafPaths(opt.children, optPath));
|
|
32
|
+
} else {
|
|
33
|
+
paths.push(optPath);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return paths;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 统计选中的子节点数量
|
|
41
|
+
*/
|
|
42
|
+
exports.collectLeafPaths = collectLeafPaths;
|
|
43
|
+
const countSelectedChildren = (currentValue, currentPath) => {
|
|
44
|
+
return currentValue.filter(selectedPath => {
|
|
45
|
+
if (selectedPath.length <= currentPath.length) return false;
|
|
46
|
+
return currentPath.every((val, idx) => selectedPath[idx] === val);
|
|
47
|
+
}).length;
|
|
48
|
+
};
|
|
49
|
+
exports.countSelectedChildren = countSelectedChildren;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CascaderOption } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* 根据路径查找选项
|
|
4
|
+
*/
|
|
5
|
+
export declare const findOptionByPath: (options: CascaderOption[], path: string[]) => CascaderOption | null;
|
|
6
|
+
/**
|
|
7
|
+
* 根据路径获取当前层级的选项列表
|
|
8
|
+
*/
|
|
9
|
+
export declare const getCurrentLevelOptions: (options: CascaderOption[], path: string[]) => CascaderOption[];
|
|
10
|
+
/**
|
|
11
|
+
* 检查路径是否在选中值中
|
|
12
|
+
*/
|
|
13
|
+
export declare const hasSelectedInPath: (currentValue: string[][], currentPath: string[]) => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* 检查路径是否完全匹配
|
|
16
|
+
*/
|
|
17
|
+
export declare const isExactMatch: (currentValue: string[][], currentPath: string[]) => boolean;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isExactMatch = exports.hasSelectedInPath = exports.getCurrentLevelOptions = exports.findOptionByPath = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* 根据路径查找选项
|
|
9
|
+
*/
|
|
10
|
+
const findOptionByPath = (options, path) => {
|
|
11
|
+
if (!path || path.length === 0) return null;
|
|
12
|
+
let currentOptions = options;
|
|
13
|
+
let currentOption = null;
|
|
14
|
+
for (const pathValue of path) {
|
|
15
|
+
const found = currentOptions.find(opt => opt.value === pathValue);
|
|
16
|
+
if (!found) return null;
|
|
17
|
+
currentOption = found;
|
|
18
|
+
currentOptions = found.children || [];
|
|
19
|
+
}
|
|
20
|
+
return currentOption;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 根据路径获取当前层级的选项列表
|
|
25
|
+
*/
|
|
26
|
+
exports.findOptionByPath = findOptionByPath;
|
|
27
|
+
const getCurrentLevelOptions = (options, path) => {
|
|
28
|
+
if (path.length === 0) return options;
|
|
29
|
+
let currentOptions = options;
|
|
30
|
+
for (const pathValue of path) {
|
|
31
|
+
const found = currentOptions.find(opt => opt.value === pathValue);
|
|
32
|
+
if (!found || !found.children) return [];
|
|
33
|
+
currentOptions = found.children;
|
|
34
|
+
}
|
|
35
|
+
return currentOptions;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 检查路径是否在选中值中
|
|
40
|
+
*/
|
|
41
|
+
exports.getCurrentLevelOptions = getCurrentLevelOptions;
|
|
42
|
+
const hasSelectedInPath = (currentValue, currentPath) => {
|
|
43
|
+
return currentValue.some(selectedPath => {
|
|
44
|
+
if (selectedPath.length < currentPath.length) return false;
|
|
45
|
+
return currentPath.every((val, idx) => selectedPath[idx] === val);
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 检查路径是否完全匹配
|
|
51
|
+
*/
|
|
52
|
+
exports.hasSelectedInPath = hasSelectedInPath;
|
|
53
|
+
const isExactMatch = (currentValue, currentPath) => {
|
|
54
|
+
return currentValue.some(selectedPath => selectedPath.length === currentPath.length && selectedPath.every((val, idx) => val === currentPath[idx]));
|
|
55
|
+
};
|
|
56
|
+
exports.isExactMatch = isExactMatch;
|
|
@@ -143,8 +143,30 @@ const FilterCheckbox = ({
|
|
|
143
143
|
const iconWidth = 10;
|
|
144
144
|
const iconWidthUnselected = 8;
|
|
145
145
|
const overlapDistance = 6;
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
|
|
147
|
+
// 按颜色分组选项,并检查每种颜色是否有选中的选项
|
|
148
|
+
const colorGroups = [];
|
|
149
|
+
const colorMap = new Map();
|
|
150
|
+
options.forEach(option => {
|
|
151
|
+
if (option.color) {
|
|
152
|
+
const isSelected = selectedValues.includes(option.value);
|
|
153
|
+
const existing = colorMap.get(option.color);
|
|
154
|
+
if (existing) {
|
|
155
|
+
// 如果已有该颜色,更新选中状态(只要有一个选中即为选中)
|
|
156
|
+
if (isSelected) {
|
|
157
|
+
existing.hasSelected = true;
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
const group = {
|
|
161
|
+
color: option.color,
|
|
162
|
+
hasSelected: isSelected
|
|
163
|
+
};
|
|
164
|
+
colorMap.set(option.color, group);
|
|
165
|
+
colorGroups.push(group);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
const containerWidth = iconWidth + (colorGroups.length - 1) * overlapDistance + 1;
|
|
148
170
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
149
171
|
style: {
|
|
150
172
|
position: 'relative',
|
|
@@ -153,8 +175,8 @@ const FilterCheckbox = ({
|
|
|
153
175
|
display: 'flex',
|
|
154
176
|
alignItems: 'center'
|
|
155
177
|
},
|
|
156
|
-
children:
|
|
157
|
-
const isSelected =
|
|
178
|
+
children: colorGroups.map((group, index) => {
|
|
179
|
+
const isSelected = group.hasSelected;
|
|
158
180
|
const baseSize = isSelected ? iconWidth : iconWidthUnselected;
|
|
159
181
|
return isSelected ? /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
160
182
|
style: {
|
|
@@ -164,10 +186,10 @@ const FilterCheckbox = ({
|
|
|
164
186
|
height: baseSize,
|
|
165
187
|
borderRadius: '50%',
|
|
166
188
|
zIndex: index,
|
|
167
|
-
backgroundColor:
|
|
189
|
+
backgroundColor: group.color,
|
|
168
190
|
border: `1px solid ${token.white}`
|
|
169
191
|
}
|
|
170
|
-
},
|
|
192
|
+
}, group.color) : /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
171
193
|
style: {
|
|
172
194
|
position: 'absolute',
|
|
173
195
|
left: index * overlapDistance,
|
|
@@ -184,7 +206,7 @@ const FilterCheckbox = ({
|
|
|
184
206
|
border: `1px solid ${token.colorBorderSecondary}`
|
|
185
207
|
}
|
|
186
208
|
})
|
|
187
|
-
},
|
|
209
|
+
}, group.color);
|
|
188
210
|
})
|
|
189
211
|
});
|
|
190
212
|
}, [isStatusMode, options, selectedValues, token]);
|
|
@@ -56,12 +56,39 @@ const FilterWrap = ({
|
|
|
56
56
|
value: item.value
|
|
57
57
|
})));
|
|
58
58
|
}, [collapsed, contextValue.filterValues]);
|
|
59
|
-
const
|
|
59
|
+
const allFilterValues = (0, _react.useMemo)(() => {
|
|
60
60
|
if (!collapsed) return [];
|
|
61
61
|
return contextValue.filterValues || [];
|
|
62
62
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
63
63
|
}, [collapsed, stableFilterValuesKey]);
|
|
64
64
|
|
|
65
|
+
// 从 children 中递归提取 label 集合,用于过滤 filterValues 只保留当前折叠项的值
|
|
66
|
+
// children 结构可能是 Fragment → Form.Item → Filter.Select,需要递归查找
|
|
67
|
+
const childLabelSet = (0, _react.useMemo)(() => {
|
|
68
|
+
const labels = new Set();
|
|
69
|
+
const extractLabel = node => {
|
|
70
|
+
if (! /*#__PURE__*/(0, _react.isValidElement)(node)) return;
|
|
71
|
+
const props = node.props;
|
|
72
|
+
if (props.label !== undefined) {
|
|
73
|
+
labels.add(props.label);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (props.children) {
|
|
77
|
+
if ( /*#__PURE__*/(0, _react.isValidElement)(props.children)) {
|
|
78
|
+
extractLabel(props.children);
|
|
79
|
+
} else if (Array.isArray(props.children)) {
|
|
80
|
+
props.children.forEach(extractLabel);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
_react.Children.forEach(children, extractLabel);
|
|
85
|
+
return labels;
|
|
86
|
+
}, [children]);
|
|
87
|
+
const filterValues = (0, _react.useMemo)(() => {
|
|
88
|
+
if (childLabelSet.size === 0) return allFilterValues;
|
|
89
|
+
return allFilterValues.filter(item => childLabelSet.has(item.label));
|
|
90
|
+
}, [allFilterValues, childLabelSet]);
|
|
91
|
+
|
|
65
92
|
// 格式化值用于 Tooltip 显示
|
|
66
93
|
const formatValueForTooltip = (0, _react.useCallback)((value, options, componentName) => {
|
|
67
94
|
if (!value) return '';
|