@rc-component/select 1.1.4 → 1.2.0-alpha.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.
- package/assets/index.css +63 -0
- package/assets/index.less +1 -0
- package/assets/patch.less +83 -0
- package/es/BaseSelect/index.d.ts +14 -3
- package/es/BaseSelect/index.js +137 -200
- package/es/OptionList.js +3 -3
- package/es/Select.d.ts +1 -1
- package/es/Select.js +5 -9
- package/es/SelectInput/Affix.d.ts +5 -0
- package/es/SelectInput/Affix.js +12 -0
- package/es/SelectInput/Content/MultipleContent.d.ts +4 -0
- package/es/SelectInput/Content/MultipleContent.js +152 -0
- package/es/SelectInput/Content/Placeholder.d.ts +5 -0
- package/es/SelectInput/Content/Placeholder.js +21 -0
- package/es/SelectInput/Content/SingleContent.d.ts +4 -0
- package/es/SelectInput/Content/SingleContent.js +98 -0
- package/es/SelectInput/Content/index.d.ts +6 -0
- package/es/SelectInput/Content/index.js +37 -0
- package/es/SelectInput/Input.d.ts +20 -0
- package/es/SelectInput/Input.js +214 -0
- package/es/SelectInput/context.d.ts +6 -0
- package/es/SelectInput/context.js +6 -0
- package/es/SelectInput/index.d.ts +39 -0
- package/es/SelectInput/index.js +189 -0
- package/es/SelectTrigger.d.ts +1 -0
- package/es/SelectTrigger.js +5 -3
- package/es/TransBtn.d.ts +10 -0
- package/es/TransBtn.js +12 -2
- package/es/hooks/useAllowClear.d.ts +8 -7
- package/es/hooks/useAllowClear.js +21 -23
- package/es/hooks/useBaseProps.d.ts +1 -0
- package/es/hooks/useComponents.d.ts +12 -0
- package/es/hooks/useComponents.js +23 -0
- package/es/hooks/useOpen.d.ts +15 -0
- package/es/hooks/useOpen.js +76 -0
- package/es/hooks/useSearchConfig.d.ts +2 -2
- package/es/hooks/useSearchConfig.js +3 -3
- package/es/hooks/useSelectTriggerControl.d.ts +1 -1
- package/es/hooks/useSelectTriggerControl.js +16 -21
- package/es/utils/keyUtil.js +4 -0
- package/lib/BaseSelect/index.d.ts +14 -3
- package/lib/BaseSelect/index.js +137 -201
- package/lib/OptionList.js +3 -3
- package/lib/Select.d.ts +1 -1
- package/lib/Select.js +5 -9
- package/lib/SelectInput/Affix.d.ts +5 -0
- package/lib/{hooks/useLayoutEffect.js → SelectInput/Affix.js} +11 -16
- package/lib/SelectInput/Content/MultipleContent.d.ts +4 -0
- package/lib/{Selector/MultipleSelector.js → SelectInput/Content/MultipleContent.js} +71 -104
- package/lib/SelectInput/Content/Placeholder.d.ts +5 -0
- package/lib/SelectInput/Content/Placeholder.js +29 -0
- package/lib/SelectInput/Content/SingleContent.d.ts +4 -0
- package/lib/SelectInput/Content/SingleContent.js +107 -0
- package/lib/SelectInput/Content/index.d.ts +6 -0
- package/lib/SelectInput/Content/index.js +46 -0
- package/lib/SelectInput/Input.d.ts +20 -0
- package/lib/SelectInput/Input.js +223 -0
- package/lib/SelectInput/context.d.ts +6 -0
- package/lib/SelectInput/context.js +15 -0
- package/lib/SelectInput/index.d.ts +39 -0
- package/lib/SelectInput/index.js +198 -0
- package/lib/SelectTrigger.d.ts +1 -0
- package/lib/SelectTrigger.js +5 -3
- package/lib/TransBtn.d.ts +10 -0
- package/lib/TransBtn.js +12 -3
- package/lib/hooks/useAllowClear.d.ts +8 -7
- package/lib/hooks/useAllowClear.js +21 -24
- package/lib/hooks/useBaseProps.d.ts +1 -0
- package/lib/hooks/useComponents.d.ts +12 -0
- package/lib/hooks/{useDelayReset.js → useComponents.js} +21 -30
- package/lib/hooks/useOpen.d.ts +15 -0
- package/lib/hooks/useOpen.js +82 -0
- package/lib/hooks/useSearchConfig.d.ts +2 -2
- package/lib/hooks/useSearchConfig.js +3 -3
- package/lib/hooks/useSelectTriggerControl.d.ts +1 -1
- package/lib/hooks/useSelectTriggerControl.js +16 -21
- package/lib/utils/keyUtil.js +4 -0
- package/package.json +5 -4
- package/es/Selector/Input.d.ts +0 -27
- package/es/Selector/Input.js +0 -61
- package/es/Selector/MultipleSelector.d.ts +0 -16
- package/es/Selector/MultipleSelector.js +0 -185
- package/es/Selector/SingleSelector.d.ts +0 -8
- package/es/Selector/SingleSelector.js +0 -104
- package/es/Selector/index.d.ts +0 -83
- package/es/Selector/index.js +0 -189
- package/es/hooks/useDelayReset.d.ts +0 -5
- package/es/hooks/useDelayReset.js +0 -33
- package/es/hooks/useLayoutEffect.d.ts +0 -5
- package/es/hooks/useLayoutEffect.js +0 -17
- package/lib/Selector/Input.d.ts +0 -27
- package/lib/Selector/Input.js +0 -70
- package/lib/Selector/MultipleSelector.d.ts +0 -16
- package/lib/Selector/SingleSelector.d.ts +0 -8
- package/lib/Selector/SingleSelector.js +0 -113
- package/lib/Selector/index.d.ts +0 -83
- package/lib/Selector/index.js +0 -196
- package/lib/hooks/useDelayReset.d.ts +0 -5
- package/lib/hooks/useLayoutEffect.d.ts +0 -5
|
@@ -0,0 +1,189 @@
|
|
|
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 * as React from 'react';
|
|
3
|
+
import Affix from "./Affix";
|
|
4
|
+
import SelectContent from "./Content";
|
|
5
|
+
import SelectInputContext from "./context";
|
|
6
|
+
import useBaseProps from "../hooks/useBaseProps";
|
|
7
|
+
import { omit, useEvent } from '@rc-component/util';
|
|
8
|
+
import KeyCode from "@rc-component/util/es/KeyCode";
|
|
9
|
+
import { isValidateOpenKey } from "../utils/keyUtil";
|
|
10
|
+
import { clsx } from 'clsx';
|
|
11
|
+
import { getDOM } from "@rc-component/util/es/Dom/findDOMNode";
|
|
12
|
+
import { composeRef } from "@rc-component/util/es/ref";
|
|
13
|
+
const DEFAULT_OMIT_PROPS = ['value', 'onChange', 'removeIcon', 'placeholder', 'maxTagCount', 'maxTagTextLength', 'maxTagPlaceholder', 'choiceTransitionName', 'onInputKeyDown', 'onPopupScroll', 'tabIndex', 'activeValue', 'onSelectorRemove', 'focused'];
|
|
14
|
+
export default /*#__PURE__*/React.forwardRef(function SelectInput(props, ref) {
|
|
15
|
+
const {
|
|
16
|
+
// Style
|
|
17
|
+
prefixCls,
|
|
18
|
+
className,
|
|
19
|
+
style,
|
|
20
|
+
// UI
|
|
21
|
+
prefix,
|
|
22
|
+
suffix,
|
|
23
|
+
clearIcon,
|
|
24
|
+
children,
|
|
25
|
+
// Data
|
|
26
|
+
multiple,
|
|
27
|
+
displayValues,
|
|
28
|
+
placeholder,
|
|
29
|
+
mode,
|
|
30
|
+
// Search
|
|
31
|
+
searchValue,
|
|
32
|
+
onSearch,
|
|
33
|
+
onSearchSubmit,
|
|
34
|
+
onInputBlur,
|
|
35
|
+
// Input
|
|
36
|
+
maxLength,
|
|
37
|
+
autoFocus,
|
|
38
|
+
// Events
|
|
39
|
+
onMouseDown,
|
|
40
|
+
onBlur,
|
|
41
|
+
onClearMouseDown,
|
|
42
|
+
onInputKeyDown,
|
|
43
|
+
onSelectorRemove,
|
|
44
|
+
// Token handling
|
|
45
|
+
tokenWithEnter,
|
|
46
|
+
// Components
|
|
47
|
+
components,
|
|
48
|
+
...restProps
|
|
49
|
+
} = props;
|
|
50
|
+
const {
|
|
51
|
+
triggerOpen,
|
|
52
|
+
toggleOpen,
|
|
53
|
+
showSearch,
|
|
54
|
+
disabled,
|
|
55
|
+
loading,
|
|
56
|
+
classNames,
|
|
57
|
+
styles
|
|
58
|
+
} = useBaseProps();
|
|
59
|
+
const rootRef = React.useRef(null);
|
|
60
|
+
const inputRef = React.useRef(null);
|
|
61
|
+
|
|
62
|
+
// Handle keyboard events similar to original Selector
|
|
63
|
+
const onInternalInputKeyDown = useEvent(event => {
|
|
64
|
+
const {
|
|
65
|
+
which
|
|
66
|
+
} = event;
|
|
67
|
+
|
|
68
|
+
// Compatible with multiple lines in TextArea
|
|
69
|
+
const isTextAreaElement = inputRef.current instanceof HTMLTextAreaElement;
|
|
70
|
+
|
|
71
|
+
// Prevent default behavior for up/down arrows when dropdown is open
|
|
72
|
+
if (!isTextAreaElement && triggerOpen && (which === KeyCode.UP || which === KeyCode.DOWN)) {
|
|
73
|
+
event.preventDefault();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Call the original onInputKeyDown callback
|
|
77
|
+
if (onInputKeyDown) {
|
|
78
|
+
onInputKeyDown(event);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Move within the text box for TextArea
|
|
82
|
+
if (isTextAreaElement && !triggerOpen && ~[KeyCode.UP, KeyCode.DOWN, KeyCode.LEFT, KeyCode.RIGHT].indexOf(which)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Open dropdown when a valid open key is pressed
|
|
87
|
+
if (isValidateOpenKey(which)) {
|
|
88
|
+
toggleOpen(true);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// ====================== Refs ======================
|
|
93
|
+
React.useImperativeHandle(ref, () => {
|
|
94
|
+
return {
|
|
95
|
+
focus: options => {
|
|
96
|
+
// Focus the inner input if available, otherwise fall back to root div.
|
|
97
|
+
(inputRef.current || rootRef.current).focus?.(options);
|
|
98
|
+
},
|
|
99
|
+
blur: () => {
|
|
100
|
+
(inputRef.current || rootRef.current).blur?.();
|
|
101
|
+
},
|
|
102
|
+
nativeElement: rootRef.current
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// ====================== Open ======================
|
|
107
|
+
const onInternalMouseDown = useEvent(event => {
|
|
108
|
+
if (!disabled) {
|
|
109
|
+
if (event.target !== getDOM(inputRef.current)) {
|
|
110
|
+
event.preventDefault();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check if we should prevent closing when clicking on selector
|
|
114
|
+
// Don't close if: open && not multiple && (combobox mode || showSearch)
|
|
115
|
+
const shouldPreventClose = triggerOpen && !multiple && (mode === 'combobox' || showSearch);
|
|
116
|
+
if (!event.nativeEvent._select_lazy) {
|
|
117
|
+
inputRef.current?.focus();
|
|
118
|
+
|
|
119
|
+
// Only toggle open if we should not prevent close
|
|
120
|
+
if (!shouldPreventClose) {
|
|
121
|
+
toggleOpen();
|
|
122
|
+
}
|
|
123
|
+
} else if (triggerOpen) {
|
|
124
|
+
// Lazy should also close when click clear icon
|
|
125
|
+
toggleOpen(false);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
onMouseDown?.(event);
|
|
129
|
+
});
|
|
130
|
+
const onInternalBlur = event => {
|
|
131
|
+
toggleOpen(false);
|
|
132
|
+
onBlur?.(event);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// =================== Components ===================
|
|
136
|
+
const {
|
|
137
|
+
root: RootComponent
|
|
138
|
+
} = components;
|
|
139
|
+
|
|
140
|
+
// ===================== Render =====================
|
|
141
|
+
const domProps = omit(restProps, DEFAULT_OMIT_PROPS);
|
|
142
|
+
|
|
143
|
+
// Create context value with wrapped callbacks
|
|
144
|
+
const contextValue = {
|
|
145
|
+
...props,
|
|
146
|
+
onInputKeyDown: onInternalInputKeyDown
|
|
147
|
+
};
|
|
148
|
+
if (RootComponent) {
|
|
149
|
+
if ( /*#__PURE__*/React.isValidElement(RootComponent)) {
|
|
150
|
+
return /*#__PURE__*/React.cloneElement(RootComponent, {
|
|
151
|
+
...domProps,
|
|
152
|
+
ref: composeRef(RootComponent.ref, rootRef)
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return /*#__PURE__*/React.createElement(RootComponent, _extends({}, domProps, {
|
|
156
|
+
ref: rootRef
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
159
|
+
return /*#__PURE__*/React.createElement(SelectInputContext.Provider, {
|
|
160
|
+
value: contextValue
|
|
161
|
+
}, /*#__PURE__*/React.createElement("div", _extends({}, domProps, {
|
|
162
|
+
// Style
|
|
163
|
+
ref: rootRef,
|
|
164
|
+
className: className,
|
|
165
|
+
style: style
|
|
166
|
+
// Mouse Events
|
|
167
|
+
,
|
|
168
|
+
onMouseDown: onInternalMouseDown,
|
|
169
|
+
onBlur: onInternalBlur
|
|
170
|
+
}), /*#__PURE__*/React.createElement(Affix, {
|
|
171
|
+
className: clsx(`${prefixCls}-prefix`, classNames?.prefix),
|
|
172
|
+
style: styles?.prefix
|
|
173
|
+
}, prefix), /*#__PURE__*/React.createElement(SelectContent, {
|
|
174
|
+
ref: inputRef
|
|
175
|
+
}), /*#__PURE__*/React.createElement(Affix, {
|
|
176
|
+
className: clsx(`${prefixCls}-suffix`, {
|
|
177
|
+
[`${prefixCls}-suffix-loading`]: loading
|
|
178
|
+
}, classNames?.suffix),
|
|
179
|
+
style: styles?.suffix
|
|
180
|
+
}, suffix), clearIcon && /*#__PURE__*/React.createElement(Affix, {
|
|
181
|
+
className: clsx(`${prefixCls}-clear`, classNames?.clear),
|
|
182
|
+
style: styles?.clear,
|
|
183
|
+
onMouseDown: e => {
|
|
184
|
+
// Mark to tell not trigger open or focus
|
|
185
|
+
e.nativeEvent._select_lazy = true;
|
|
186
|
+
onClearMouseDown?.(e);
|
|
187
|
+
}
|
|
188
|
+
}, clearIcon), children));
|
|
189
|
+
});
|
package/es/SelectTrigger.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export interface SelectTriggerProps {
|
|
|
24
24
|
empty: boolean;
|
|
25
25
|
onPopupVisibleChange?: (visible: boolean) => void;
|
|
26
26
|
onPopupMouseEnter: () => void;
|
|
27
|
+
onPopupMouseDown: React.MouseEventHandler<HTMLDivElement>;
|
|
27
28
|
}
|
|
28
29
|
declare const RefSelectTrigger: React.ForwardRefExoticComponent<SelectTriggerProps & React.RefAttributes<RefTriggerProps>>;
|
|
29
30
|
export default RefSelectTrigger;
|
package/es/SelectTrigger.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
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
2
|
import Trigger from '@rc-component/trigger';
|
|
3
|
-
import
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
const getBuiltInPlacements = popupMatchSelectWidth => {
|
|
6
6
|
// Enable horizontal overflow auto-adjustment when a custom dropdown width is provided
|
|
@@ -65,6 +65,7 @@ const SelectTrigger = (props, ref) => {
|
|
|
65
65
|
empty,
|
|
66
66
|
onPopupVisibleChange,
|
|
67
67
|
onPopupMouseEnter,
|
|
68
|
+
onPopupMouseDown,
|
|
68
69
|
...restProps
|
|
69
70
|
} = props;
|
|
70
71
|
|
|
@@ -115,14 +116,15 @@ const SelectTrigger = (props, ref) => {
|
|
|
115
116
|
motionName: mergedTransitionName
|
|
116
117
|
},
|
|
117
118
|
popup: /*#__PURE__*/React.createElement("div", {
|
|
118
|
-
onMouseEnter: onPopupMouseEnter
|
|
119
|
+
onMouseEnter: onPopupMouseEnter,
|
|
120
|
+
onMouseDown: onPopupMouseDown
|
|
119
121
|
}, popupNode),
|
|
120
122
|
ref: triggerPopupRef,
|
|
121
123
|
stretch: stretch,
|
|
122
124
|
popupAlign: popupAlign,
|
|
123
125
|
popupVisible: visible,
|
|
124
126
|
getPopupContainer: getPopupContainer,
|
|
125
|
-
popupClassName:
|
|
127
|
+
popupClassName: clsx(popupClassName, {
|
|
126
128
|
[`${popupPrefixCls}-empty`]: empty
|
|
127
129
|
}),
|
|
128
130
|
popupStyle: mergedPopupStyle,
|
package/es/TransBtn.d.ts
CHANGED
|
@@ -9,5 +9,15 @@ export interface TransBtnProps {
|
|
|
9
9
|
onClick?: React.MouseEventHandler<HTMLSpanElement>;
|
|
10
10
|
children?: React.ReactNode;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Small wrapper for Select icons (clear/arrow/etc.).
|
|
14
|
+
* Prevents default mousedown to avoid blurring or caret moves, and
|
|
15
|
+
* renders a custom icon or a fallback icon span.
|
|
16
|
+
*
|
|
17
|
+
* DOM structure:
|
|
18
|
+
* <span className={className} ...>
|
|
19
|
+
* { icon || <span className={`${className}-icon`}>{children}</span> }
|
|
20
|
+
* </span>
|
|
21
|
+
*/
|
|
12
22
|
declare const TransBtn: React.FC<TransBtnProps>;
|
|
13
23
|
export default TransBtn;
|
package/es/TransBtn.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
/**
|
|
4
|
+
* Small wrapper for Select icons (clear/arrow/etc.).
|
|
5
|
+
* Prevents default mousedown to avoid blurring or caret moves, and
|
|
6
|
+
* renders a custom icon or a fallback icon span.
|
|
7
|
+
*
|
|
8
|
+
* DOM structure:
|
|
9
|
+
* <span className={className} ...>
|
|
10
|
+
* { icon || <span className={`${className}-icon`}>{children}</span> }
|
|
11
|
+
* </span>
|
|
12
|
+
*/
|
|
3
13
|
const TransBtn = props => {
|
|
4
14
|
const {
|
|
5
15
|
className,
|
|
@@ -26,7 +36,7 @@ const TransBtn = props => {
|
|
|
26
36
|
onClick: onClick,
|
|
27
37
|
"aria-hidden": true
|
|
28
38
|
}, icon !== undefined ? icon : /*#__PURE__*/React.createElement("span", {
|
|
29
|
-
className:
|
|
39
|
+
className: clsx(className.split(/\s+/).map(cls => `${cls}-icon`))
|
|
30
40
|
}, children));
|
|
31
41
|
};
|
|
32
42
|
export default TransBtn;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { DisplayValueType, Mode
|
|
2
|
-
import React from 'react';
|
|
3
|
-
export
|
|
4
|
-
clearIcon?: RenderNode;
|
|
5
|
-
}, clearIcon?: RenderNode, disabled?: boolean, mergedSearchValue?: string, mode?: Mode) => {
|
|
1
|
+
import type { DisplayValueType, Mode } from '../interface';
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
export interface AllowClearConfig {
|
|
6
4
|
allowClear: boolean;
|
|
7
|
-
clearIcon: React.
|
|
8
|
-
}
|
|
5
|
+
clearIcon: React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
export declare const useAllowClear: (prefixCls: string, displayValues: DisplayValueType[], allowClear?: boolean | {
|
|
8
|
+
clearIcon?: React.ReactNode;
|
|
9
|
+
}, clearIcon?: React.ReactNode, disabled?: boolean, mergedSearchValue?: string, mode?: Mode) => AllowClearConfig;
|
|
@@ -1,26 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
if (typeof allowClear === '
|
|
6
|
-
return
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
export const useAllowClear = (prefixCls, displayValues, allowClear, clearIcon, disabled = false, mergedSearchValue, mode) => {
|
|
3
|
+
// Convert boolean to object first
|
|
4
|
+
const allowClearConfig = useMemo(() => {
|
|
5
|
+
if (typeof allowClear === 'boolean') {
|
|
6
|
+
return {
|
|
7
|
+
allowClear
|
|
8
|
+
};
|
|
7
9
|
}
|
|
8
|
-
if (
|
|
9
|
-
return
|
|
10
|
+
if (allowClear && typeof allowClear === 'object') {
|
|
11
|
+
return allowClear;
|
|
10
12
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
onMouseDown: onClearMouseDown,
|
|
23
|
-
customizeIcon: mergedClearIcon
|
|
24
|
-
}, "\xD7")
|
|
25
|
-
};
|
|
13
|
+
return {
|
|
14
|
+
allowClear: false
|
|
15
|
+
};
|
|
16
|
+
}, [allowClear]);
|
|
17
|
+
return useMemo(() => {
|
|
18
|
+
const mergedAllowClear = !disabled && allowClearConfig.allowClear !== false && (displayValues.length || mergedSearchValue) && !(mode === 'combobox' && mergedSearchValue === '');
|
|
19
|
+
return {
|
|
20
|
+
allowClear: mergedAllowClear,
|
|
21
|
+
clearIcon: mergedAllowClear ? allowClearConfig.clearIcon || clearIcon || '×' : null
|
|
22
|
+
};
|
|
23
|
+
}, [allowClearConfig, clearIcon, disabled, displayValues.length, mergedSearchValue, mode]);
|
|
26
24
|
};
|
|
@@ -8,6 +8,7 @@ export interface BaseSelectContextProps extends BaseSelectProps {
|
|
|
8
8
|
triggerOpen: boolean;
|
|
9
9
|
multiple: boolean;
|
|
10
10
|
toggleOpen: (open?: boolean) => void;
|
|
11
|
+
role?: React.AriaRole;
|
|
11
12
|
}
|
|
12
13
|
export declare const BaseSelectContext: React.Context<BaseSelectContextProps>;
|
|
13
14
|
export default function useBaseProps(): BaseSelectContextProps;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { SelectInputRef, SelectInputProps } from '../SelectInput';
|
|
3
|
+
import type { BaseSelectProps } from '../BaseSelect';
|
|
4
|
+
export interface ComponentsConfig {
|
|
5
|
+
root?: React.ComponentType<any> | string | React.ReactElement;
|
|
6
|
+
input?: React.ComponentType<any> | string | React.ReactElement;
|
|
7
|
+
}
|
|
8
|
+
export interface FilledComponentsConfig {
|
|
9
|
+
root: React.ForwardRefExoticComponent<SelectInputProps & React.RefAttributes<SelectInputRef>>;
|
|
10
|
+
input: React.ForwardRefExoticComponent<React.TextareaHTMLAttributes<HTMLTextAreaElement> | (React.InputHTMLAttributes<HTMLInputElement> & React.RefAttributes<HTMLInputElement | HTMLTextAreaElement>)>;
|
|
11
|
+
}
|
|
12
|
+
export default function useComponents(components?: ComponentsConfig, getInputElement?: BaseSelectProps['getInputElement'], getRawInputElement?: BaseSelectProps['getRawInputElement']): ComponentsConfig;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export default function useComponents(components, getInputElement, getRawInputElement) {
|
|
3
|
+
return React.useMemo(() => {
|
|
4
|
+
let {
|
|
5
|
+
root,
|
|
6
|
+
input
|
|
7
|
+
} = components || {};
|
|
8
|
+
|
|
9
|
+
// root: getRawInputElement
|
|
10
|
+
if (getRawInputElement) {
|
|
11
|
+
root = getRawInputElement();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// input: getInputElement
|
|
15
|
+
if (getInputElement) {
|
|
16
|
+
input = getInputElement();
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
root,
|
|
20
|
+
input
|
|
21
|
+
};
|
|
22
|
+
}, [components, getInputElement, getRawInputElement]);
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trigger by latest open call, if nextOpen is undefined, means toggle.
|
|
3
|
+
* ignoreNext will skip next call in the macro task queue.
|
|
4
|
+
*/
|
|
5
|
+
export type TriggerOpenType = (nextOpen?: boolean, ignoreNext?: boolean) => void;
|
|
6
|
+
/**
|
|
7
|
+
* When `open` is controlled, follow the controlled value;
|
|
8
|
+
* Otherwise use uncontrolled logic.
|
|
9
|
+
* Setting `open` takes effect immediately,
|
|
10
|
+
* but setting it to `false` is delayed via MessageChannel.
|
|
11
|
+
*
|
|
12
|
+
* SSR handling: During SSR, `open` is always false to avoid Portal issues.
|
|
13
|
+
* On client-side hydration, it syncs with the actual open state.
|
|
14
|
+
*/
|
|
15
|
+
export default function useOpen(propOpen: boolean, onOpen: (nextOpen: boolean) => void, postOpen: (nextOpen: boolean) => boolean): [boolean, TriggerOpenType];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { useControlledState, useEvent } from '@rc-component/util';
|
|
2
|
+
import { useRef, useState, useEffect } from 'react';
|
|
3
|
+
const internalMacroTask = fn => {
|
|
4
|
+
const channel = new MessageChannel();
|
|
5
|
+
channel.port1.onmessage = fn;
|
|
6
|
+
channel.port2.postMessage(null);
|
|
7
|
+
};
|
|
8
|
+
const macroTask = (fn, times = 1) => {
|
|
9
|
+
if (times <= 0) {
|
|
10
|
+
fn();
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
internalMacroTask(() => {
|
|
14
|
+
macroTask(fn, times - 1);
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Trigger by latest open call, if nextOpen is undefined, means toggle.
|
|
20
|
+
* ignoreNext will skip next call in the macro task queue.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* When `open` is controlled, follow the controlled value;
|
|
25
|
+
* Otherwise use uncontrolled logic.
|
|
26
|
+
* Setting `open` takes effect immediately,
|
|
27
|
+
* but setting it to `false` is delayed via MessageChannel.
|
|
28
|
+
*
|
|
29
|
+
* SSR handling: During SSR, `open` is always false to avoid Portal issues.
|
|
30
|
+
* On client-side hydration, it syncs with the actual open state.
|
|
31
|
+
*/
|
|
32
|
+
export default function useOpen(propOpen, onOpen, postOpen) {
|
|
33
|
+
// SSR not support Portal which means we need delay `open` for the first time render
|
|
34
|
+
const [rendered, setRendered] = useState(false);
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
setRendered(true);
|
|
37
|
+
}, []);
|
|
38
|
+
const [stateOpen, internalSetOpen] = useControlledState(false, propOpen);
|
|
39
|
+
|
|
40
|
+
// During SSR, always return false for open state
|
|
41
|
+
const ssrSafeOpen = rendered ? stateOpen : false;
|
|
42
|
+
const mergedOpen = postOpen(ssrSafeOpen);
|
|
43
|
+
const taskIdRef = useRef(0);
|
|
44
|
+
const taskLockRef = useRef(false);
|
|
45
|
+
const triggerEvent = useEvent(nextOpen => {
|
|
46
|
+
if (onOpen && mergedOpen !== nextOpen) {
|
|
47
|
+
onOpen(nextOpen);
|
|
48
|
+
}
|
|
49
|
+
internalSetOpen(nextOpen);
|
|
50
|
+
});
|
|
51
|
+
const toggleOpen = useEvent((nextOpen, ignoreNext = false) => {
|
|
52
|
+
taskIdRef.current += 1;
|
|
53
|
+
const id = taskIdRef.current;
|
|
54
|
+
const nextOpenVal = typeof nextOpen === 'boolean' ? nextOpen : !mergedOpen;
|
|
55
|
+
|
|
56
|
+
// Since `mergedOpen` is post-processed, we need to check if the value really changed
|
|
57
|
+
if (nextOpenVal) {
|
|
58
|
+
triggerEvent(true);
|
|
59
|
+
|
|
60
|
+
// Lock if needed
|
|
61
|
+
if (ignoreNext) {
|
|
62
|
+
taskLockRef.current = ignoreNext;
|
|
63
|
+
macroTask(() => {
|
|
64
|
+
taskLockRef.current = false;
|
|
65
|
+
}, 2);
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
macroTask(() => {
|
|
70
|
+
if (id === taskIdRef.current && !taskLockRef.current) {
|
|
71
|
+
triggerEvent(false);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
return [mergedOpen, toggleOpen];
|
|
76
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { SearchConfig, DefaultOptionType } from
|
|
2
|
-
export default function useSearchConfig(showSearch: boolean | SearchConfig<DefaultOptionType> | undefined, props: SearchConfig<DefaultOptionType>): [boolean, SearchConfig<DefaultOptionType>];
|
|
1
|
+
import type { SearchConfig, DefaultOptionType, SelectProps } from '../Select';
|
|
2
|
+
export default function useSearchConfig(showSearch: boolean | SearchConfig<DefaultOptionType> | undefined, props: SearchConfig<DefaultOptionType>, mode: SelectProps<DefaultOptionType>['mode']): [boolean, SearchConfig<DefaultOptionType>];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
// Convert `showSearch` to unique config
|
|
4
|
-
export default function useSearchConfig(showSearch, props) {
|
|
4
|
+
export default function useSearchConfig(showSearch, props, mode) {
|
|
5
5
|
const {
|
|
6
6
|
filterOption,
|
|
7
7
|
searchValue,
|
|
@@ -21,6 +21,6 @@ export default function useSearchConfig(showSearch, props) {
|
|
|
21
21
|
autoClearSearchValue,
|
|
22
22
|
...(isObject ? showSearch : {})
|
|
23
23
|
};
|
|
24
|
-
return [isObject ? true : showSearch, searchConfig];
|
|
25
|
-
}, [showSearch, filterOption, searchValue, optionFilterProp, filterSort, onSearch, autoClearSearchValue]);
|
|
24
|
+
return [isObject || mode === 'combobox' || mode === 'tags' || mode === 'multiple' && showSearch === undefined ? true : showSearch, searchConfig];
|
|
25
|
+
}, [mode, showSearch, filterOption, searchValue, optionFilterProp, filterSort, onSearch, autoClearSearchValue]);
|
|
26
26
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function useSelectTriggerControl(elements: () => (HTMLElement | undefined)[], open: boolean, triggerOpen: (open: boolean) => void, customizedTrigger: boolean): void;
|
|
1
|
+
export default function useSelectTriggerControl(elements: () => (HTMLElement | SVGElement | undefined)[], open: boolean, triggerOpen: (open: boolean) => void, customizedTrigger: boolean): void;
|
|
@@ -1,27 +1,22 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { useEvent } from '@rc-component/util';
|
|
2
3
|
export default function useSelectTriggerControl(elements, open, triggerOpen, customizedTrigger) {
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
}
|
|
4
|
+
const onGlobalMouseDown = useEvent(event => {
|
|
5
|
+
// If trigger is customized, Trigger will take control of popupVisible
|
|
6
|
+
if (customizedTrigger) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
let target = event.target;
|
|
10
|
+
if (target.shadowRoot && event.composed) {
|
|
11
|
+
target = event.composedPath()[0] || target;
|
|
23
12
|
}
|
|
13
|
+
if (open && elements().filter(element => element).every(element => !element.contains(target) && element !== target)) {
|
|
14
|
+
// Should trigger close
|
|
15
|
+
triggerOpen(false);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
React.useEffect(() => {
|
|
24
19
|
window.addEventListener('mousedown', onGlobalMouseDown);
|
|
25
20
|
return () => window.removeEventListener('mousedown', onGlobalMouseDown);
|
|
26
|
-
}, []);
|
|
21
|
+
}, [onGlobalMouseDown]);
|
|
27
22
|
}
|
package/es/utils/keyUtil.js
CHANGED
|
@@ -10,6 +10,10 @@ export function isValidateOpenKey(currentKeyCode) {
|
|
|
10
10
|
![
|
|
11
11
|
// System function button
|
|
12
12
|
KeyCode.ESC, KeyCode.SHIFT, KeyCode.BACKSPACE, KeyCode.TAB, KeyCode.WIN_KEY, KeyCode.ALT, KeyCode.META, KeyCode.WIN_KEY_RIGHT, KeyCode.CTRL, KeyCode.SEMICOLON, KeyCode.EQUALS, KeyCode.CAPS_LOCK, KeyCode.CONTEXT_MENU,
|
|
13
|
+
// Arrow keys - should not trigger open when navigating in input
|
|
14
|
+
KeyCode.UP,
|
|
15
|
+
// KeyCode.DOWN,
|
|
16
|
+
KeyCode.LEFT, KeyCode.RIGHT,
|
|
13
17
|
// F1-F12
|
|
14
18
|
KeyCode.F1, KeyCode.F2, KeyCode.F3, KeyCode.F4, KeyCode.F5, KeyCode.F6, KeyCode.F7, KeyCode.F8, KeyCode.F9, KeyCode.F10, KeyCode.F11, KeyCode.F12].includes(currentKeyCode)
|
|
15
19
|
);
|
|
@@ -2,7 +2,15 @@ import type { AlignType, BuildInPlacements } from '@rc-component/trigger/lib/int
|
|
|
2
2
|
import type { ScrollConfig, ScrollTo } from 'rc-virtual-list/lib/List';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import type { DisplayInfoType, DisplayValueType, Mode, Placement, RawValueType, RenderDOMFunc, RenderNode } from '../interface';
|
|
5
|
-
|
|
5
|
+
import type { ComponentsConfig } from '../hooks/useComponents';
|
|
6
|
+
export type BaseSelectSemanticName = 'prefix' | 'suffix' | 'input' | 'clear';
|
|
7
|
+
/**
|
|
8
|
+
* ZombieJ:
|
|
9
|
+
* We are currently refactoring the semantic structure of the component. Changelog:
|
|
10
|
+
* - Remove `suffixIcon` and change to `suffix`.
|
|
11
|
+
* - Add `components.root` for replacing response element.
|
|
12
|
+
* - Remove `getInputElement` and `getRawInputElement` since we can use `components.input` instead.
|
|
13
|
+
*/
|
|
6
14
|
export type { DisplayInfoType, DisplayValueType, Mode, Placement, RenderDOMFunc, RenderNode, RawValueType, };
|
|
7
15
|
export interface RefOptionListProps {
|
|
8
16
|
onKeyDown: React.KeyboardEventHandler;
|
|
@@ -84,15 +92,17 @@ export interface BaseSelectProps extends BaseSelectPrivateProps, React.AriaAttri
|
|
|
84
92
|
maxTagPlaceholder?: React.ReactNode | ((omittedValues: DisplayValueType[]) => React.ReactNode);
|
|
85
93
|
tokenSeparators?: string[];
|
|
86
94
|
allowClear?: boolean | {
|
|
87
|
-
clearIcon?:
|
|
95
|
+
clearIcon?: React.ReactNode;
|
|
88
96
|
};
|
|
89
97
|
prefix?: React.ReactNode;
|
|
98
|
+
/** @deprecated Please use `suffix` instead. */
|
|
90
99
|
suffixIcon?: RenderNode;
|
|
100
|
+
suffix?: RenderNode;
|
|
91
101
|
/**
|
|
92
102
|
* Clear all icon
|
|
93
103
|
* @deprecated Please use `allowClear` instead
|
|
94
104
|
**/
|
|
95
|
-
clearIcon?:
|
|
105
|
+
clearIcon?: React.ReactNode;
|
|
96
106
|
/** Selector remove icon */
|
|
97
107
|
removeIcon?: RenderNode;
|
|
98
108
|
animation?: string;
|
|
@@ -116,6 +126,7 @@ export interface BaseSelectProps extends BaseSelectPrivateProps, React.AriaAttri
|
|
|
116
126
|
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
|
|
117
127
|
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
|
|
118
128
|
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
|
129
|
+
components?: ComponentsConfig;
|
|
119
130
|
}
|
|
120
131
|
export declare const isMultiple: (mode: Mode) => boolean;
|
|
121
132
|
declare const BaseSelect: React.ForwardRefExoticComponent<BaseSelectProps & React.RefAttributes<BaseSelectRef>>;
|