@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
@@ -0,0 +1,184 @@
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
+ * Cursor rule:
4
+ * 1. Only `showSearch` enabled
5
+ * 2. Only `open` is `true`
6
+ * 3. When typing, set `open` to `true` which hit rule of 2
7
+ *
8
+ * Accessibility:
9
+ * - https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html
10
+ */
11
+
12
+ import KeyCode from "@rc-component/util/es/KeyCode";
13
+ import * as React from 'react';
14
+ import { useRef } from 'react';
15
+ import useLock from "../hooks/useLock";
16
+ import { isValidateOpenKey } from "../utils/keyUtil";
17
+ import MultipleSelector from "./MultipleSelector";
18
+ import SingleSelector from "./SingleSelector";
19
+ const Selector = (props, ref) => {
20
+ const inputRef = useRef(null);
21
+ const compositionStatusRef = useRef(false);
22
+ const {
23
+ prefixCls,
24
+ open,
25
+ mode,
26
+ showSearch,
27
+ tokenWithEnter,
28
+ disabled,
29
+ prefix,
30
+ autoClearSearchValue,
31
+ onSearch,
32
+ onSearchSubmit,
33
+ onToggleOpen,
34
+ onInputKeyDown,
35
+ onInputBlur,
36
+ domRef
37
+ } = props;
38
+
39
+ // ======================= Ref =======================
40
+ React.useImperativeHandle(ref, () => ({
41
+ focus: options => {
42
+ inputRef.current.focus(options);
43
+ },
44
+ blur: () => {
45
+ inputRef.current.blur();
46
+ }
47
+ }));
48
+
49
+ // ====================== Input ======================
50
+ const [getInputMouseDown, setInputMouseDown] = useLock(0);
51
+ const onInternalInputKeyDown = event => {
52
+ const {
53
+ which
54
+ } = event;
55
+
56
+ // Compatible with multiple lines in TextArea
57
+ const isTextAreaElement = inputRef.current instanceof HTMLTextAreaElement;
58
+ if (!isTextAreaElement && open && (which === KeyCode.UP || which === KeyCode.DOWN)) {
59
+ event.preventDefault();
60
+ }
61
+ if (onInputKeyDown) {
62
+ onInputKeyDown(event);
63
+ }
64
+ if (which === KeyCode.ENTER && mode === 'tags' && !compositionStatusRef.current && !open) {
65
+ // When menu isn't open, OptionList won't trigger a value change
66
+ // So when enter is pressed, the tag's input value should be emitted here to let selector know
67
+ onSearchSubmit?.(event.target.value);
68
+ }
69
+ // Move within the text box
70
+ if (isTextAreaElement && !open && ~[KeyCode.UP, KeyCode.DOWN, KeyCode.LEFT, KeyCode.RIGHT].indexOf(which)) {
71
+ return;
72
+ }
73
+ if (isValidateOpenKey(which)) {
74
+ onToggleOpen(true);
75
+ }
76
+ };
77
+
78
+ /**
79
+ * We can not use `findDOMNode` sine it will get warning,
80
+ * have to use timer to check if is input element.
81
+ */
82
+ const onInternalInputMouseDown = () => {
83
+ setInputMouseDown(true);
84
+ };
85
+
86
+ // When paste come, ignore next onChange
87
+ const pastedTextRef = useRef(null);
88
+ const triggerOnSearch = value => {
89
+ if (onSearch(value, true, compositionStatusRef.current) !== false) {
90
+ onToggleOpen(true);
91
+ }
92
+ };
93
+ const onInputCompositionStart = () => {
94
+ compositionStatusRef.current = true;
95
+ };
96
+ const onInputCompositionEnd = e => {
97
+ compositionStatusRef.current = false;
98
+
99
+ // Trigger search again to support `tokenSeparators` with typewriting
100
+ if (mode !== 'combobox') {
101
+ triggerOnSearch(e.target.value);
102
+ }
103
+ };
104
+ const onInputChange = event => {
105
+ let {
106
+ target: {
107
+ value
108
+ }
109
+ } = event;
110
+
111
+ // Pasted text should replace back to origin content
112
+ if (tokenWithEnter && pastedTextRef.current && /[\r\n]/.test(pastedTextRef.current)) {
113
+ // CRLF will be treated as a single space for input element
114
+ const replacedText = pastedTextRef.current.replace(/[\r\n]+$/, '').replace(/\r\n/g, ' ').replace(/[\r\n]/g, ' ');
115
+ value = value.replace(replacedText, pastedTextRef.current);
116
+ }
117
+ pastedTextRef.current = null;
118
+ triggerOnSearch(value);
119
+ };
120
+ const onInputPaste = e => {
121
+ const {
122
+ clipboardData
123
+ } = e;
124
+ const value = clipboardData?.getData('text');
125
+ pastedTextRef.current = value || '';
126
+ };
127
+ const onClick = ({
128
+ target
129
+ }) => {
130
+ if (target !== inputRef.current) {
131
+ // Should focus input if click the selector
132
+ const isIE = document.body.style.msTouchAction !== undefined;
133
+ if (isIE) {
134
+ setTimeout(() => {
135
+ inputRef.current.focus();
136
+ });
137
+ } else {
138
+ inputRef.current.focus();
139
+ }
140
+ }
141
+ };
142
+ const onMouseDown = event => {
143
+ const inputMouseDown = getInputMouseDown();
144
+
145
+ // when mode is combobox and it is disabled, don't prevent default behavior
146
+ // https://github.com/ant-design/ant-design/issues/37320
147
+ // https://github.com/ant-design/ant-design/issues/48281
148
+ if (event.target !== inputRef.current && !inputMouseDown && !(mode === 'combobox' && disabled)) {
149
+ event.preventDefault();
150
+ }
151
+ if (mode !== 'combobox' && (!showSearch || !inputMouseDown) || !open) {
152
+ if (open && autoClearSearchValue !== false) {
153
+ onSearch('', true, false);
154
+ }
155
+ onToggleOpen();
156
+ }
157
+ };
158
+
159
+ // ================= Inner Selector ==================
160
+ const sharedProps = {
161
+ inputRef,
162
+ onInputKeyDown: onInternalInputKeyDown,
163
+ onInputMouseDown: onInternalInputMouseDown,
164
+ onInputChange,
165
+ onInputPaste,
166
+ onInputCompositionStart,
167
+ onInputCompositionEnd,
168
+ onInputBlur
169
+ };
170
+ const selectNode = mode === 'multiple' || mode === 'tags' ? /*#__PURE__*/React.createElement(MultipleSelector, _extends({}, props, sharedProps)) : /*#__PURE__*/React.createElement(SingleSelector, _extends({}, props, sharedProps));
171
+ return /*#__PURE__*/React.createElement("div", {
172
+ ref: domRef,
173
+ className: `${prefixCls}-selector`,
174
+ onClick: onClick,
175
+ onMouseDown: onMouseDown
176
+ }, prefix && /*#__PURE__*/React.createElement("div", {
177
+ className: `${prefixCls}-prefix`
178
+ }, prefix), selectNode);
179
+ };
180
+ const ForwardSelector = /*#__PURE__*/React.forwardRef(Selector);
181
+ if (process.env.NODE_ENV !== 'production') {
182
+ ForwardSelector.displayName = 'Selector';
183
+ }
184
+ export default ForwardSelector;
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ import type { RenderNode } from './BaseSelect';
3
+ export interface TransBtnProps {
4
+ className: string;
5
+ customizeIcon: RenderNode;
6
+ customizeIconProps?: any;
7
+ onMouseDown?: React.MouseEventHandler<HTMLSpanElement>;
8
+ onClick?: React.MouseEventHandler<HTMLSpanElement>;
9
+ children?: React.ReactNode;
10
+ }
11
+ declare const TransBtn: React.FC<TransBtnProps>;
12
+ export default TransBtn;
package/es/TransBtn.js ADDED
@@ -0,0 +1,30 @@
1
+ import * as React from 'react';
2
+ import classNames from 'classnames';
3
+ const TransBtn = props => {
4
+ const {
5
+ className,
6
+ customizeIcon,
7
+ customizeIconProps,
8
+ children,
9
+ onMouseDown,
10
+ onClick
11
+ } = props;
12
+ const icon = typeof customizeIcon === 'function' ? customizeIcon(customizeIconProps) : customizeIcon;
13
+ return /*#__PURE__*/React.createElement("span", {
14
+ className: className,
15
+ onMouseDown: event => {
16
+ event.preventDefault();
17
+ onMouseDown?.(event);
18
+ },
19
+ style: {
20
+ userSelect: 'none',
21
+ WebkitUserSelect: 'none'
22
+ },
23
+ unselectable: "on",
24
+ onClick: onClick,
25
+ "aria-hidden": true
26
+ }, icon !== undefined ? icon : /*#__PURE__*/React.createElement("span", {
27
+ className: classNames(className.split(/\s+/).map(cls => `${cls}-icon`))
28
+ }, children));
29
+ };
30
+ export default TransBtn;
@@ -0,0 +1,8 @@
1
+ import type { DisplayValueType, Mode, RenderNode } from '../interface';
2
+ import React from 'react';
3
+ export declare const useAllowClear: (prefixCls: string, onClearMouseDown: React.MouseEventHandler<HTMLSpanElement>, displayValues: DisplayValueType[], allowClear?: boolean | {
4
+ clearIcon?: RenderNode;
5
+ }, clearIcon?: RenderNode, disabled?: boolean, mergedSearchValue?: string, mode?: Mode) => {
6
+ allowClear: boolean;
7
+ clearIcon: React.JSX.Element;
8
+ };
@@ -0,0 +1,26 @@
1
+ import TransBtn from "../TransBtn";
2
+ import React from 'react';
3
+ export const useAllowClear = (prefixCls, onClearMouseDown, displayValues, allowClear, clearIcon, disabled = false, mergedSearchValue, mode) => {
4
+ const mergedClearIcon = React.useMemo(() => {
5
+ if (typeof allowClear === 'object') {
6
+ return allowClear.clearIcon;
7
+ }
8
+ if (clearIcon) {
9
+ return clearIcon;
10
+ }
11
+ }, [allowClear, clearIcon]);
12
+ const mergedAllowClear = React.useMemo(() => {
13
+ if (!disabled && !!allowClear && (displayValues.length || mergedSearchValue) && !(mode === 'combobox' && mergedSearchValue === '')) {
14
+ return true;
15
+ }
16
+ return false;
17
+ }, [allowClear, disabled, displayValues.length, mergedSearchValue, mode]);
18
+ return {
19
+ allowClear: mergedAllowClear,
20
+ clearIcon: /*#__PURE__*/React.createElement(TransBtn, {
21
+ className: `${prefixCls}-clear`,
22
+ onMouseDown: onClearMouseDown,
23
+ customizeIcon: mergedClearIcon
24
+ }, "\xD7")
25
+ };
26
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * BaseSelect provide some parsed data into context.
3
+ * You can use this hooks to get them.
4
+ */
5
+ import * as React from 'react';
6
+ import type { BaseSelectProps } from '../BaseSelect';
7
+ export interface BaseSelectContextProps extends BaseSelectProps {
8
+ triggerOpen: boolean;
9
+ multiple: boolean;
10
+ toggleOpen: (open?: boolean) => void;
11
+ }
12
+ export declare const BaseSelectContext: React.Context<BaseSelectContextProps>;
13
+ export default function useBaseProps(): BaseSelectContextProps;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * BaseSelect provide some parsed data into context.
3
+ * You can use this hooks to get them.
4
+ */
5
+
6
+ import * as React from 'react';
7
+ export const BaseSelectContext = /*#__PURE__*/React.createContext(null);
8
+ export default function useBaseProps() {
9
+ return React.useContext(BaseSelectContext);
10
+ }
@@ -0,0 +1,7 @@
1
+ import type { RawValueType } from '../BaseSelect';
2
+ import type { DefaultOptionType, LabelInValueType } from '../Select';
3
+ /**
4
+ * Cache `value` related LabeledValue & options.
5
+ */
6
+ declare const _default: (labeledValues: LabelInValueType[], valueOptions: Map<RawValueType, DefaultOptionType>) => [LabelInValueType[], (val: RawValueType) => DefaultOptionType];
7
+ export default _default;
@@ -0,0 +1,40 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * Cache `value` related LabeledValue & options.
4
+ */
5
+ export default ((labeledValues, valueOptions) => {
6
+ const cacheRef = React.useRef({
7
+ values: new Map(),
8
+ options: new Map()
9
+ });
10
+ const filledLabeledValues = React.useMemo(() => {
11
+ const {
12
+ values: prevValueCache,
13
+ options: prevOptionCache
14
+ } = cacheRef.current;
15
+
16
+ // Fill label by cache
17
+ const patchedValues = labeledValues.map(item => {
18
+ if (item.label === undefined) {
19
+ return {
20
+ ...item,
21
+ label: prevValueCache.get(item.value)?.label
22
+ };
23
+ }
24
+ return item;
25
+ });
26
+
27
+ // Refresh cache
28
+ const valueCache = new Map();
29
+ const optionCache = new Map();
30
+ patchedValues.forEach(item => {
31
+ valueCache.set(item.value, item);
32
+ optionCache.set(item.value, valueOptions.get(item.value) || prevOptionCache.get(item.value));
33
+ });
34
+ cacheRef.current.values = valueCache;
35
+ cacheRef.current.options = optionCache;
36
+ return patchedValues;
37
+ }, [labeledValues, valueOptions]);
38
+ const getOption = React.useCallback(val => valueOptions.get(val) || cacheRef.current.options.get(val), [valueOptions]);
39
+ return [filledLabeledValues, getOption];
40
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Similar with `useLock`, but this hook will always execute last value.
3
+ * When set to `true`, it will keep `true` for a short time even if `false` is set.
4
+ */
5
+ export default function useDelayReset(timeout?: number): [boolean, (val: boolean, callback?: () => void) => void, () => void];
@@ -0,0 +1,24 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * Similar with `useLock`, but this hook will always execute last value.
5
+ * When set to `true`, it will keep `true` for a short time even if `false` is set.
6
+ */
7
+ export default function useDelayReset(timeout = 10) {
8
+ const [bool, setBool] = React.useState(false);
9
+ const delayRef = React.useRef(null);
10
+ const cancelLatest = () => {
11
+ window.clearTimeout(delayRef.current);
12
+ };
13
+ React.useEffect(() => cancelLatest, []);
14
+ const delaySetBool = (value, callback) => {
15
+ cancelLatest();
16
+ delayRef.current = window.setTimeout(() => {
17
+ setBool(value);
18
+ if (callback) {
19
+ callback();
20
+ }
21
+ }, timeout);
22
+ };
23
+ return [bool, delaySetBool, cancelLatest];
24
+ }
@@ -0,0 +1,3 @@
1
+ import type { FieldNames, DefaultOptionType, SelectProps } from '../Select';
2
+ declare const _default: (options: DefaultOptionType[], fieldNames: FieldNames, searchValue?: string, filterOption?: SelectProps['filterOption'], optionFilterProp?: string) => DefaultOptionType[];
3
+ export default _default;
@@ -0,0 +1,57 @@
1
+ import * as React from 'react';
2
+ import { toArray } from "../utils/commonUtil";
3
+ import { injectPropsWithOption } from "../utils/valueUtil";
4
+ function includes(test, search) {
5
+ return toArray(test).join('').toUpperCase().includes(search);
6
+ }
7
+ export default ((options, fieldNames, searchValue, filterOption, optionFilterProp) => React.useMemo(() => {
8
+ if (!searchValue || filterOption === false) {
9
+ return options;
10
+ }
11
+ const {
12
+ options: fieldOptions,
13
+ label: fieldLabel,
14
+ value: fieldValue
15
+ } = fieldNames;
16
+ const filteredOptions = [];
17
+ const customizeFilter = typeof filterOption === 'function';
18
+ const upperSearch = searchValue.toUpperCase();
19
+ const filterFunc = customizeFilter ? filterOption : (_, option) => {
20
+ // Use provided `optionFilterProp`
21
+ if (optionFilterProp) {
22
+ return includes(option[optionFilterProp], upperSearch);
23
+ }
24
+
25
+ // Auto select `label` or `value` by option type
26
+ if (option[fieldOptions]) {
27
+ // hack `fieldLabel` since `OptionGroup` children is not `label`
28
+ return includes(option[fieldLabel !== 'children' ? fieldLabel : 'label'], upperSearch);
29
+ }
30
+ return includes(option[fieldValue], upperSearch);
31
+ };
32
+ const wrapOption = customizeFilter ? opt => injectPropsWithOption(opt) : opt => opt;
33
+ options.forEach(item => {
34
+ // Group should check child options
35
+ if (item[fieldOptions]) {
36
+ // Check group first
37
+ const matchGroup = filterFunc(searchValue, wrapOption(item));
38
+ if (matchGroup) {
39
+ filteredOptions.push(item);
40
+ } else {
41
+ // Check option
42
+ const subOptions = item[fieldOptions].filter(subItem => filterFunc(searchValue, wrapOption(subItem)));
43
+ if (subOptions.length) {
44
+ filteredOptions.push({
45
+ ...item,
46
+ [fieldOptions]: subOptions
47
+ });
48
+ }
49
+ }
50
+ return;
51
+ }
52
+ if (filterFunc(searchValue, wrapOption(item))) {
53
+ filteredOptions.push(item);
54
+ }
55
+ });
56
+ return filteredOptions;
57
+ }, [options, filterOption, optionFilterProp, searchValue, fieldNames]));
@@ -0,0 +1,5 @@
1
+ /** Is client side and not jsdom */
2
+ export declare const isBrowserClient: boolean;
3
+ /** Get unique id for accessibility usage */
4
+ export declare function getUUID(): number | string;
5
+ export default function useId(id?: string): string;
@@ -0,0 +1,29 @@
1
+ import * as React from 'react';
2
+ import canUseDom from "@rc-component/util/es/Dom/canUseDom";
3
+ let uuid = 0;
4
+
5
+ /** Is client side and not jsdom */
6
+ export const isBrowserClient = process.env.NODE_ENV !== 'test' && canUseDom();
7
+
8
+ /** Get unique id for accessibility usage */
9
+ export function getUUID() {
10
+ let retId;
11
+
12
+ // Test never reach
13
+ /* istanbul ignore if */
14
+ if (isBrowserClient) {
15
+ retId = uuid;
16
+ uuid += 1;
17
+ } else {
18
+ retId = 'TEST_OR_SSR';
19
+ }
20
+ return retId;
21
+ }
22
+ export default function useId(id) {
23
+ // Inner id for accessibility usage. Only work in client side
24
+ const [innerId, setInnerId] = React.useState();
25
+ React.useEffect(() => {
26
+ setInnerId(`rc_select_${getUUID()}`);
27
+ }, []);
28
+ return id || innerId;
29
+ }
@@ -0,0 +1,5 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * Wrap `React.useLayoutEffect` which will not throw warning message in test env
4
+ */
5
+ export default function useLayoutEffect(effect: React.EffectCallback, deps?: React.DependencyList): void;
@@ -0,0 +1,17 @@
1
+ /* eslint-disable react-hooks/rules-of-hooks */
2
+ import * as React from 'react';
3
+ import { isBrowserClient } from "../utils/commonUtil";
4
+
5
+ /**
6
+ * Wrap `React.useLayoutEffect` which will not throw warning message in test env
7
+ */
8
+ export default function useLayoutEffect(effect, deps) {
9
+ // Never happen in test env
10
+ if (isBrowserClient) {
11
+ /* istanbul ignore next */
12
+ React.useLayoutEffect(effect, deps);
13
+ } else {
14
+ React.useEffect(effect, deps);
15
+ }
16
+ }
17
+ /* eslint-enable */
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Locker return cached mark.
3
+ * If set to `true`, will return `true` in a short time even if set `false`.
4
+ * If set to `false` and then set to `true`, will change to `true`.
5
+ * And after time duration, it will back to `null` automatically.
6
+ */
7
+ export default function useLock(duration?: number): [() => boolean, (lock: boolean) => void];
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * Locker return cached mark.
5
+ * If set to `true`, will return `true` in a short time even if set `false`.
6
+ * If set to `false` and then set to `true`, will change to `true`.
7
+ * And after time duration, it will back to `null` automatically.
8
+ */
9
+ export default function useLock(duration = 250) {
10
+ const lockRef = React.useRef(null);
11
+ const timeoutRef = React.useRef(null);
12
+
13
+ // Clean up
14
+ React.useEffect(() => () => {
15
+ window.clearTimeout(timeoutRef.current);
16
+ }, []);
17
+ function doLock(locked) {
18
+ if (locked || lockRef.current === null) {
19
+ lockRef.current = locked;
20
+ }
21
+ window.clearTimeout(timeoutRef.current);
22
+ timeoutRef.current = window.setTimeout(() => {
23
+ lockRef.current = null;
24
+ }, duration);
25
+ }
26
+ return [() => lockRef.current, doLock];
27
+ }
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ import type { FieldNames, RawValueType } from '../Select';
3
+ /**
4
+ * Parse `children` to `options` if `options` is not provided.
5
+ * Then flatten the `options`.
6
+ */
7
+ declare const useOptions: <OptionType>(options: OptionType[], children: React.ReactNode, fieldNames: FieldNames, optionFilterProp: string, optionLabelProp: string) => {
8
+ options: OptionType[];
9
+ valueOptions: Map<RawValueType, OptionType>;
10
+ labelOptions: Map<React.ReactNode, OptionType>;
11
+ };
12
+ export default useOptions;
@@ -0,0 +1,45 @@
1
+ import * as React from 'react';
2
+ import { convertChildrenToData } from "../utils/legacyUtil";
3
+
4
+ /**
5
+ * Parse `children` to `options` if `options` is not provided.
6
+ * Then flatten the `options`.
7
+ */
8
+ const useOptions = (options, children, fieldNames, optionFilterProp, optionLabelProp) => {
9
+ return React.useMemo(() => {
10
+ let mergedOptions = options;
11
+ const childrenAsData = !options;
12
+ if (childrenAsData) {
13
+ mergedOptions = convertChildrenToData(children);
14
+ }
15
+ const valueOptions = new Map();
16
+ const labelOptions = new Map();
17
+ const setLabelOptions = (labelOptionsMap, option, key) => {
18
+ if (key && typeof key === 'string') {
19
+ labelOptionsMap.set(option[key], option);
20
+ }
21
+ };
22
+ const dig = (optionList, isChildren = false) => {
23
+ // for loop to speed up collection speed
24
+ for (let i = 0; i < optionList.length; i += 1) {
25
+ const option = optionList[i];
26
+ if (!option[fieldNames.options] || isChildren) {
27
+ valueOptions.set(option[fieldNames.value], option);
28
+ setLabelOptions(labelOptions, option, fieldNames.label);
29
+ // https://github.com/ant-design/ant-design/issues/35304
30
+ setLabelOptions(labelOptions, option, optionFilterProp);
31
+ setLabelOptions(labelOptions, option, optionLabelProp);
32
+ } else {
33
+ dig(option[fieldNames.options], true);
34
+ }
35
+ }
36
+ };
37
+ dig(mergedOptions);
38
+ return {
39
+ options: mergedOptions,
40
+ valueOptions,
41
+ labelOptions
42
+ };
43
+ }, [options, children, fieldNames, optionFilterProp, optionLabelProp]);
44
+ };
45
+ export default useOptions;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Same as `React.useCallback` but always return a memoized function
3
+ * but redirect to real function.
4
+ */
5
+ export default function useRefFunc<T extends (...args: any[]) => any>(callback: T): T;
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * Same as `React.useCallback` but always return a memoized function
5
+ * but redirect to real function.
6
+ */
7
+ export default function useRefFunc(callback) {
8
+ const funcRef = React.useRef();
9
+ funcRef.current = callback;
10
+ const cacheFn = React.useCallback((...args) => {
11
+ return funcRef.current(...args);
12
+ }, []);
13
+ return cacheFn;
14
+ }
@@ -0,0 +1 @@
1
+ export default function useSelectTriggerControl(elements: () => (HTMLElement | undefined)[], open: boolean, triggerOpen: (open: boolean) => void, customizedTrigger: boolean): void;
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ export default function useSelectTriggerControl(elements, open, triggerOpen, customizedTrigger) {
3
+ const propsRef = React.useRef(null);
4
+ propsRef.current = {
5
+ open,
6
+ triggerOpen,
7
+ customizedTrigger
8
+ };
9
+ React.useEffect(() => {
10
+ function onGlobalMouseDown(event) {
11
+ // If trigger is customized, Trigger will take control of popupVisible
12
+ if (propsRef.current?.customizedTrigger) {
13
+ return;
14
+ }
15
+ let target = event.target;
16
+ if (target.shadowRoot && event.composed) {
17
+ target = event.composedPath()[0] || target;
18
+ }
19
+ if (propsRef.current.open && elements().filter(element => element).every(element => !element.contains(target) && element !== target)) {
20
+ // Should trigger close
21
+ propsRef.current.triggerOpen(false);
22
+ }
23
+ }
24
+ window.addEventListener('mousedown', onGlobalMouseDown);
25
+ return () => window.removeEventListener('mousedown', onGlobalMouseDown);
26
+ }, []);
27
+ }
package/es/index.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import Select from './Select';
2
+ import Option from './Option';
3
+ import OptGroup from './OptGroup';
4
+ import type { SelectProps } from './Select';
5
+ import BaseSelect from './BaseSelect';
6
+ import type { BaseSelectProps, BaseSelectRef, BaseSelectPropsWithoutPrivate } from './BaseSelect';
7
+ import useBaseProps from './hooks/useBaseProps';
8
+ export { Option, OptGroup, BaseSelect, useBaseProps };
9
+ export type { SelectProps, BaseSelectProps, BaseSelectRef, BaseSelectPropsWithoutPrivate };
10
+ export default Select;
package/es/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import Select from "./Select";
2
+ import Option from "./Option";
3
+ import OptGroup from "./OptGroup";
4
+ import BaseSelect from "./BaseSelect";
5
+ import useBaseProps from "./hooks/useBaseProps";
6
+ export { Option, OptGroup, BaseSelect, useBaseProps };
7
+ export default Select;