@rc-component/select 1.0.3 → 1.0.5

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.
@@ -4,6 +4,7 @@ import useLayoutEffect from "@rc-component/util/es/hooks/useLayoutEffect";
4
4
  import useMergedState from "@rc-component/util/es/hooks/useMergedState";
5
5
  import isMobile from "@rc-component/util/es/isMobile";
6
6
  import { useComposeRef } from "@rc-component/util/es/ref";
7
+ import { getDOM } from "@rc-component/util/es/Dom/findDOMNode";
7
8
  import * as React from 'react';
8
9
  import { useAllowClear } from "../hooks/useAllowClear";
9
10
  import { BaseSelectContext } from "../hooks/useBaseProps";
@@ -109,11 +110,11 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
109
110
 
110
111
  // ============================== Refs ==============================
111
112
  const containerRef = React.useRef(null);
112
- const selectorDomRef = React.useRef(null);
113
113
  const triggerRef = React.useRef(null);
114
114
  const selectorRef = React.useRef(null);
115
115
  const listRef = React.useRef(null);
116
116
  const blurRef = React.useRef(false);
117
+ const customDomRef = React.useRef(null);
117
118
 
118
119
  /** Used for component focused management */
119
120
  const [mockFocused, setMockFocused, cancelSetMockFocused] = useDelayReset();
@@ -123,7 +124,7 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
123
124
  focus: selectorRef.current?.focus,
124
125
  blur: selectorRef.current?.blur,
125
126
  scrollTo: arg => listRef.current?.scrollTo(arg),
126
- nativeElement: containerRef.current || selectorDomRef.current
127
+ nativeElement: containerRef.current || selectorRef.current?.nativeElement || getDOM(customDomRef.current)
127
128
  }));
128
129
 
129
130
  // ========================== Search Value ==========================
@@ -141,7 +142,7 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
141
142
 
142
143
  // Used for customize replacement for `rc-cascader`
143
144
  const customizeRawInputElement = typeof getRawInputElement === 'function' && getRawInputElement();
144
- const customizeRawInputRef = useComposeRef(selectorDomRef, customizeRawInputElement?.props?.ref);
145
+ const customizeRawInputRef = useComposeRef(customDomRef, customizeRawInputElement?.props?.ref);
145
146
 
146
147
  // ============================== Open ==============================
147
148
  // SSR not support Portal which means we need delay `open` for the first time render
@@ -504,11 +505,6 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
504
505
  builtinPlacements: builtinPlacements,
505
506
  getPopupContainer: getPopupContainer,
506
507
  empty: emptyOptions,
507
- getTriggerDOMNode: node =>
508
- // TODO: This is workaround and should be removed in `rc-select`
509
- // And use new standard `nativeElement` for ref.
510
- // But we should update `rc-resize-observer` first.
511
- selectorDomRef.current || node,
512
508
  onPopupVisibleChange: onTriggerVisibleChange,
513
509
  onPopupMouseEnter: onPopupMouseEnter
514
510
  }, customizeRawInputElement ? ( /*#__PURE__*/React.cloneElement(customizeRawInputElement, {
@@ -516,7 +512,6 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
516
512
  })) : /*#__PURE__*/React.createElement(Selector, _extends({}, props, {
517
513
  prefixClassName: classNames?.prefix,
518
514
  prefixStyle: styles?.prefix,
519
- domRef: selectorDomRef,
520
515
  prefixCls: prefixCls,
521
516
  inputElement: customizeInputElement,
522
517
  ref: selectorRef,
package/es/OptionList.js CHANGED
@@ -127,18 +127,20 @@ const OptionList = (_, ref) => {
127
127
  * `setActive` function will call root accessibility state update which makes re-render.
128
128
  * So we need to delay to let Input component trigger onChange first.
129
129
  */
130
- const timeoutId = setTimeout(() => {
131
- if (!multiple && open && rawValues.size === 1) {
132
- const value = Array.from(rawValues)[0];
133
- const index = memoFlattenOptions.findIndex(({
134
- data
135
- }) => data.value === value);
136
- if (index !== -1) {
137
- setActive(index);
130
+ let timeoutId;
131
+ if (!multiple && open && rawValues.size === 1) {
132
+ const value = Array.from(rawValues)[0];
133
+ // Scroll to the option closest to the searchValue if searching.
134
+ const index = memoFlattenOptions.findIndex(({
135
+ data
136
+ }) => searchValue ? String(data.value).startsWith(searchValue) : data.value === value);
137
+ if (index !== -1) {
138
+ setActive(index);
139
+ timeoutId = setTimeout(() => {
138
140
  scrollIntoView(index);
139
- }
141
+ });
140
142
  }
141
- });
143
+ }
142
144
 
143
145
  // Force trigger scrollbar visible when open
144
146
  if (open) {
package/es/Select.d.ts CHANGED
@@ -77,6 +77,7 @@ export interface SelectProps<ValueType = any, OptionType extends BaseOptionType
77
77
  autoClearSearchValue?: boolean;
78
78
  onSelect?: SelectHandler<ArrayElementType<ValueType>, OptionType>;
79
79
  onDeselect?: SelectHandler<ArrayElementType<ValueType>, OptionType>;
80
+ onActive?: (value: ValueType) => void;
80
81
  /**
81
82
  * In Select, `false` means do nothing.
82
83
  * In TreeSelect, `false` will highlight match item.
@@ -109,9 +110,7 @@ export interface SelectProps<ValueType = any, OptionType extends BaseOptionType
109
110
  classNames?: Partial<Record<SemanticName, string>>;
110
111
  styles?: Partial<Record<SemanticName, React.CSSProperties>>;
111
112
  }
112
- declare const TypedSelect: (<ValueType = any, OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType>(props: SelectProps<ValueType, OptionType> & {
113
- children?: React.ReactNode;
114
- } & React.RefAttributes<BaseSelectRef>) => React.ReactElement) & {
113
+ declare const TypedSelect: (<ValueType = any, OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType>(props: React.PropsWithChildren<SelectProps<ValueType, OptionType>> & React.RefAttributes<BaseSelectRef>) => React.ReactElement) & {
115
114
  Option: typeof Option;
116
115
  OptGroup: typeof OptGroup;
117
116
  };
package/es/Select.js CHANGED
@@ -64,6 +64,7 @@ const Select = /*#__PURE__*/React.forwardRef((props, ref) => {
64
64
  // Select
65
65
  onSelect,
66
66
  onDeselect,
67
+ onActive,
67
68
  popupMatchSelectWidth = true,
68
69
  // Options
69
70
  filterOption,
@@ -305,6 +306,7 @@ const Select = /*#__PURE__*/React.forwardRef((props, ref) => {
305
306
  const [activeValue, setActiveValue] = React.useState(null);
306
307
  const [accessibilityIndex, setAccessibilityIndex] = React.useState(0);
307
308
  const mergedDefaultActiveFirstOption = defaultActiveFirstOption !== undefined ? defaultActiveFirstOption : mode !== 'combobox';
309
+ const activeEventRef = React.useRef();
308
310
  const onActiveValue = React.useCallback((active, index, {
309
311
  source = 'keyboard'
310
312
  } = {}) => {
@@ -312,7 +314,16 @@ const Select = /*#__PURE__*/React.forwardRef((props, ref) => {
312
314
  if (backfill && mode === 'combobox' && active !== null && source === 'keyboard') {
313
315
  setActiveValue(String(active));
314
316
  }
315
- }, [backfill, mode]);
317
+
318
+ // Active will call multiple times.
319
+ // We only need trigger the last one.
320
+ const promise = Promise.resolve().then(() => {
321
+ if (activeEventRef.current === promise) {
322
+ onActive?.(active);
323
+ }
324
+ });
325
+ activeEventRef.current = promise;
326
+ }, [backfill, mode, onActive]);
316
327
 
317
328
  // ========================= OptionList =========================
318
329
  const triggerSelect = (val, selected, type) => {
@@ -22,7 +22,6 @@ export interface SelectTriggerProps {
22
22
  getPopupContainer?: RenderDOMFunc;
23
23
  popupAlign: AlignType;
24
24
  empty: boolean;
25
- getTriggerDOMNode: (node: HTMLElement) => HTMLElement;
26
25
  onPopupVisibleChange?: (visible: boolean) => void;
27
26
  onPopupMouseEnter: () => void;
28
27
  }
@@ -63,7 +63,6 @@ const SelectTrigger = (props, ref) => {
63
63
  popupAlign,
64
64
  getPopupContainer,
65
65
  empty,
66
- getTriggerDOMNode,
67
66
  onPopupVisibleChange,
68
67
  onPopupMouseEnter,
69
68
  ...restProps
@@ -127,7 +126,6 @@ const SelectTrigger = (props, ref) => {
127
126
  [`${popupPrefixCls}-empty`]: empty
128
127
  }),
129
128
  popupStyle: mergedPopupStyle,
130
- getTriggerDOMNode: getTriggerDOMNode,
131
129
  onPopupVisibleChange: onPopupVisibleChange
132
130
  }), children);
133
131
  };
@@ -40,6 +40,7 @@ export interface RefSelectorProps {
40
40
  focus: (options?: FocusOptions) => void;
41
41
  blur: () => void;
42
42
  scrollTo?: ScrollTo;
43
+ nativeElement: HTMLDivElement;
43
44
  }
44
45
  export interface SelectorProps {
45
46
  prefixClassName: string;
@@ -77,11 +78,6 @@ export interface SelectorProps {
77
78
  onRemove: (value: DisplayValueType) => void;
78
79
  onInputKeyDown?: React.KeyboardEventHandler<HTMLInputElement | HTMLTextAreaElement>;
79
80
  onInputBlur?: () => void;
80
- /**
81
- * @private get real dom for trigger align.
82
- * This may be removed after React provides replacement of `findDOMNode`
83
- */
84
- domRef: React.Ref<HTMLDivElement>;
85
81
  }
86
82
  declare const ForwardSelector: React.ForwardRefExoticComponent<SelectorProps & React.RefAttributes<RefSelectorProps>>;
87
83
  export default ForwardSelector;
@@ -35,18 +35,19 @@ const Selector = (props, ref) => {
35
35
  onSearchSubmit,
36
36
  onToggleOpen,
37
37
  onInputKeyDown,
38
- onInputBlur,
39
- domRef
38
+ onInputBlur
40
39
  } = props;
41
40
 
42
41
  // ======================= Ref =======================
42
+ const containerRef = React.useRef(null);
43
43
  React.useImperativeHandle(ref, () => ({
44
44
  focus: options => {
45
45
  inputRef.current.focus(options);
46
46
  },
47
47
  blur: () => {
48
48
  inputRef.current.blur();
49
- }
49
+ },
50
+ nativeElement: containerRef.current
50
51
  }));
51
52
 
52
53
  // ====================== Input ======================
@@ -172,7 +173,7 @@ const Selector = (props, ref) => {
172
173
  };
173
174
  const selectNode = mode === 'multiple' || mode === 'tags' ? /*#__PURE__*/React.createElement(MultipleSelector, _extends({}, props, sharedProps)) : /*#__PURE__*/React.createElement(SingleSelector, _extends({}, props, sharedProps));
174
175
  return /*#__PURE__*/React.createElement("div", {
175
- ref: domRef,
176
+ ref: containerRef,
176
177
  className: `${prefixCls}-selector`,
177
178
  onClick: onClick,
178
179
  onMouseDown: onMouseDown
@@ -9,6 +9,7 @@ var _useLayoutEffect = _interopRequireDefault(require("@rc-component/util/lib/ho
9
9
  var _useMergedState = _interopRequireDefault(require("@rc-component/util/lib/hooks/useMergedState"));
10
10
  var _isMobile = _interopRequireDefault(require("@rc-component/util/lib/isMobile"));
11
11
  var _ref = require("@rc-component/util/lib/ref");
12
+ var _findDOMNode = require("@rc-component/util/lib/Dom/findDOMNode");
12
13
  var React = _interopRequireWildcard(require("react"));
13
14
  var _useAllowClear = require("../hooks/useAllowClear");
14
15
  var _useBaseProps = require("../hooks/useBaseProps");
@@ -119,11 +120,11 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
119
120
 
120
121
  // ============================== Refs ==============================
121
122
  const containerRef = React.useRef(null);
122
- const selectorDomRef = React.useRef(null);
123
123
  const triggerRef = React.useRef(null);
124
124
  const selectorRef = React.useRef(null);
125
125
  const listRef = React.useRef(null);
126
126
  const blurRef = React.useRef(false);
127
+ const customDomRef = React.useRef(null);
127
128
 
128
129
  /** Used for component focused management */
129
130
  const [mockFocused, setMockFocused, cancelSetMockFocused] = (0, _useDelayReset.default)();
@@ -133,7 +134,7 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
133
134
  focus: selectorRef.current?.focus,
134
135
  blur: selectorRef.current?.blur,
135
136
  scrollTo: arg => listRef.current?.scrollTo(arg),
136
- nativeElement: containerRef.current || selectorDomRef.current
137
+ nativeElement: containerRef.current || selectorRef.current?.nativeElement || (0, _findDOMNode.getDOM)(customDomRef.current)
137
138
  }));
138
139
 
139
140
  // ========================== Search Value ==========================
@@ -151,7 +152,7 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
151
152
 
152
153
  // Used for customize replacement for `rc-cascader`
153
154
  const customizeRawInputElement = typeof getRawInputElement === 'function' && getRawInputElement();
154
- const customizeRawInputRef = (0, _ref.useComposeRef)(selectorDomRef, customizeRawInputElement?.props?.ref);
155
+ const customizeRawInputRef = (0, _ref.useComposeRef)(customDomRef, customizeRawInputElement?.props?.ref);
155
156
 
156
157
  // ============================== Open ==============================
157
158
  // SSR not support Portal which means we need delay `open` for the first time render
@@ -514,11 +515,6 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
514
515
  builtinPlacements: builtinPlacements,
515
516
  getPopupContainer: getPopupContainer,
516
517
  empty: emptyOptions,
517
- getTriggerDOMNode: node =>
518
- // TODO: This is workaround and should be removed in `rc-select`
519
- // And use new standard `nativeElement` for ref.
520
- // But we should update `rc-resize-observer` first.
521
- selectorDomRef.current || node,
522
518
  onPopupVisibleChange: onTriggerVisibleChange,
523
519
  onPopupMouseEnter: onPopupMouseEnter
524
520
  }, customizeRawInputElement ? ( /*#__PURE__*/React.cloneElement(customizeRawInputElement, {
@@ -526,7 +522,6 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
526
522
  })) : /*#__PURE__*/React.createElement(_Selector.default, _extends({}, props, {
527
523
  prefixClassName: classNames?.prefix,
528
524
  prefixStyle: styles?.prefix,
529
- domRef: selectorDomRef,
530
525
  prefixCls: prefixCls,
531
526
  inputElement: customizeInputElement,
532
527
  ref: selectorRef,
package/lib/OptionList.js CHANGED
@@ -135,18 +135,20 @@ const OptionList = (_, ref) => {
135
135
  * `setActive` function will call root accessibility state update which makes re-render.
136
136
  * So we need to delay to let Input component trigger onChange first.
137
137
  */
138
- const timeoutId = setTimeout(() => {
139
- if (!multiple && open && rawValues.size === 1) {
140
- const value = Array.from(rawValues)[0];
141
- const index = memoFlattenOptions.findIndex(({
142
- data
143
- }) => data.value === value);
144
- if (index !== -1) {
145
- setActive(index);
138
+ let timeoutId;
139
+ if (!multiple && open && rawValues.size === 1) {
140
+ const value = Array.from(rawValues)[0];
141
+ // Scroll to the option closest to the searchValue if searching.
142
+ const index = memoFlattenOptions.findIndex(({
143
+ data
144
+ }) => searchValue ? String(data.value).startsWith(searchValue) : data.value === value);
145
+ if (index !== -1) {
146
+ setActive(index);
147
+ timeoutId = setTimeout(() => {
146
148
  scrollIntoView(index);
147
- }
149
+ });
148
150
  }
149
- });
151
+ }
150
152
 
151
153
  // Force trigger scrollbar visible when open
152
154
  if (open) {
package/lib/Select.d.ts CHANGED
@@ -77,6 +77,7 @@ export interface SelectProps<ValueType = any, OptionType extends BaseOptionType
77
77
  autoClearSearchValue?: boolean;
78
78
  onSelect?: SelectHandler<ArrayElementType<ValueType>, OptionType>;
79
79
  onDeselect?: SelectHandler<ArrayElementType<ValueType>, OptionType>;
80
+ onActive?: (value: ValueType) => void;
80
81
  /**
81
82
  * In Select, `false` means do nothing.
82
83
  * In TreeSelect, `false` will highlight match item.
@@ -109,9 +110,7 @@ export interface SelectProps<ValueType = any, OptionType extends BaseOptionType
109
110
  classNames?: Partial<Record<SemanticName, string>>;
110
111
  styles?: Partial<Record<SemanticName, React.CSSProperties>>;
111
112
  }
112
- declare const TypedSelect: (<ValueType = any, OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType>(props: SelectProps<ValueType, OptionType> & {
113
- children?: React.ReactNode;
114
- } & React.RefAttributes<BaseSelectRef>) => React.ReactElement) & {
113
+ declare const TypedSelect: (<ValueType = any, OptionType extends BaseOptionType | DefaultOptionType = DefaultOptionType>(props: React.PropsWithChildren<SelectProps<ValueType, OptionType>> & React.RefAttributes<BaseSelectRef>) => React.ReactElement) & {
115
114
  Option: typeof Option;
116
115
  OptGroup: typeof OptGroup;
117
116
  };
package/lib/Select.js CHANGED
@@ -71,6 +71,7 @@ const Select = /*#__PURE__*/React.forwardRef((props, ref) => {
71
71
  // Select
72
72
  onSelect,
73
73
  onDeselect,
74
+ onActive,
74
75
  popupMatchSelectWidth = true,
75
76
  // Options
76
77
  filterOption,
@@ -312,6 +313,7 @@ const Select = /*#__PURE__*/React.forwardRef((props, ref) => {
312
313
  const [activeValue, setActiveValue] = React.useState(null);
313
314
  const [accessibilityIndex, setAccessibilityIndex] = React.useState(0);
314
315
  const mergedDefaultActiveFirstOption = defaultActiveFirstOption !== undefined ? defaultActiveFirstOption : mode !== 'combobox';
316
+ const activeEventRef = React.useRef();
315
317
  const onActiveValue = React.useCallback((active, index, {
316
318
  source = 'keyboard'
317
319
  } = {}) => {
@@ -319,7 +321,16 @@ const Select = /*#__PURE__*/React.forwardRef((props, ref) => {
319
321
  if (backfill && mode === 'combobox' && active !== null && source === 'keyboard') {
320
322
  setActiveValue(String(active));
321
323
  }
322
- }, [backfill, mode]);
324
+
325
+ // Active will call multiple times.
326
+ // We only need trigger the last one.
327
+ const promise = Promise.resolve().then(() => {
328
+ if (activeEventRef.current === promise) {
329
+ onActive?.(active);
330
+ }
331
+ });
332
+ activeEventRef.current = promise;
333
+ }, [backfill, mode, onActive]);
323
334
 
324
335
  // ========================= OptionList =========================
325
336
  const triggerSelect = (val, selected, type) => {
@@ -22,7 +22,6 @@ export interface SelectTriggerProps {
22
22
  getPopupContainer?: RenderDOMFunc;
23
23
  popupAlign: AlignType;
24
24
  empty: boolean;
25
- getTriggerDOMNode: (node: HTMLElement) => HTMLElement;
26
25
  onPopupVisibleChange?: (visible: boolean) => void;
27
26
  onPopupMouseEnter: () => void;
28
27
  }
@@ -72,7 +72,6 @@ const SelectTrigger = (props, ref) => {
72
72
  popupAlign,
73
73
  getPopupContainer,
74
74
  empty,
75
- getTriggerDOMNode,
76
75
  onPopupVisibleChange,
77
76
  onPopupMouseEnter,
78
77
  ...restProps
@@ -136,7 +135,6 @@ const SelectTrigger = (props, ref) => {
136
135
  [`${popupPrefixCls}-empty`]: empty
137
136
  }),
138
137
  popupStyle: mergedPopupStyle,
139
- getTriggerDOMNode: getTriggerDOMNode,
140
138
  onPopupVisibleChange: onPopupVisibleChange
141
139
  }), children);
142
140
  };
@@ -40,6 +40,7 @@ export interface RefSelectorProps {
40
40
  focus: (options?: FocusOptions) => void;
41
41
  blur: () => void;
42
42
  scrollTo?: ScrollTo;
43
+ nativeElement: HTMLDivElement;
43
44
  }
44
45
  export interface SelectorProps {
45
46
  prefixClassName: string;
@@ -77,11 +78,6 @@ export interface SelectorProps {
77
78
  onRemove: (value: DisplayValueType) => void;
78
79
  onInputKeyDown?: React.KeyboardEventHandler<HTMLInputElement | HTMLTextAreaElement>;
79
80
  onInputBlur?: () => void;
80
- /**
81
- * @private get real dom for trigger align.
82
- * This may be removed after React provides replacement of `findDOMNode`
83
- */
84
- domRef: React.Ref<HTMLDivElement>;
85
81
  }
86
82
  declare const ForwardSelector: React.ForwardRefExoticComponent<SelectorProps & React.RefAttributes<RefSelectorProps>>;
87
83
  export default ForwardSelector;
@@ -42,18 +42,19 @@ const Selector = (props, ref) => {
42
42
  onSearchSubmit,
43
43
  onToggleOpen,
44
44
  onInputKeyDown,
45
- onInputBlur,
46
- domRef
45
+ onInputBlur
47
46
  } = props;
48
47
 
49
48
  // ======================= Ref =======================
49
+ const containerRef = React.useRef(null);
50
50
  React.useImperativeHandle(ref, () => ({
51
51
  focus: options => {
52
52
  inputRef.current.focus(options);
53
53
  },
54
54
  blur: () => {
55
55
  inputRef.current.blur();
56
- }
56
+ },
57
+ nativeElement: containerRef.current
57
58
  }));
58
59
 
59
60
  // ====================== Input ======================
@@ -179,7 +180,7 @@ const Selector = (props, ref) => {
179
180
  };
180
181
  const selectNode = mode === 'multiple' || mode === 'tags' ? /*#__PURE__*/React.createElement(_MultipleSelector.default, _extends({}, props, sharedProps)) : /*#__PURE__*/React.createElement(_SingleSelector.default, _extends({}, props, sharedProps));
181
182
  return /*#__PURE__*/React.createElement("div", {
182
- ref: domRef,
183
+ ref: containerRef,
183
184
  className: `${prefixCls}-selector`,
184
185
  onClick: onClick,
185
186
  onMouseDown: onMouseDown
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rc-component/select",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "React Select",
5
5
  "engines": {
6
6
  "node": ">=8.x"