@rc-component/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 +9 -0
  2. package/README.md +191 -0
  3. package/assets/index.css +306 -0
  4. package/assets/index.less +397 -0
  5. package/es/BaseSelect/Polite.d.ts +7 -0
  6. package/es/BaseSelect/Polite.js +26 -0
  7. package/es/BaseSelect/index.d.ts +118 -0
  8. package/es/BaseSelect/index.js +569 -0
  9. package/es/OptGroup.d.ts +12 -0
  10. package/es/OptGroup.js +6 -0
  11. package/es/Option.d.ts +14 -0
  12. package/es/Option.js +6 -0
  13. package/es/OptionList.d.ts +10 -0
  14. package/es/OptionList.js +379 -0
  15. package/es/Select.d.ts +114 -0
  16. package/es/Select.js +480 -0
  17. package/es/SelectContext.d.ts +23 -0
  18. package/es/SelectContext.js +6 -0
  19. package/es/SelectTrigger.d.ts +30 -0
  20. package/es/SelectTrigger.js +138 -0
  21. package/es/Selector/Input.d.ts +27 -0
  22. package/es/Selector/Input.js +114 -0
  23. package/es/Selector/MultipleSelector.d.ts +16 -0
  24. package/es/Selector/MultipleSelector.js +185 -0
  25. package/es/Selector/SingleSelector.d.ts +8 -0
  26. package/es/Selector/SingleSelector.js +104 -0
  27. package/es/Selector/index.d.ts +85 -0
  28. package/es/Selector/index.js +184 -0
  29. package/es/TransBtn.d.ts +12 -0
  30. package/es/TransBtn.js +30 -0
  31. package/es/hooks/useAllowClear.d.ts +8 -0
  32. package/es/hooks/useAllowClear.js +26 -0
  33. package/es/hooks/useBaseProps.d.ts +13 -0
  34. package/es/hooks/useBaseProps.js +10 -0
  35. package/es/hooks/useCache.d.ts +7 -0
  36. package/es/hooks/useCache.js +40 -0
  37. package/es/hooks/useDelayReset.d.ts +5 -0
  38. package/es/hooks/useDelayReset.js +24 -0
  39. package/es/hooks/useFilterOptions.d.ts +3 -0
  40. package/es/hooks/useFilterOptions.js +57 -0
  41. package/es/hooks/useId.d.ts +5 -0
  42. package/es/hooks/useId.js +29 -0
  43. package/es/hooks/useLayoutEffect.d.ts +5 -0
  44. package/es/hooks/useLayoutEffect.js +17 -0
  45. package/es/hooks/useLock.d.ts +7 -0
  46. package/es/hooks/useLock.js +27 -0
  47. package/es/hooks/useOptions.d.ts +12 -0
  48. package/es/hooks/useOptions.js +45 -0
  49. package/es/hooks/useRefFunc.d.ts +5 -0
  50. package/es/hooks/useRefFunc.js +14 -0
  51. package/es/hooks/useSelectTriggerControl.d.ts +1 -0
  52. package/es/hooks/useSelectTriggerControl.js +27 -0
  53. package/es/index.d.ts +10 -0
  54. package/es/index.js +7 -0
  55. package/es/interface.d.ts +23 -0
  56. package/es/interface.js +1 -0
  57. package/es/utils/__mocks__/platformUtil.d.ts +1 -0
  58. package/es/utils/__mocks__/platformUtil.js +3 -0
  59. package/es/utils/commonUtil.d.ts +9 -0
  60. package/es/utils/commonUtil.js +32 -0
  61. package/es/utils/keyUtil.d.ts +2 -0
  62. package/es/utils/keyUtil.js +16 -0
  63. package/es/utils/legacyUtil.d.ts +3 -0
  64. package/es/utils/legacyUtil.js +44 -0
  65. package/es/utils/platformUtil.d.ts +1 -0
  66. package/es/utils/platformUtil.js +4 -0
  67. package/es/utils/valueUtil.d.ts +24 -0
  68. package/es/utils/valueUtil.js +128 -0
  69. package/es/utils/warningPropsUtil.d.ts +4 -0
  70. package/es/utils/warningPropsUtil.js +119 -0
  71. package/lib/BaseSelect/Polite.d.ts +7 -0
  72. package/lib/BaseSelect/Polite.js +34 -0
  73. package/lib/BaseSelect/index.d.ts +118 -0
  74. package/lib/BaseSelect/index.js +579 -0
  75. package/lib/OptGroup.d.ts +12 -0
  76. package/lib/OptGroup.js +12 -0
  77. package/lib/Option.d.ts +14 -0
  78. package/lib/Option.js +12 -0
  79. package/lib/OptionList.d.ts +10 -0
  80. package/lib/OptionList.js +387 -0
  81. package/lib/Select.d.ts +114 -0
  82. package/lib/Select.js +487 -0
  83. package/lib/SelectContext.d.ts +23 -0
  84. package/lib/SelectContext.js +13 -0
  85. package/lib/SelectTrigger.d.ts +30 -0
  86. package/lib/SelectTrigger.js +147 -0
  87. package/lib/Selector/Input.d.ts +27 -0
  88. package/lib/Selector/Input.js +123 -0
  89. package/lib/Selector/MultipleSelector.d.ts +16 -0
  90. package/lib/Selector/MultipleSelector.js +194 -0
  91. package/lib/Selector/SingleSelector.d.ts +8 -0
  92. package/lib/Selector/SingleSelector.js +113 -0
  93. package/lib/Selector/index.d.ts +85 -0
  94. package/lib/Selector/index.js +191 -0
  95. package/lib/TransBtn.d.ts +12 -0
  96. package/lib/TransBtn.js +39 -0
  97. package/lib/hooks/useAllowClear.d.ts +8 -0
  98. package/lib/hooks/useAllowClear.js +34 -0
  99. package/lib/hooks/useBaseProps.d.ts +13 -0
  100. package/lib/hooks/useBaseProps.js +19 -0
  101. package/lib/hooks/useCache.d.ts +7 -0
  102. package/lib/hooks/useCache.js +49 -0
  103. package/lib/hooks/useDelayReset.d.ts +5 -0
  104. package/lib/hooks/useDelayReset.js +31 -0
  105. package/lib/hooks/useFilterOptions.d.ts +3 -0
  106. package/lib/hooks/useFilterOptions.js +66 -0
  107. package/lib/hooks/useId.d.ts +5 -0
  108. package/lib/hooks/useId.js +40 -0
  109. package/lib/hooks/useLayoutEffect.d.ts +5 -0
  110. package/lib/hooks/useLayoutEffect.js +25 -0
  111. package/lib/hooks/useLock.d.ts +7 -0
  112. package/lib/hooks/useLock.js +34 -0
  113. package/lib/hooks/useOptions.d.ts +12 -0
  114. package/lib/hooks/useOptions.js +52 -0
  115. package/lib/hooks/useRefFunc.d.ts +5 -0
  116. package/lib/hooks/useRefFunc.js +21 -0
  117. package/lib/hooks/useSelectTriggerControl.d.ts +1 -0
  118. package/lib/hooks/useSelectTriggerControl.js +35 -0
  119. package/lib/index.d.ts +10 -0
  120. package/lib/index.js +37 -0
  121. package/lib/interface.d.ts +23 -0
  122. package/lib/interface.js +5 -0
  123. package/lib/utils/__mocks__/platformUtil.d.ts +1 -0
  124. package/lib/utils/__mocks__/platformUtil.js +9 -0
  125. package/lib/utils/commonUtil.d.ts +9 -0
  126. package/lib/utils/commonUtil.js +42 -0
  127. package/lib/utils/keyUtil.d.ts +2 -0
  128. package/lib/utils/keyUtil.js +22 -0
  129. package/lib/utils/legacyUtil.d.ts +3 -0
  130. package/lib/utils/legacyUtil.js +53 -0
  131. package/lib/utils/platformUtil.d.ts +1 -0
  132. package/lib/utils/platformUtil.js +10 -0
  133. package/lib/utils/valueUtil.d.ts +24 -0
  134. package/lib/utils/valueUtil.js +140 -0
  135. package/lib/utils/warningPropsUtil.d.ts +4 -0
  136. package/lib/utils/warningPropsUtil.js +129 -0
  137. package/package.json +86 -0
package/es/Select.js ADDED
@@ -0,0 +1,480 @@
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
+ /**
3
+ * To match accessibility requirement, we always provide an input in the component.
4
+ * Other element will not set `tabIndex` to avoid `onBlur` sequence problem.
5
+ * For focused select, we set `aria-live="polite"` to update the accessibility content.
6
+ *
7
+ * ref:
8
+ * - keyboard: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/listbox_role#Keyboard_interactions
9
+ *
10
+ * New api:
11
+ * - listHeight
12
+ * - listItemHeight
13
+ * - component
14
+ *
15
+ * Remove deprecated api:
16
+ * - multiple
17
+ * - tags
18
+ * - combobox
19
+ * - firstActiveValue
20
+ * - dropdownMenuStyle
21
+ * - openClassName (Not list in api)
22
+ *
23
+ * Update:
24
+ * - `backfill` only support `combobox` mode
25
+ * - `combobox` mode not support `labelInValue` since it's meaningless
26
+ * - `getInputElement` only support `combobox` mode
27
+ * - `onChange` return OptionData instead of ReactNode
28
+ * - `filterOption` `onChange` `onSelect` accept OptionData instead of ReactNode
29
+ * - `combobox` mode trigger `onChange` will get `undefined` if no `value` match in Option
30
+ * - `combobox` mode not support `optionLabelProp`
31
+ */
32
+
33
+ import useMergedState from "@rc-component/util/es/hooks/useMergedState";
34
+ import warning from "@rc-component/util/es/warning";
35
+ import * as React from 'react';
36
+ import BaseSelect, { isMultiple } from "./BaseSelect";
37
+ import OptGroup from "./OptGroup";
38
+ import Option from "./Option";
39
+ import OptionList from "./OptionList";
40
+ import SelectContext from "./SelectContext";
41
+ import useCache from "./hooks/useCache";
42
+ import useFilterOptions from "./hooks/useFilterOptions";
43
+ import useId from "./hooks/useId";
44
+ import useOptions from "./hooks/useOptions";
45
+ import useRefFunc from "./hooks/useRefFunc";
46
+ import { hasValue, isComboNoValue, toArray } from "./utils/commonUtil";
47
+ import { fillFieldNames, flattenOptions, injectPropsWithOption } from "./utils/valueUtil";
48
+ import warningProps, { warningNullOptions } from "./utils/warningPropsUtil";
49
+ const OMIT_DOM_PROPS = ['inputValue'];
50
+ function isRawValue(value) {
51
+ return !value || typeof value !== 'object';
52
+ }
53
+ const Select = /*#__PURE__*/React.forwardRef((props, ref) => {
54
+ const {
55
+ id,
56
+ mode,
57
+ prefixCls = 'rc-select',
58
+ backfill,
59
+ fieldNames,
60
+ // Search
61
+ searchValue,
62
+ onSearch,
63
+ autoClearSearchValue = true,
64
+ // Select
65
+ onSelect,
66
+ onDeselect,
67
+ popupMatchSelectWidth = true,
68
+ // Options
69
+ filterOption,
70
+ filterSort,
71
+ optionFilterProp,
72
+ optionLabelProp,
73
+ options,
74
+ optionRender,
75
+ children,
76
+ defaultActiveFirstOption,
77
+ menuItemSelectedIcon,
78
+ virtual,
79
+ direction,
80
+ listHeight = 200,
81
+ listItemHeight = 20,
82
+ labelRender,
83
+ // Value
84
+ value,
85
+ defaultValue,
86
+ labelInValue,
87
+ onChange,
88
+ maxCount,
89
+ ...restProps
90
+ } = props;
91
+ const mergedId = useId(id);
92
+ const multiple = isMultiple(mode);
93
+ const childrenAsData = !!(!options && children);
94
+ const mergedFilterOption = React.useMemo(() => {
95
+ if (filterOption === undefined && mode === 'combobox') {
96
+ return false;
97
+ }
98
+ return filterOption;
99
+ }, [filterOption, mode]);
100
+
101
+ // ========================= FieldNames =========================
102
+ const mergedFieldNames = React.useMemo(() => fillFieldNames(fieldNames, childrenAsData), /* eslint-disable react-hooks/exhaustive-deps */
103
+ [
104
+ // We stringify fieldNames to avoid unnecessary re-renders.
105
+ JSON.stringify(fieldNames), childrenAsData]
106
+ /* eslint-enable react-hooks/exhaustive-deps */);
107
+
108
+ // =========================== Search ===========================
109
+ const [mergedSearchValue, setSearchValue] = useMergedState('', {
110
+ value: searchValue,
111
+ postState: search => search || ''
112
+ });
113
+
114
+ // =========================== Option ===========================
115
+ const parsedOptions = useOptions(options, children, mergedFieldNames, optionFilterProp, optionLabelProp);
116
+ const {
117
+ valueOptions,
118
+ labelOptions,
119
+ options: mergedOptions
120
+ } = parsedOptions;
121
+
122
+ // ========================= Wrap Value =========================
123
+ const convert2LabelValues = React.useCallback(draftValues => {
124
+ // Convert to array
125
+ const valueList = toArray(draftValues);
126
+
127
+ // Convert to labelInValue type
128
+ return valueList.map(val => {
129
+ let rawValue;
130
+ let rawLabel;
131
+ let rawDisabled;
132
+ let rawTitle;
133
+
134
+ // Fill label & value
135
+ if (isRawValue(val)) {
136
+ rawValue = val;
137
+ } else {
138
+ rawLabel = val.label;
139
+ rawValue = val.value;
140
+ }
141
+ const option = valueOptions.get(rawValue);
142
+ if (option) {
143
+ // Fill missing props
144
+ if (rawLabel === undefined) rawLabel = option?.[optionLabelProp || mergedFieldNames.label];
145
+ rawDisabled = option?.disabled;
146
+ rawTitle = option?.title;
147
+
148
+ // Warning if label not same as provided
149
+ if (process.env.NODE_ENV !== 'production' && !optionLabelProp) {
150
+ const optionLabel = option?.[mergedFieldNames.label];
151
+ if (optionLabel !== undefined && ! /*#__PURE__*/React.isValidElement(optionLabel) && ! /*#__PURE__*/React.isValidElement(rawLabel) && optionLabel !== rawLabel) {
152
+ warning(false, '`label` of `value` is not same as `label` in Select options.');
153
+ }
154
+ }
155
+ }
156
+ return {
157
+ label: rawLabel,
158
+ value: rawValue,
159
+ key: rawValue,
160
+ disabled: rawDisabled,
161
+ title: rawTitle
162
+ };
163
+ });
164
+ }, [mergedFieldNames, optionLabelProp, valueOptions]);
165
+
166
+ // =========================== Values ===========================
167
+ const [internalValue, setInternalValue] = useMergedState(defaultValue, {
168
+ value
169
+ });
170
+
171
+ // Merged value with LabelValueType
172
+ const rawLabeledValues = React.useMemo(() => {
173
+ const newInternalValue = multiple && internalValue === null ? [] : internalValue;
174
+ const values = convert2LabelValues(newInternalValue);
175
+
176
+ // combobox no need save value when it's no value (exclude value equal 0)
177
+ if (mode === 'combobox' && isComboNoValue(values[0]?.value)) {
178
+ return [];
179
+ }
180
+ return values;
181
+ }, [internalValue, convert2LabelValues, mode, multiple]);
182
+
183
+ // Fill label with cache to avoid option remove
184
+ const [mergedValues, getMixedOption] = useCache(rawLabeledValues, valueOptions);
185
+ const displayValues = React.useMemo(() => {
186
+ // `null` need show as placeholder instead
187
+ // https://github.com/ant-design/ant-design/issues/25057
188
+ if (!mode && mergedValues.length === 1) {
189
+ const firstValue = mergedValues[0];
190
+ if (firstValue.value === null && (firstValue.label === null || firstValue.label === undefined)) {
191
+ return [];
192
+ }
193
+ }
194
+ return mergedValues.map(item => ({
195
+ ...item,
196
+ label: (typeof labelRender === 'function' ? labelRender(item) : item.label) ?? item.value
197
+ }));
198
+ }, [mode, mergedValues, labelRender]);
199
+
200
+ /** Convert `displayValues` to raw value type set */
201
+ const rawValues = React.useMemo(() => new Set(mergedValues.map(val => val.value)), [mergedValues]);
202
+ React.useEffect(() => {
203
+ if (mode === 'combobox') {
204
+ const strValue = mergedValues[0]?.value;
205
+ setSearchValue(hasValue(strValue) ? String(strValue) : '');
206
+ }
207
+ }, [mergedValues]);
208
+
209
+ // ======================= Display Option =======================
210
+ // Create a placeholder item if not exist in `options`
211
+ const createTagOption = useRefFunc((val, label) => {
212
+ const mergedLabel = label ?? val;
213
+ return {
214
+ [mergedFieldNames.value]: val,
215
+ [mergedFieldNames.label]: mergedLabel
216
+ };
217
+ });
218
+
219
+ // Fill tag as option if mode is `tags`
220
+ const filledTagOptions = React.useMemo(() => {
221
+ if (mode !== 'tags') {
222
+ return mergedOptions;
223
+ }
224
+
225
+ // >>> Tag mode
226
+ const cloneOptions = [...mergedOptions];
227
+
228
+ // Check if value exist in options (include new patch item)
229
+ const existOptions = val => valueOptions.has(val);
230
+
231
+ // Fill current value as option
232
+ [...mergedValues].sort((a, b) => a.value < b.value ? -1 : 1).forEach(item => {
233
+ const val = item.value;
234
+ if (!existOptions(val)) {
235
+ cloneOptions.push(createTagOption(val, item.label));
236
+ }
237
+ });
238
+ return cloneOptions;
239
+ }, [createTagOption, mergedOptions, valueOptions, mergedValues, mode]);
240
+ const filteredOptions = useFilterOptions(filledTagOptions, mergedFieldNames, mergedSearchValue, mergedFilterOption, optionFilterProp);
241
+
242
+ // Fill options with search value if needed
243
+ const filledSearchOptions = React.useMemo(() => {
244
+ if (mode !== 'tags' || !mergedSearchValue || filteredOptions.some(item => item[optionFilterProp || 'value'] === mergedSearchValue)) {
245
+ return filteredOptions;
246
+ }
247
+ // ignore when search value equal select input value
248
+ if (filteredOptions.some(item => item[mergedFieldNames.value] === mergedSearchValue)) {
249
+ return filteredOptions;
250
+ }
251
+ // Fill search value as option
252
+ return [createTagOption(mergedSearchValue), ...filteredOptions];
253
+ }, [createTagOption, optionFilterProp, mode, filteredOptions, mergedSearchValue, mergedFieldNames]);
254
+ const sorter = inputOptions => {
255
+ const sortedOptions = [...inputOptions].sort((a, b) => filterSort(a, b, {
256
+ searchValue: mergedSearchValue
257
+ }));
258
+ return sortedOptions.map(item => {
259
+ if (Array.isArray(item.options)) {
260
+ return {
261
+ ...item,
262
+ options: item.options.length > 0 ? sorter(item.options) : item.options
263
+ };
264
+ }
265
+ return item;
266
+ });
267
+ };
268
+ const orderedFilteredOptions = React.useMemo(() => {
269
+ if (!filterSort) {
270
+ return filledSearchOptions;
271
+ }
272
+ return sorter(filledSearchOptions);
273
+ }, [filledSearchOptions, filterSort, mergedSearchValue]);
274
+ const displayOptions = React.useMemo(() => flattenOptions(orderedFilteredOptions, {
275
+ fieldNames: mergedFieldNames,
276
+ childrenAsData
277
+ }), [orderedFilteredOptions, mergedFieldNames, childrenAsData]);
278
+
279
+ // =========================== Change ===========================
280
+ const triggerChange = values => {
281
+ const labeledValues = convert2LabelValues(values);
282
+ setInternalValue(labeledValues);
283
+ if (onChange && (
284
+ // Trigger event only when value changed
285
+ labeledValues.length !== mergedValues.length || labeledValues.some((newVal, index) => mergedValues[index]?.value !== newVal?.value))) {
286
+ const returnValues = labelInValue ? labeledValues.map(({
287
+ label: l,
288
+ value: v
289
+ }) => ({
290
+ label: l,
291
+ value: v
292
+ })) : labeledValues.map(v => v.value);
293
+ const returnOptions = labeledValues.map(v => injectPropsWithOption(getMixedOption(v.value)));
294
+ onChange(
295
+ // Value
296
+ multiple ? returnValues : returnValues[0],
297
+ // Option
298
+ multiple ? returnOptions : returnOptions[0]);
299
+ }
300
+ };
301
+
302
+ // ======================= Accessibility ========================
303
+ const [activeValue, setActiveValue] = React.useState(null);
304
+ const [accessibilityIndex, setAccessibilityIndex] = React.useState(0);
305
+ const mergedDefaultActiveFirstOption = defaultActiveFirstOption !== undefined ? defaultActiveFirstOption : mode !== 'combobox';
306
+ const onActiveValue = React.useCallback((active, index, {
307
+ source = 'keyboard'
308
+ } = {}) => {
309
+ setAccessibilityIndex(index);
310
+ if (backfill && mode === 'combobox' && active !== null && source === 'keyboard') {
311
+ setActiveValue(String(active));
312
+ }
313
+ }, [backfill, mode]);
314
+
315
+ // ========================= OptionList =========================
316
+ const triggerSelect = (val, selected, type) => {
317
+ const getSelectEnt = () => {
318
+ const option = getMixedOption(val);
319
+ return [labelInValue ? {
320
+ label: option?.[mergedFieldNames.label],
321
+ value: val
322
+ } : val, injectPropsWithOption(option)];
323
+ };
324
+ if (selected && onSelect) {
325
+ const [wrappedValue, option] = getSelectEnt();
326
+ onSelect(wrappedValue, option);
327
+ } else if (!selected && onDeselect && type !== 'clear') {
328
+ const [wrappedValue, option] = getSelectEnt();
329
+ onDeselect(wrappedValue, option);
330
+ }
331
+ };
332
+
333
+ // Used for OptionList selection
334
+ const onInternalSelect = useRefFunc((val, info) => {
335
+ let cloneValues;
336
+
337
+ // Single mode always trigger select only with option list
338
+ const mergedSelect = multiple ? info.selected : true;
339
+ if (mergedSelect) {
340
+ cloneValues = multiple ? [...mergedValues, val] : [val];
341
+ } else {
342
+ cloneValues = mergedValues.filter(v => v.value !== val);
343
+ }
344
+ triggerChange(cloneValues);
345
+ triggerSelect(val, mergedSelect);
346
+
347
+ // Clean search value if single or configured
348
+ if (mode === 'combobox') {
349
+ setActiveValue('');
350
+ } else if (!isMultiple || autoClearSearchValue) {
351
+ setSearchValue('');
352
+ setActiveValue('');
353
+ }
354
+ });
355
+
356
+ // ======================= Display Change =======================
357
+ // BaseSelect display values change
358
+ const onDisplayValuesChange = (nextValues, info) => {
359
+ triggerChange(nextValues);
360
+ const {
361
+ type,
362
+ values
363
+ } = info;
364
+ if (type === 'remove' || type === 'clear') {
365
+ values.forEach(item => {
366
+ triggerSelect(item.value, false, type);
367
+ });
368
+ }
369
+ };
370
+
371
+ // =========================== Search ===========================
372
+ const onInternalSearch = (searchText, info) => {
373
+ setSearchValue(searchText);
374
+ setActiveValue(null);
375
+
376
+ // [Submit] Tag mode should flush input
377
+ if (info.source === 'submit') {
378
+ const formatted = (searchText || '').trim();
379
+ // prevent empty tags from appearing when you click the Enter button
380
+ if (formatted) {
381
+ const newRawValues = Array.from(new Set([...rawValues, formatted]));
382
+ triggerChange(newRawValues);
383
+ triggerSelect(formatted, true);
384
+ setSearchValue('');
385
+ }
386
+ return;
387
+ }
388
+ if (info.source !== 'blur') {
389
+ if (mode === 'combobox') {
390
+ triggerChange(searchText);
391
+ }
392
+ onSearch?.(searchText);
393
+ }
394
+ };
395
+ const onInternalSearchSplit = words => {
396
+ let patchValues = words;
397
+ if (mode !== 'tags') {
398
+ patchValues = words.map(word => {
399
+ const opt = labelOptions.get(word);
400
+ return opt?.value;
401
+ }).filter(val => val !== undefined);
402
+ }
403
+ const newRawValues = Array.from(new Set([...rawValues, ...patchValues]));
404
+ triggerChange(newRawValues);
405
+ newRawValues.forEach(newRawValue => {
406
+ triggerSelect(newRawValue, true);
407
+ });
408
+ };
409
+
410
+ // ========================== Context ===========================
411
+ const selectContext = React.useMemo(() => {
412
+ const realVirtual = virtual !== false && popupMatchSelectWidth !== false;
413
+ return {
414
+ ...parsedOptions,
415
+ flattenOptions: displayOptions,
416
+ onActiveValue,
417
+ defaultActiveFirstOption: mergedDefaultActiveFirstOption,
418
+ onSelect: onInternalSelect,
419
+ menuItemSelectedIcon,
420
+ rawValues,
421
+ fieldNames: mergedFieldNames,
422
+ virtual: realVirtual,
423
+ direction,
424
+ listHeight,
425
+ listItemHeight,
426
+ childrenAsData,
427
+ maxCount,
428
+ optionRender
429
+ };
430
+ }, [maxCount, parsedOptions, displayOptions, onActiveValue, mergedDefaultActiveFirstOption, onInternalSelect, menuItemSelectedIcon, rawValues, mergedFieldNames, virtual, popupMatchSelectWidth, direction, listHeight, listItemHeight, childrenAsData, optionRender]);
431
+
432
+ // ========================== Warning ===========================
433
+ if (process.env.NODE_ENV !== 'production') {
434
+ warningProps(props);
435
+ warningNullOptions(mergedOptions, mergedFieldNames);
436
+ }
437
+
438
+ // ==============================================================
439
+ // == Render ==
440
+ // ==============================================================
441
+ return /*#__PURE__*/React.createElement(SelectContext.Provider, {
442
+ value: selectContext
443
+ }, /*#__PURE__*/React.createElement(BaseSelect, _extends({}, restProps, {
444
+ // >>> MISC
445
+ id: mergedId,
446
+ prefixCls: prefixCls,
447
+ ref: ref,
448
+ omitDomProps: OMIT_DOM_PROPS,
449
+ mode: mode
450
+ // >>> Values
451
+ ,
452
+ displayValues: displayValues,
453
+ onDisplayValuesChange: onDisplayValuesChange
454
+ // >>> Trigger
455
+ ,
456
+ direction: direction
457
+ // >>> Search
458
+ ,
459
+ searchValue: mergedSearchValue,
460
+ onSearch: onInternalSearch,
461
+ autoClearSearchValue: autoClearSearchValue,
462
+ onSearchSplit: onInternalSearchSplit,
463
+ popupMatchSelectWidth: popupMatchSelectWidth
464
+ // >>> OptionList
465
+ ,
466
+ OptionList: OptionList,
467
+ emptyOptions: !displayOptions.length
468
+ // >>> Accessibility
469
+ ,
470
+ activeValue: activeValue,
471
+ activeDescendantId: `${mergedId}_list_${accessibilityIndex}`
472
+ })));
473
+ });
474
+ if (process.env.NODE_ENV !== 'production') {
475
+ Select.displayName = 'Select';
476
+ }
477
+ const TypedSelect = Select;
478
+ TypedSelect.Option = Option;
479
+ TypedSelect.OptGroup = OptGroup;
480
+ export default TypedSelect;
@@ -0,0 +1,23 @@
1
+ import * as React from 'react';
2
+ import type { RawValueType, RenderNode } from './BaseSelect';
3
+ import type { BaseOptionType, FieldNames, OnActiveValue, OnInternalSelect, SelectProps } from './Select';
4
+ import type { FlattenOptionData } from './interface';
5
+ export interface SelectContextProps {
6
+ options: BaseOptionType[];
7
+ optionRender?: SelectProps['optionRender'];
8
+ flattenOptions: FlattenOptionData<BaseOptionType>[];
9
+ onActiveValue: OnActiveValue;
10
+ defaultActiveFirstOption?: boolean;
11
+ onSelect: OnInternalSelect;
12
+ menuItemSelectedIcon?: RenderNode;
13
+ rawValues: Set<RawValueType>;
14
+ fieldNames?: FieldNames;
15
+ virtual?: boolean;
16
+ direction?: 'ltr' | 'rtl';
17
+ listHeight?: number;
18
+ listItemHeight?: number;
19
+ childrenAsData?: boolean;
20
+ maxCount?: number;
21
+ }
22
+ declare const SelectContext: React.Context<SelectContextProps>;
23
+ export default SelectContext;
@@ -0,0 +1,6 @@
1
+ import * as React from 'react';
2
+
3
+ // Use any here since we do not get the type during compilation
4
+
5
+ const SelectContext = /*#__PURE__*/React.createContext(null);
6
+ export default SelectContext;
@@ -0,0 +1,30 @@
1
+ import type { AlignType, BuildInPlacements } from '@rc-component/trigger/lib/interface';
2
+ import * as React from 'react';
3
+ import type { Placement, RenderDOMFunc } from './BaseSelect';
4
+ export interface RefTriggerProps {
5
+ getPopupElement: () => HTMLDivElement;
6
+ }
7
+ export interface SelectTriggerProps {
8
+ prefixCls: string;
9
+ children: React.ReactElement;
10
+ disabled: boolean;
11
+ visible: boolean;
12
+ popupElement: React.ReactElement;
13
+ animation?: string;
14
+ transitionName?: string;
15
+ placement?: Placement;
16
+ builtinPlacements?: BuildInPlacements;
17
+ popupStyle: React.CSSProperties;
18
+ popupClassName: string;
19
+ direction: string;
20
+ popupMatchSelectWidth?: boolean | number;
21
+ popupRender?: (menu: React.ReactElement) => React.ReactElement;
22
+ getPopupContainer?: RenderDOMFunc;
23
+ popupAlign: AlignType;
24
+ empty: boolean;
25
+ getTriggerDOMNode: (node: HTMLElement) => HTMLElement;
26
+ onPopupVisibleChange?: (visible: boolean) => void;
27
+ onPopupMouseEnter: () => void;
28
+ }
29
+ declare const RefSelectTrigger: React.ForwardRefExoticComponent<SelectTriggerProps & React.RefAttributes<RefTriggerProps>>;
30
+ export default RefSelectTrigger;
@@ -0,0 +1,138 @@
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 Trigger from '@rc-component/trigger';
3
+ import classNames from 'classnames';
4
+ import * as React from 'react';
5
+ const getBuiltInPlacements = popupMatchSelectWidth => {
6
+ // Enable horizontal overflow auto-adjustment when a custom dropdown width is provided
7
+ const adjustX = popupMatchSelectWidth === true ? 0 : 1;
8
+ return {
9
+ bottomLeft: {
10
+ points: ['tl', 'bl'],
11
+ offset: [0, 4],
12
+ overflow: {
13
+ adjustX,
14
+ adjustY: 1
15
+ },
16
+ htmlRegion: 'scroll'
17
+ },
18
+ bottomRight: {
19
+ points: ['tr', 'br'],
20
+ offset: [0, 4],
21
+ overflow: {
22
+ adjustX,
23
+ adjustY: 1
24
+ },
25
+ htmlRegion: 'scroll'
26
+ },
27
+ topLeft: {
28
+ points: ['bl', 'tl'],
29
+ offset: [0, -4],
30
+ overflow: {
31
+ adjustX,
32
+ adjustY: 1
33
+ },
34
+ htmlRegion: 'scroll'
35
+ },
36
+ topRight: {
37
+ points: ['br', 'tr'],
38
+ offset: [0, -4],
39
+ overflow: {
40
+ adjustX,
41
+ adjustY: 1
42
+ },
43
+ htmlRegion: 'scroll'
44
+ }
45
+ };
46
+ };
47
+ const SelectTrigger = (props, ref) => {
48
+ const {
49
+ prefixCls,
50
+ disabled,
51
+ visible,
52
+ children,
53
+ popupElement,
54
+ animation,
55
+ transitionName,
56
+ popupStyle,
57
+ popupClassName,
58
+ direction = 'ltr',
59
+ placement,
60
+ builtinPlacements,
61
+ popupMatchSelectWidth,
62
+ popupRender,
63
+ popupAlign,
64
+ getPopupContainer,
65
+ empty,
66
+ getTriggerDOMNode,
67
+ onPopupVisibleChange,
68
+ onPopupMouseEnter,
69
+ ...restProps
70
+ } = props;
71
+
72
+ // We still use `dropdown` className to keep compatibility
73
+ // This is used for:
74
+ // 1. Styles
75
+ // 2. Animation
76
+ // 3. Theme customization
77
+ // Please do not modify this since it's a breaking change
78
+ const popupPrefixCls = `${prefixCls}-dropdown`;
79
+ let popupNode = popupElement;
80
+ if (popupRender) {
81
+ popupNode = popupRender(popupElement);
82
+ }
83
+ const mergedBuiltinPlacements = React.useMemo(() => builtinPlacements || getBuiltInPlacements(popupMatchSelectWidth), [builtinPlacements, popupMatchSelectWidth]);
84
+
85
+ // ===================== Motion ======================
86
+ const mergedTransitionName = animation ? `${popupPrefixCls}-${animation}` : transitionName;
87
+
88
+ // =================== Popup Width ===================
89
+ const isNumberPopupWidth = typeof popupMatchSelectWidth === 'number';
90
+ const stretch = React.useMemo(() => {
91
+ if (isNumberPopupWidth) {
92
+ return null;
93
+ }
94
+ return popupMatchSelectWidth === false ? 'minWidth' : 'width';
95
+ }, [popupMatchSelectWidth, isNumberPopupWidth]);
96
+ let mergedPopupStyle = popupStyle;
97
+ if (isNumberPopupWidth) {
98
+ mergedPopupStyle = {
99
+ ...popupStyle,
100
+ width: popupMatchSelectWidth
101
+ };
102
+ }
103
+
104
+ // ======================= Ref =======================
105
+ const triggerPopupRef = React.useRef(null);
106
+ React.useImperativeHandle(ref, () => ({
107
+ getPopupElement: () => triggerPopupRef.current?.popupElement
108
+ }));
109
+ return /*#__PURE__*/React.createElement(Trigger, _extends({}, restProps, {
110
+ showAction: onPopupVisibleChange ? ['click'] : [],
111
+ hideAction: onPopupVisibleChange ? ['click'] : [],
112
+ popupPlacement: placement || (direction === 'rtl' ? 'bottomRight' : 'bottomLeft'),
113
+ builtinPlacements: mergedBuiltinPlacements,
114
+ prefixCls: popupPrefixCls,
115
+ popupMotion: {
116
+ motionName: mergedTransitionName
117
+ },
118
+ popup: /*#__PURE__*/React.createElement("div", {
119
+ onMouseEnter: onPopupMouseEnter
120
+ }, popupNode),
121
+ ref: triggerPopupRef,
122
+ stretch: stretch,
123
+ popupAlign: popupAlign,
124
+ popupVisible: visible,
125
+ getPopupContainer: getPopupContainer,
126
+ popupClassName: classNames(popupClassName, {
127
+ [`${popupPrefixCls}-empty`]: empty
128
+ }),
129
+ popupStyle: mergedPopupStyle,
130
+ getTriggerDOMNode: getTriggerDOMNode,
131
+ onPopupVisibleChange: onPopupVisibleChange
132
+ }), children);
133
+ };
134
+ const RefSelectTrigger = /*#__PURE__*/React.forwardRef(SelectTrigger);
135
+ if (process.env.NODE_ENV !== 'production') {
136
+ RefSelectTrigger.displayName = 'SelectTrigger';
137
+ }
138
+ export default RefSelectTrigger;