@rc-component/tree-select 1.0.0

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 (137) hide show
  1. package/LICENSE.md +22 -0
  2. package/README.md +140 -0
  3. package/assets/icons.png +0 -0
  4. package/assets/index.less +2 -0
  5. package/assets/loading.gif +0 -0
  6. package/assets/minus.gif +0 -0
  7. package/assets/select.less +3 -0
  8. package/assets/tree.less +3 -0
  9. package/dist/223.90c7e648.async.js +3 -0
  10. package/dist/338.a2a48f3b.async.js +15 -0
  11. package/dist/338.e8c51481.chunk.css +5 -0
  12. package/dist/404.html +20 -0
  13. package/dist/439.67bede3f.async.js +137 -0
  14. package/dist/929.df8dd03f.async.js +32 -0
  15. package/dist/demo/basic/index.html +20 -0
  16. package/dist/demo/big-data/index.html +20 -0
  17. package/dist/demo/controlled/index.html +20 -0
  18. package/dist/demo/custom-icons/index.html +20 -0
  19. package/dist/demo/debug/index.html +20 -0
  20. package/dist/demo/disable/index.html +20 -0
  21. package/dist/demo/dynamic/index.html +20 -0
  22. package/dist/demo/field-names/index.html +20 -0
  23. package/dist/demo/filter/index.html +20 -0
  24. package/dist/demo/form/index.html +20 -0
  25. package/dist/demo/mutiple-with-max-count/index.html +20 -0
  26. package/dist/demo/tree-node-label-prop/index.html +20 -0
  27. package/dist/demo/width/index.html +20 -0
  28. package/dist/demos.f221b577.async.js +1 -0
  29. package/dist/docs__demo__basic.md.bcb0edb7.async.js +1 -0
  30. package/dist/docs__demo__big-data.md.8783cd6b.async.js +1 -0
  31. package/dist/docs__demo__controlled.md.e65ef7d7.async.js +1 -0
  32. package/dist/docs__demo__custom-icons.md.d53bf2f9.async.js +1 -0
  33. package/dist/docs__demo__debug.md.099ad226.async.js +1 -0
  34. package/dist/docs__demo__disable.md.6ba57652.async.js +1 -0
  35. package/dist/docs__demo__dynamic.md.f7559890.async.js +1 -0
  36. package/dist/docs__demo__fieldNames.md.16131d9f.async.js +1 -0
  37. package/dist/docs__demo__filter.md.02527db4.async.js +1 -0
  38. package/dist/docs__demo__form.md.6b77dffe.async.js +1 -0
  39. package/dist/docs__demo__mutiple-with-maxCount.md.7268d4ad.async.js +1 -0
  40. package/dist/docs__demo__treeNodeLabelProp.md.24709916.async.js +1 -0
  41. package/dist/docs__demo__width.md.24bfecd6.async.js +1 -0
  42. package/dist/docs__index.md.a6e45331.async.js +1 -0
  43. package/dist/dumi__tmp-production__dumi__theme__ContextWrapper.f56a0670.async.js +1 -0
  44. package/dist/index.html +20 -0
  45. package/dist/meta__docs.255fc42b.chunk.css +1 -0
  46. package/dist/meta__docs.dbf04b66.async.js +3359 -0
  47. package/dist/nm__dumi__dist__client__pages__404.8b85f2d9.chunk.css +1 -0
  48. package/dist/nm__dumi__dist__client__pages__404.ca2add38.async.js +1 -0
  49. package/dist/nm__dumi__dist__client__pages__Demo__index.29e9ef8f.async.js +1 -0
  50. package/dist/nm__dumi__dist__client__pages__Demo__index.578aa5c0.chunk.css +1 -0
  51. package/dist/nm__dumi__theme-default__layouts__DocLayout__index.8ed833d7.async.js +1 -0
  52. package/dist/preload_helper.d41c4da0.js +1 -0
  53. package/dist/umi.4f47b921.js +124 -0
  54. package/dist/umi.8faca2de.css +1 -0
  55. package/dist/~demos/:id/index.html +20 -0
  56. package/dist/~demos/docs-demo-basic-demo-basic/index.html +20 -0
  57. package/dist/~demos/docs-demo-big-data-demo-big-data/index.html +20 -0
  58. package/dist/~demos/docs-demo-controlled-demo-controlled/index.html +20 -0
  59. package/dist/~demos/docs-demo-custom-icons-demo-custom-icons/index.html +20 -0
  60. package/dist/~demos/docs-demo-debug-demo-debug/index.html +20 -0
  61. package/dist/~demos/docs-demo-disable-demo-disable/index.html +20 -0
  62. package/dist/~demos/docs-demo-dynamic-demo-dynamic/index.html +20 -0
  63. package/dist/~demos/docs-demo-field-names-demo-fieldnames/index.html +20 -0
  64. package/dist/~demos/docs-demo-filter-demo-filter/index.html +20 -0
  65. package/dist/~demos/docs-demo-form-demo-form/index.html +20 -0
  66. package/dist/~demos/docs-demo-mutiple-with-max-count-demo-mutiple-with-maxcount/index.html +20 -0
  67. package/dist/~demos/docs-demo-tree-node-label-prop-demo-treenodelabelprop/index.html +20 -0
  68. package/dist/~demos/docs-demo-width-demo-width/index.html +20 -0
  69. package/es/LegacyContext.d.ts +24 -0
  70. package/es/LegacyContext.js +3 -0
  71. package/es/OptionList.d.ts +8 -0
  72. package/es/OptionList.js +351 -0
  73. package/es/TreeNode.d.ts +9 -0
  74. package/es/TreeNode.js +5 -0
  75. package/es/TreeSelect.d.ts +65 -0
  76. package/es/TreeSelect.js +507 -0
  77. package/es/TreeSelectContext.d.ts +25 -0
  78. package/es/TreeSelectContext.js +3 -0
  79. package/es/hooks/useCache.d.ts +7 -0
  80. package/es/hooks/useCache.js +32 -0
  81. package/es/hooks/useCheckedKeys.d.ts +5 -0
  82. package/es/hooks/useCheckedKeys.js +21 -0
  83. package/es/hooks/useDataEntities.d.ts +7 -0
  84. package/es/hooks/useDataEntities.js +26 -0
  85. package/es/hooks/useFilterTreeData.d.ts +8 -0
  86. package/es/hooks/useFilterTreeData.js +33 -0
  87. package/es/hooks/useRefFunc.d.ts +5 -0
  88. package/es/hooks/useRefFunc.js +14 -0
  89. package/es/hooks/useTreeData.d.ts +7 -0
  90. package/es/hooks/useTreeData.js +52 -0
  91. package/es/index.d.ts +7 -0
  92. package/es/index.js +5 -0
  93. package/es/interface.d.ts +57 -0
  94. package/es/interface.js +1 -0
  95. package/es/utils/legacyUtil.d.ts +5 -0
  96. package/es/utils/legacyUtil.js +120 -0
  97. package/es/utils/strategyUtil.d.ts +7 -0
  98. package/es/utils/strategyUtil.js +25 -0
  99. package/es/utils/valueUtil.d.ts +11 -0
  100. package/es/utils/valueUtil.js +30 -0
  101. package/es/utils/warningPropsUtil.d.ts +5 -0
  102. package/es/utils/warningPropsUtil.js +30 -0
  103. package/lib/LegacyContext.d.ts +24 -0
  104. package/lib/LegacyContext.js +11 -0
  105. package/lib/OptionList.d.ts +8 -0
  106. package/lib/OptionList.js +359 -0
  107. package/lib/TreeNode.d.ts +9 -0
  108. package/lib/TreeNode.js +11 -0
  109. package/lib/TreeSelect.d.ts +65 -0
  110. package/lib/TreeSelect.js +516 -0
  111. package/lib/TreeSelectContext.d.ts +25 -0
  112. package/lib/TreeSelectContext.js +11 -0
  113. package/lib/hooks/useCache.d.ts +7 -0
  114. package/lib/hooks/useCache.js +41 -0
  115. package/lib/hooks/useCheckedKeys.d.ts +5 -0
  116. package/lib/hooks/useCheckedKeys.js +29 -0
  117. package/lib/hooks/useDataEntities.d.ts +7 -0
  118. package/lib/hooks/useDataEntities.js +36 -0
  119. package/lib/hooks/useFilterTreeData.d.ts +8 -0
  120. package/lib/hooks/useFilterTreeData.js +41 -0
  121. package/lib/hooks/useRefFunc.d.ts +5 -0
  122. package/lib/hooks/useRefFunc.js +21 -0
  123. package/lib/hooks/useTreeData.d.ts +7 -0
  124. package/lib/hooks/useTreeData.js +60 -0
  125. package/lib/index.d.ts +7 -0
  126. package/lib/index.js +35 -0
  127. package/lib/interface.d.ts +57 -0
  128. package/lib/interface.js +5 -0
  129. package/lib/utils/legacyUtil.d.ts +5 -0
  130. package/lib/utils/legacyUtil.js +131 -0
  131. package/lib/utils/strategyUtil.d.ts +7 -0
  132. package/lib/utils/strategyUtil.js +32 -0
  133. package/lib/utils/valueUtil.d.ts +11 -0
  134. package/lib/utils/valueUtil.js +41 -0
  135. package/lib/utils/warningPropsUtil.d.ts +5 -0
  136. package/lib/utils/warningPropsUtil.js +37 -0
  137. package/package.json +89 -0
@@ -0,0 +1,507 @@
1
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
+ import { BaseSelect } from '@rc-component/select';
3
+ import useId from "@rc-component/select/es/hooks/useId";
4
+ import { conductCheck } from "rc-tree/es/utils/conductUtil";
5
+ import useMergedState from "@rc-component/util/es/hooks/useMergedState";
6
+ import * as React from 'react';
7
+ import useCache from "./hooks/useCache";
8
+ import useCheckedKeys from "./hooks/useCheckedKeys";
9
+ import useDataEntities from "./hooks/useDataEntities";
10
+ import useFilterTreeData from "./hooks/useFilterTreeData";
11
+ import useRefFunc from "./hooks/useRefFunc";
12
+ import useTreeData from "./hooks/useTreeData";
13
+ import LegacyContext from "./LegacyContext";
14
+ import OptionList from "./OptionList";
15
+ import TreeNode from "./TreeNode";
16
+ import TreeSelectContext from "./TreeSelectContext";
17
+ import { fillAdditionalInfo, fillLegacyProps } from "./utils/legacyUtil";
18
+ import { formatStrategyValues, SHOW_ALL, SHOW_CHILD, SHOW_PARENT } from "./utils/strategyUtil";
19
+ import { fillFieldNames, isNil, toArray } from "./utils/valueUtil";
20
+ import warningProps from "./utils/warningPropsUtil";
21
+ function isRawValue(value) {
22
+ return !value || typeof value !== 'object';
23
+ }
24
+ const TreeSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
25
+ const {
26
+ id,
27
+ prefixCls = 'rc-tree-select',
28
+ // Value
29
+ value,
30
+ defaultValue,
31
+ onChange,
32
+ onSelect,
33
+ onDeselect,
34
+ // Search
35
+ searchValue,
36
+ inputValue,
37
+ onSearch,
38
+ autoClearSearchValue = true,
39
+ filterTreeNode,
40
+ treeNodeFilterProp = 'value',
41
+ // Selector
42
+ showCheckedStrategy,
43
+ treeNodeLabelProp,
44
+ // Mode
45
+ multiple,
46
+ treeCheckable,
47
+ treeCheckStrictly,
48
+ labelInValue,
49
+ maxCount,
50
+ // FieldNames
51
+ fieldNames,
52
+ // Data
53
+ treeDataSimpleMode,
54
+ treeData,
55
+ children,
56
+ loadData,
57
+ treeLoadedKeys,
58
+ onTreeLoad,
59
+ // Expanded
60
+ treeDefaultExpandAll,
61
+ treeExpandedKeys,
62
+ treeDefaultExpandedKeys,
63
+ onTreeExpand,
64
+ treeExpandAction,
65
+ // Options
66
+ virtual,
67
+ listHeight = 200,
68
+ listItemHeight = 20,
69
+ listItemScrollOffset = 0,
70
+ onPopupVisibleChange,
71
+ popupMatchSelectWidth = true,
72
+ // Tree
73
+ treeLine,
74
+ treeIcon,
75
+ showTreeIcon,
76
+ switcherIcon,
77
+ treeMotion,
78
+ treeTitleRender,
79
+ onPopupScroll,
80
+ ...restProps
81
+ } = props;
82
+ const mergedId = useId(id);
83
+ const treeConduction = treeCheckable && !treeCheckStrictly;
84
+ const mergedCheckable = treeCheckable || treeCheckStrictly;
85
+ const mergedLabelInValue = treeCheckStrictly || labelInValue;
86
+ const mergedMultiple = mergedCheckable || multiple;
87
+ const [internalValue, setInternalValue] = useMergedState(defaultValue, {
88
+ value
89
+ });
90
+
91
+ // `multiple` && `!treeCheckable` should be show all
92
+ const mergedShowCheckedStrategy = React.useMemo(() => {
93
+ if (!treeCheckable) {
94
+ return SHOW_ALL;
95
+ }
96
+ return showCheckedStrategy || SHOW_CHILD;
97
+ }, [showCheckedStrategy, treeCheckable]);
98
+
99
+ // ========================== Warning ===========================
100
+ if (process.env.NODE_ENV !== 'production') {
101
+ warningProps(props);
102
+ }
103
+
104
+ // ========================= FieldNames =========================
105
+ const mergedFieldNames = React.useMemo(() => fillFieldNames(fieldNames), /* eslint-disable react-hooks/exhaustive-deps */
106
+ [JSON.stringify(fieldNames)]
107
+ /* eslint-enable react-hooks/exhaustive-deps */);
108
+
109
+ // =========================== Search ===========================
110
+ const [mergedSearchValue, setSearchValue] = useMergedState('', {
111
+ value: searchValue !== undefined ? searchValue : inputValue,
112
+ postState: search => search || ''
113
+ });
114
+ const onInternalSearch = searchText => {
115
+ setSearchValue(searchText);
116
+ onSearch?.(searchText);
117
+ };
118
+
119
+ // ============================ Data ============================
120
+ // `useTreeData` only do convert of `children` or `simpleMode`.
121
+ // Else will return origin `treeData` for perf consideration.
122
+ // Do not do anything to loop the data.
123
+ const mergedTreeData = useTreeData(treeData, children, treeDataSimpleMode);
124
+ const {
125
+ keyEntities,
126
+ valueEntities
127
+ } = useDataEntities(mergedTreeData, mergedFieldNames);
128
+
129
+ /** Get `missingRawValues` which not exist in the tree yet */
130
+ const splitRawValues = React.useCallback(newRawValues => {
131
+ const missingRawValues = [];
132
+ const existRawValues = [];
133
+
134
+ // Keep missing value in the cache
135
+ newRawValues.forEach(val => {
136
+ if (valueEntities.has(val)) {
137
+ existRawValues.push(val);
138
+ } else {
139
+ missingRawValues.push(val);
140
+ }
141
+ });
142
+ return {
143
+ missingRawValues,
144
+ existRawValues
145
+ };
146
+ }, [valueEntities]);
147
+
148
+ // Filtered Tree
149
+ const filteredTreeData = useFilterTreeData(mergedTreeData, mergedSearchValue, {
150
+ fieldNames: mergedFieldNames,
151
+ treeNodeFilterProp,
152
+ filterTreeNode
153
+ });
154
+
155
+ // =========================== Label ============================
156
+ const getLabel = React.useCallback(item => {
157
+ if (item) {
158
+ if (treeNodeLabelProp) {
159
+ return item[treeNodeLabelProp];
160
+ }
161
+
162
+ // Loop from fieldNames
163
+ const {
164
+ _title: titleList
165
+ } = mergedFieldNames;
166
+ for (let i = 0; i < titleList.length; i += 1) {
167
+ const title = item[titleList[i]];
168
+ if (title !== undefined) {
169
+ return title;
170
+ }
171
+ }
172
+ }
173
+ }, [mergedFieldNames, treeNodeLabelProp]);
174
+
175
+ // ========================= Wrap Value =========================
176
+ const toLabeledValues = React.useCallback(draftValues => {
177
+ const values = toArray(draftValues);
178
+ return values.map(val => {
179
+ if (isRawValue(val)) {
180
+ return {
181
+ value: val
182
+ };
183
+ }
184
+ return val;
185
+ });
186
+ }, []);
187
+ const convert2LabelValues = React.useCallback(draftValues => {
188
+ const values = toLabeledValues(draftValues);
189
+ return values.map(item => {
190
+ let {
191
+ label: rawLabel
192
+ } = item;
193
+ const {
194
+ value: rawValue,
195
+ halfChecked: rawHalfChecked
196
+ } = item;
197
+ let rawDisabled;
198
+ const entity = valueEntities.get(rawValue);
199
+
200
+ // Fill missing label & status
201
+ if (entity) {
202
+ rawLabel = treeTitleRender ? treeTitleRender(entity.node) : rawLabel ?? getLabel(entity.node);
203
+ rawDisabled = entity.node.disabled;
204
+ } else if (rawLabel === undefined) {
205
+ // We try to find in current `labelInValue` value
206
+ const labelInValueItem = toLabeledValues(internalValue).find(labeledItem => labeledItem.value === rawValue);
207
+ rawLabel = labelInValueItem.label;
208
+ }
209
+ return {
210
+ label: rawLabel,
211
+ value: rawValue,
212
+ halfChecked: rawHalfChecked,
213
+ disabled: rawDisabled
214
+ };
215
+ });
216
+ }, [valueEntities, getLabel, toLabeledValues, internalValue]);
217
+
218
+ // =========================== Values ===========================
219
+ const rawMixedLabeledValues = React.useMemo(() => toLabeledValues(internalValue === null ? [] : internalValue), [toLabeledValues, internalValue]);
220
+
221
+ // Split value into full check and half check
222
+ const [rawLabeledValues, rawHalfLabeledValues] = React.useMemo(() => {
223
+ const fullCheckValues = [];
224
+ const halfCheckValues = [];
225
+ rawMixedLabeledValues.forEach(item => {
226
+ if (item.halfChecked) {
227
+ halfCheckValues.push(item);
228
+ } else {
229
+ fullCheckValues.push(item);
230
+ }
231
+ });
232
+ return [fullCheckValues, halfCheckValues];
233
+ }, [rawMixedLabeledValues]);
234
+
235
+ // const [mergedValues] = useCache(rawLabeledValues);
236
+ const rawValues = React.useMemo(() => rawLabeledValues.map(item => item.value), [rawLabeledValues]);
237
+
238
+ // Convert value to key. Will fill missed keys for conduct check.
239
+ const [rawCheckedValues, rawHalfCheckedValues] = useCheckedKeys(rawLabeledValues, rawHalfLabeledValues, treeConduction, keyEntities);
240
+
241
+ // Convert rawCheckedKeys to check strategy related values
242
+ const displayValues = React.useMemo(() => {
243
+ // Collect keys which need to show
244
+ const displayKeys = formatStrategyValues(rawCheckedValues, mergedShowCheckedStrategy, keyEntities, mergedFieldNames);
245
+
246
+ // Convert to value and filled with label
247
+ const values = displayKeys.map(key => keyEntities[key]?.node?.[mergedFieldNames.value] ?? key);
248
+
249
+ // Back fill with origin label
250
+ const labeledValues = values.map(val => {
251
+ const targetItem = rawLabeledValues.find(item => item.value === val);
252
+ const label = labelInValue ? targetItem?.label : treeTitleRender?.(targetItem);
253
+ return {
254
+ value: val,
255
+ label
256
+ };
257
+ });
258
+ const rawDisplayValues = convert2LabelValues(labeledValues);
259
+ const firstVal = rawDisplayValues[0];
260
+ if (!mergedMultiple && firstVal && isNil(firstVal.value) && isNil(firstVal.label)) {
261
+ return [];
262
+ }
263
+ return rawDisplayValues.map(item => ({
264
+ ...item,
265
+ label: item.label ?? item.value
266
+ }));
267
+ // eslint-disable-next-line react-hooks/exhaustive-deps
268
+ }, [mergedFieldNames, mergedMultiple, rawCheckedValues, rawLabeledValues, convert2LabelValues, mergedShowCheckedStrategy, keyEntities]);
269
+ const [cachedDisplayValues] = useCache(displayValues);
270
+
271
+ // ========================== MaxCount ==========================
272
+ const mergedMaxCount = React.useMemo(() => {
273
+ if (mergedMultiple && (mergedShowCheckedStrategy === 'SHOW_CHILD' || treeCheckStrictly || !treeCheckable)) {
274
+ return maxCount;
275
+ }
276
+ return null;
277
+ }, [maxCount, mergedMultiple, treeCheckStrictly, mergedShowCheckedStrategy, treeCheckable]);
278
+
279
+ // =========================== Change ===========================
280
+ const triggerChange = useRefFunc((newRawValues, extra, source) => {
281
+ const formattedKeyList = formatStrategyValues(newRawValues, mergedShowCheckedStrategy, keyEntities, mergedFieldNames);
282
+
283
+ // Not allow pass with `maxCount`
284
+ if (mergedMaxCount && formattedKeyList.length > mergedMaxCount) {
285
+ return;
286
+ }
287
+ const labeledValues = convert2LabelValues(newRawValues);
288
+ setInternalValue(labeledValues);
289
+
290
+ // Clean up if needed
291
+ if (autoClearSearchValue) {
292
+ setSearchValue('');
293
+ }
294
+
295
+ // Generate rest parameters is costly, so only do it when necessary
296
+ if (onChange) {
297
+ let eventValues = newRawValues;
298
+ if (treeConduction) {
299
+ eventValues = formattedKeyList.map(key => {
300
+ const entity = valueEntities.get(key);
301
+ return entity ? entity.node[mergedFieldNames.value] : key;
302
+ });
303
+ }
304
+ const {
305
+ triggerValue,
306
+ selected
307
+ } = extra || {
308
+ triggerValue: undefined,
309
+ selected: undefined
310
+ };
311
+ let returnRawValues = eventValues;
312
+
313
+ // We need fill half check back
314
+ if (treeCheckStrictly) {
315
+ const halfValues = rawHalfLabeledValues.filter(item => !eventValues.includes(item.value));
316
+ returnRawValues = [...returnRawValues, ...halfValues];
317
+ }
318
+ const returnLabeledValues = convert2LabelValues(returnRawValues);
319
+ const additionalInfo = {
320
+ // [Legacy] Always return as array contains label & value
321
+ preValue: rawLabeledValues,
322
+ triggerValue
323
+ };
324
+
325
+ // [Legacy] Fill legacy data if user query.
326
+ // This is expansive that we only fill when user query
327
+ // https://github.com/react-component/tree-select/blob/fe33eb7c27830c9ac70cd1fdb1ebbe7bc679c16a/src/Select.jsx
328
+ let showPosition = true;
329
+ if (treeCheckStrictly || source === 'selection' && !selected) {
330
+ showPosition = false;
331
+ }
332
+ fillAdditionalInfo(additionalInfo, triggerValue, newRawValues, mergedTreeData, showPosition, mergedFieldNames);
333
+ if (mergedCheckable) {
334
+ additionalInfo.checked = selected;
335
+ } else {
336
+ additionalInfo.selected = selected;
337
+ }
338
+ const returnValues = mergedLabelInValue ? returnLabeledValues : returnLabeledValues.map(item => item.value);
339
+ onChange(mergedMultiple ? returnValues : returnValues[0], mergedLabelInValue ? null : returnLabeledValues.map(item => item.label), additionalInfo);
340
+ }
341
+ });
342
+
343
+ // ========================== Options ===========================
344
+ /** Trigger by option list */
345
+ const onOptionSelect = React.useCallback((selectedKey, {
346
+ selected,
347
+ source
348
+ }) => {
349
+ const entity = keyEntities[selectedKey];
350
+ const node = entity?.node;
351
+ const selectedValue = node?.[mergedFieldNames.value] ?? selectedKey;
352
+
353
+ // Never be falsy but keep it safe
354
+ if (!mergedMultiple) {
355
+ // Single mode always set value
356
+ triggerChange([selectedValue], {
357
+ selected: true,
358
+ triggerValue: selectedValue
359
+ }, 'option');
360
+ } else {
361
+ let newRawValues = selected ? [...rawValues, selectedValue] : rawCheckedValues.filter(v => v !== selectedValue);
362
+
363
+ // Add keys if tree conduction
364
+ if (treeConduction) {
365
+ // Should keep missing values
366
+ const {
367
+ missingRawValues,
368
+ existRawValues
369
+ } = splitRawValues(newRawValues);
370
+ const keyList = existRawValues.map(val => valueEntities.get(val).key);
371
+
372
+ // Conduction by selected or not
373
+ let checkedKeys;
374
+ if (selected) {
375
+ ({
376
+ checkedKeys
377
+ } = conductCheck(keyList, true, keyEntities));
378
+ } else {
379
+ ({
380
+ checkedKeys
381
+ } = conductCheck(keyList, {
382
+ checked: false,
383
+ halfCheckedKeys: rawHalfCheckedValues
384
+ }, keyEntities));
385
+ }
386
+
387
+ // Fill back of keys
388
+ newRawValues = [...missingRawValues, ...checkedKeys.map(key => keyEntities[key].node[mergedFieldNames.value])];
389
+ }
390
+ triggerChange(newRawValues, {
391
+ selected,
392
+ triggerValue: selectedValue
393
+ }, source || 'option');
394
+ }
395
+
396
+ // Trigger select event
397
+ if (selected || !mergedMultiple) {
398
+ onSelect?.(selectedValue, fillLegacyProps(node));
399
+ } else {
400
+ onDeselect?.(selectedValue, fillLegacyProps(node));
401
+ }
402
+ }, [splitRawValues, valueEntities, keyEntities, mergedFieldNames, mergedMultiple, rawValues, triggerChange, treeConduction, onSelect, onDeselect, rawCheckedValues, rawHalfCheckedValues, maxCount]);
403
+
404
+ // ========================== Dropdown ==========================
405
+ const onInternalPopupVisibleChange = React.useCallback(open => {
406
+ if (onPopupVisibleChange) {
407
+ onPopupVisibleChange(open);
408
+ }
409
+ }, [onPopupVisibleChange]);
410
+
411
+ // ====================== Display Change ========================
412
+ const onDisplayValuesChange = useRefFunc((newValues, info) => {
413
+ const newRawValues = newValues.map(item => item.value);
414
+ if (info.type === 'clear') {
415
+ triggerChange(newRawValues, {}, 'selection');
416
+ return;
417
+ }
418
+
419
+ // TreeSelect only have multiple mode which means display change only has remove
420
+ if (info.values.length) {
421
+ onOptionSelect(info.values[0].value, {
422
+ selected: false,
423
+ source: 'selection'
424
+ });
425
+ }
426
+ });
427
+
428
+ // ========================== Context ===========================
429
+ const treeSelectContext = React.useMemo(() => {
430
+ return {
431
+ virtual,
432
+ popupMatchSelectWidth,
433
+ listHeight,
434
+ listItemHeight,
435
+ listItemScrollOffset,
436
+ treeData: filteredTreeData,
437
+ fieldNames: mergedFieldNames,
438
+ onSelect: onOptionSelect,
439
+ treeExpandAction,
440
+ treeTitleRender,
441
+ onPopupScroll,
442
+ leftMaxCount: maxCount === undefined ? null : maxCount - cachedDisplayValues.length,
443
+ leafCountOnly: mergedShowCheckedStrategy === 'SHOW_CHILD' && !treeCheckStrictly && !!treeCheckable,
444
+ valueEntities
445
+ };
446
+ }, [virtual, popupMatchSelectWidth, listHeight, listItemHeight, listItemScrollOffset, filteredTreeData, mergedFieldNames, onOptionSelect, treeExpandAction, treeTitleRender, onPopupScroll, maxCount, cachedDisplayValues.length, mergedShowCheckedStrategy, treeCheckStrictly, treeCheckable, valueEntities]);
447
+
448
+ // ======================= Legacy Context =======================
449
+ const legacyContext = React.useMemo(() => ({
450
+ checkable: mergedCheckable,
451
+ loadData,
452
+ treeLoadedKeys,
453
+ onTreeLoad,
454
+ checkedKeys: rawCheckedValues,
455
+ halfCheckedKeys: rawHalfCheckedValues,
456
+ treeDefaultExpandAll,
457
+ treeExpandedKeys,
458
+ treeDefaultExpandedKeys,
459
+ onTreeExpand,
460
+ treeIcon,
461
+ treeMotion,
462
+ showTreeIcon,
463
+ switcherIcon,
464
+ treeLine,
465
+ treeNodeFilterProp,
466
+ keyEntities
467
+ }), [mergedCheckable, loadData, treeLoadedKeys, onTreeLoad, rawCheckedValues, rawHalfCheckedValues, treeDefaultExpandAll, treeExpandedKeys, treeDefaultExpandedKeys, onTreeExpand, treeIcon, treeMotion, showTreeIcon, switcherIcon, treeLine, treeNodeFilterProp, keyEntities]);
468
+
469
+ // =========================== Render ===========================
470
+ return /*#__PURE__*/React.createElement(TreeSelectContext.Provider, {
471
+ value: treeSelectContext
472
+ }, /*#__PURE__*/React.createElement(LegacyContext.Provider, {
473
+ value: legacyContext
474
+ }, /*#__PURE__*/React.createElement(BaseSelect, _extends({
475
+ ref: ref
476
+ }, restProps, {
477
+ // >>> MISC
478
+ id: mergedId,
479
+ prefixCls: prefixCls,
480
+ mode: mergedMultiple ? 'multiple' : undefined
481
+ // >>> Display Value
482
+ ,
483
+ displayValues: cachedDisplayValues,
484
+ onDisplayValuesChange: onDisplayValuesChange
485
+ // >>> Search
486
+ ,
487
+ searchValue: mergedSearchValue,
488
+ onSearch: onInternalSearch
489
+ // >>> Options
490
+ ,
491
+ OptionList: OptionList,
492
+ emptyOptions: !mergedTreeData.length,
493
+ onPopupVisibleChange: onInternalPopupVisibleChange,
494
+ popupMatchSelectWidth: popupMatchSelectWidth
495
+ }))));
496
+ });
497
+
498
+ // Assign name for Debug
499
+ if (process.env.NODE_ENV !== 'production') {
500
+ TreeSelect.displayName = 'TreeSelect';
501
+ }
502
+ const GenericTreeSelect = TreeSelect;
503
+ GenericTreeSelect.TreeNode = TreeNode;
504
+ GenericTreeSelect.SHOW_ALL = SHOW_ALL;
505
+ GenericTreeSelect.SHOW_PARENT = SHOW_PARENT;
506
+ GenericTreeSelect.SHOW_CHILD = SHOW_CHILD;
507
+ export default GenericTreeSelect;
@@ -0,0 +1,25 @@
1
+ import * as React from 'react';
2
+ import type { ExpandAction } from 'rc-tree/lib/Tree';
3
+ import type { DataNode, FieldNames, Key } from './interface';
4
+ import type useDataEntities from './hooks/useDataEntities';
5
+ export interface TreeSelectContextProps {
6
+ virtual?: boolean;
7
+ popupMatchSelectWidth?: boolean | number;
8
+ listHeight: number;
9
+ listItemHeight: number;
10
+ listItemScrollOffset?: number;
11
+ treeData: DataNode[];
12
+ fieldNames: FieldNames;
13
+ onSelect: (value: Key, info: {
14
+ selected: boolean;
15
+ }) => void;
16
+ treeExpandAction?: ExpandAction;
17
+ treeTitleRender?: (node: any) => React.ReactNode;
18
+ onPopupScroll?: React.UIEventHandler<HTMLDivElement>;
19
+ leftMaxCount: number | null;
20
+ /** When `true`, only take leaf node as count, or take all as count with `maxCount` limitation */
21
+ leafCountOnly: boolean;
22
+ valueEntities: ReturnType<typeof useDataEntities>['valueEntities'];
23
+ }
24
+ declare const TreeSelectContext: React.Context<TreeSelectContextProps>;
25
+ export default TreeSelectContext;
@@ -0,0 +1,3 @@
1
+ import * as React from 'react';
2
+ const TreeSelectContext = /*#__PURE__*/React.createContext(null);
3
+ export default TreeSelectContext;
@@ -0,0 +1,7 @@
1
+ import type { LabeledValueType } from '../interface';
2
+ /**
3
+ * This function will try to call requestIdleCallback if available to save performance.
4
+ * No need `getLabel` here since already fetch on `rawLabeledValue`.
5
+ */
6
+ declare const _default: (values: LabeledValueType[]) => [LabeledValueType[]];
7
+ export default _default;
@@ -0,0 +1,32 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * This function will try to call requestIdleCallback if available to save performance.
4
+ * No need `getLabel` here since already fetch on `rawLabeledValue`.
5
+ */
6
+ export default (values => {
7
+ const cacheRef = React.useRef({
8
+ valueLabels: new Map()
9
+ });
10
+ return React.useMemo(() => {
11
+ const {
12
+ valueLabels
13
+ } = cacheRef.current;
14
+ const valueLabelsCache = new Map();
15
+ const filledValues = values.map(item => {
16
+ const {
17
+ value,
18
+ label
19
+ } = item;
20
+ const mergedLabel = label ?? valueLabels.get(value);
21
+
22
+ // Save in cache
23
+ valueLabelsCache.set(value, mergedLabel);
24
+ return {
25
+ ...item,
26
+ label: mergedLabel
27
+ };
28
+ });
29
+ cacheRef.current.valueLabels = valueLabelsCache;
30
+ return [filledValues];
31
+ }, [values]);
32
+ });
@@ -0,0 +1,5 @@
1
+ import * as React from 'react';
2
+ import type { DataEntity } from 'rc-tree/lib/interface';
3
+ import type { LabeledValueType, SafeKey } from '../interface';
4
+ declare const useCheckedKeys: (rawLabeledValues: LabeledValueType[], rawHalfCheckedValues: LabeledValueType[], treeConduction: boolean, keyEntities: Record<SafeKey, DataEntity>) => React.Key[][];
5
+ export default useCheckedKeys;
@@ -0,0 +1,21 @@
1
+ import * as React from 'react';
2
+ import { conductCheck } from "rc-tree/es/utils/conductUtil";
3
+ const useCheckedKeys = (rawLabeledValues, rawHalfCheckedValues, treeConduction, keyEntities) => {
4
+ return React.useMemo(() => {
5
+ const extractValues = values => values.map(({
6
+ value
7
+ }) => value);
8
+ const checkedKeys = extractValues(rawLabeledValues);
9
+ const halfCheckedKeys = extractValues(rawHalfCheckedValues);
10
+ const missingValues = checkedKeys.filter(key => !keyEntities[key]);
11
+ let finalCheckedKeys = checkedKeys;
12
+ let finalHalfCheckedKeys = halfCheckedKeys;
13
+ if (treeConduction) {
14
+ const conductResult = conductCheck(checkedKeys, true, keyEntities);
15
+ finalCheckedKeys = conductResult.checkedKeys;
16
+ finalHalfCheckedKeys = conductResult.halfCheckedKeys;
17
+ }
18
+ return [Array.from(new Set([...missingValues, ...finalCheckedKeys])), finalHalfCheckedKeys];
19
+ }, [rawLabeledValues, rawHalfCheckedValues, treeConduction, keyEntities]);
20
+ };
21
+ export default useCheckedKeys;
@@ -0,0 +1,7 @@
1
+ import type { DataEntity } from 'rc-tree/lib/interface';
2
+ import type { SafeKey, FieldNames } from '../interface';
3
+ declare const _default: (treeData: any, fieldNames: FieldNames) => {
4
+ valueEntities: Map<SafeKey, DataEntity>;
5
+ keyEntities: Record<string, DataEntity>;
6
+ };
7
+ export default _default;
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+ import { convertDataToEntities } from "rc-tree/es/utils/treeUtil";
3
+ import warning from "@rc-component/util/es/warning";
4
+ import { isNil } from "../utils/valueUtil";
5
+ export default ((treeData, fieldNames) => React.useMemo(() => {
6
+ const collection = convertDataToEntities(treeData, {
7
+ fieldNames,
8
+ initWrapper: wrapper => ({
9
+ ...wrapper,
10
+ valueEntities: new Map()
11
+ }),
12
+ processEntity: (entity, wrapper) => {
13
+ const val = entity.node[fieldNames.value];
14
+
15
+ // Check if exist same value
16
+ if (process.env.NODE_ENV !== 'production') {
17
+ const key = entity.node.key;
18
+ warning(!isNil(val), 'TreeNode `value` is invalidate: undefined');
19
+ warning(!wrapper.valueEntities.has(val), `Same \`value\` exist in the tree: ${val}`);
20
+ warning(!key || String(key) === String(val), `\`key\` or \`value\` with TreeNode must be the same or you can remove one of them. key: ${key}, value: ${val}.`);
21
+ }
22
+ wrapper.valueEntities.set(val, entity);
23
+ }
24
+ });
25
+ return collection;
26
+ }, [treeData, fieldNames]));
@@ -0,0 +1,8 @@
1
+ import type { TreeSelectProps } from '../TreeSelect';
2
+ import type { DataNode, FieldNames } from '../interface';
3
+ declare const useFilterTreeData: (treeData: DataNode[], searchValue: string, options: {
4
+ fieldNames: FieldNames;
5
+ treeNodeFilterProp: string;
6
+ filterTreeNode: TreeSelectProps['filterTreeNode'];
7
+ }) => DataNode[];
8
+ export default useFilterTreeData;
@@ -0,0 +1,33 @@
1
+ import * as React from 'react';
2
+ import { fillLegacyProps } from "../utils/legacyUtil";
3
+ const useFilterTreeData = (treeData, searchValue, options) => {
4
+ const {
5
+ fieldNames,
6
+ treeNodeFilterProp,
7
+ filterTreeNode
8
+ } = options;
9
+ const {
10
+ children: fieldChildren
11
+ } = fieldNames;
12
+ return React.useMemo(() => {
13
+ if (!searchValue || filterTreeNode === false) {
14
+ return treeData;
15
+ }
16
+ const filterOptionFunc = typeof filterTreeNode === 'function' ? filterTreeNode : (_, dataNode) => String(dataNode[treeNodeFilterProp]).toUpperCase().includes(searchValue.toUpperCase());
17
+ const filterTreeNodes = (nodes, keepAll = false) => nodes.reduce((filtered, node) => {
18
+ const children = node[fieldChildren];
19
+ const isMatch = keepAll || filterOptionFunc(searchValue, fillLegacyProps(node));
20
+ const filteredChildren = filterTreeNodes(children || [], isMatch);
21
+ if (isMatch || filteredChildren.length) {
22
+ filtered.push({
23
+ ...node,
24
+ isLeaf: undefined,
25
+ [fieldChildren]: filteredChildren
26
+ });
27
+ }
28
+ return filtered;
29
+ }, []);
30
+ return filterTreeNodes(treeData);
31
+ }, [treeData, searchValue, fieldChildren, treeNodeFilterProp, filterTreeNode]);
32
+ };
33
+ export default useFilterTreeData;