@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.
Files changed (99) hide show
  1. package/assets/index.css +63 -0
  2. package/assets/index.less +1 -0
  3. package/assets/patch.less +83 -0
  4. package/es/BaseSelect/index.d.ts +14 -3
  5. package/es/BaseSelect/index.js +137 -200
  6. package/es/OptionList.js +3 -3
  7. package/es/Select.d.ts +1 -1
  8. package/es/Select.js +5 -9
  9. package/es/SelectInput/Affix.d.ts +5 -0
  10. package/es/SelectInput/Affix.js +12 -0
  11. package/es/SelectInput/Content/MultipleContent.d.ts +4 -0
  12. package/es/SelectInput/Content/MultipleContent.js +152 -0
  13. package/es/SelectInput/Content/Placeholder.d.ts +5 -0
  14. package/es/SelectInput/Content/Placeholder.js +21 -0
  15. package/es/SelectInput/Content/SingleContent.d.ts +4 -0
  16. package/es/SelectInput/Content/SingleContent.js +98 -0
  17. package/es/SelectInput/Content/index.d.ts +6 -0
  18. package/es/SelectInput/Content/index.js +37 -0
  19. package/es/SelectInput/Input.d.ts +20 -0
  20. package/es/SelectInput/Input.js +214 -0
  21. package/es/SelectInput/context.d.ts +6 -0
  22. package/es/SelectInput/context.js +6 -0
  23. package/es/SelectInput/index.d.ts +39 -0
  24. package/es/SelectInput/index.js +189 -0
  25. package/es/SelectTrigger.d.ts +1 -0
  26. package/es/SelectTrigger.js +5 -3
  27. package/es/TransBtn.d.ts +10 -0
  28. package/es/TransBtn.js +12 -2
  29. package/es/hooks/useAllowClear.d.ts +8 -7
  30. package/es/hooks/useAllowClear.js +21 -23
  31. package/es/hooks/useBaseProps.d.ts +1 -0
  32. package/es/hooks/useComponents.d.ts +12 -0
  33. package/es/hooks/useComponents.js +23 -0
  34. package/es/hooks/useOpen.d.ts +15 -0
  35. package/es/hooks/useOpen.js +76 -0
  36. package/es/hooks/useSearchConfig.d.ts +2 -2
  37. package/es/hooks/useSearchConfig.js +3 -3
  38. package/es/hooks/useSelectTriggerControl.d.ts +1 -1
  39. package/es/hooks/useSelectTriggerControl.js +16 -21
  40. package/es/utils/keyUtil.js +4 -0
  41. package/lib/BaseSelect/index.d.ts +14 -3
  42. package/lib/BaseSelect/index.js +137 -201
  43. package/lib/OptionList.js +3 -3
  44. package/lib/Select.d.ts +1 -1
  45. package/lib/Select.js +5 -9
  46. package/lib/SelectInput/Affix.d.ts +5 -0
  47. package/lib/{hooks/useLayoutEffect.js → SelectInput/Affix.js} +11 -16
  48. package/lib/SelectInput/Content/MultipleContent.d.ts +4 -0
  49. package/lib/{Selector/MultipleSelector.js → SelectInput/Content/MultipleContent.js} +71 -104
  50. package/lib/SelectInput/Content/Placeholder.d.ts +5 -0
  51. package/lib/SelectInput/Content/Placeholder.js +29 -0
  52. package/lib/SelectInput/Content/SingleContent.d.ts +4 -0
  53. package/lib/SelectInput/Content/SingleContent.js +107 -0
  54. package/lib/SelectInput/Content/index.d.ts +6 -0
  55. package/lib/SelectInput/Content/index.js +46 -0
  56. package/lib/SelectInput/Input.d.ts +20 -0
  57. package/lib/SelectInput/Input.js +223 -0
  58. package/lib/SelectInput/context.d.ts +6 -0
  59. package/lib/SelectInput/context.js +15 -0
  60. package/lib/SelectInput/index.d.ts +39 -0
  61. package/lib/SelectInput/index.js +198 -0
  62. package/lib/SelectTrigger.d.ts +1 -0
  63. package/lib/SelectTrigger.js +5 -3
  64. package/lib/TransBtn.d.ts +10 -0
  65. package/lib/TransBtn.js +12 -3
  66. package/lib/hooks/useAllowClear.d.ts +8 -7
  67. package/lib/hooks/useAllowClear.js +21 -24
  68. package/lib/hooks/useBaseProps.d.ts +1 -0
  69. package/lib/hooks/useComponents.d.ts +12 -0
  70. package/lib/hooks/{useDelayReset.js → useComponents.js} +21 -30
  71. package/lib/hooks/useOpen.d.ts +15 -0
  72. package/lib/hooks/useOpen.js +82 -0
  73. package/lib/hooks/useSearchConfig.d.ts +2 -2
  74. package/lib/hooks/useSearchConfig.js +3 -3
  75. package/lib/hooks/useSelectTriggerControl.d.ts +1 -1
  76. package/lib/hooks/useSelectTriggerControl.js +16 -21
  77. package/lib/utils/keyUtil.js +4 -0
  78. package/package.json +5 -4
  79. package/es/Selector/Input.d.ts +0 -27
  80. package/es/Selector/Input.js +0 -61
  81. package/es/Selector/MultipleSelector.d.ts +0 -16
  82. package/es/Selector/MultipleSelector.js +0 -185
  83. package/es/Selector/SingleSelector.d.ts +0 -8
  84. package/es/Selector/SingleSelector.js +0 -104
  85. package/es/Selector/index.d.ts +0 -83
  86. package/es/Selector/index.js +0 -189
  87. package/es/hooks/useDelayReset.d.ts +0 -5
  88. package/es/hooks/useDelayReset.js +0 -33
  89. package/es/hooks/useLayoutEffect.d.ts +0 -5
  90. package/es/hooks/useLayoutEffect.js +0 -17
  91. package/lib/Selector/Input.d.ts +0 -27
  92. package/lib/Selector/Input.js +0 -70
  93. package/lib/Selector/MultipleSelector.d.ts +0 -16
  94. package/lib/Selector/SingleSelector.d.ts +0 -8
  95. package/lib/Selector/SingleSelector.js +0 -113
  96. package/lib/Selector/index.d.ts +0 -83
  97. package/lib/Selector/index.js +0 -196
  98. package/lib/hooks/useDelayReset.d.ts +0 -5
  99. 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
+ });
@@ -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;
@@ -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 classNames from 'classnames';
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: classNames(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 classNames from 'classnames';
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: classNames(className.split(/\s+/).map(cls => `${cls}-icon`))
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, 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) => {
1
+ import type { DisplayValueType, Mode } from '../interface';
2
+ import type React from 'react';
3
+ export interface AllowClearConfig {
6
4
  allowClear: boolean;
7
- clearIcon: React.JSX.Element;
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 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;
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 (clearIcon) {
9
- return clearIcon;
10
+ if (allowClear && typeof allowClear === 'object') {
11
+ return allowClear;
10
12
  }
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
- };
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 "../Select";
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 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
- }
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
  }
@@ -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
- export type BaseSelectSemanticName = 'prefix' | 'suffix' | 'input';
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?: RenderNode;
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?: RenderNode;
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>>;