@rc-component/select 1.3.2 → 1.3.4

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.
@@ -295,9 +295,7 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
295
295
  useSelectTriggerControl(getSelectElements, mergedOpen, triggerOpen, !!mergedComponents.root);
296
296
 
297
297
  // ========================== Focus / Blur ==========================
298
- /** Record real focus status */
299
- // const focusRef = React.useRef<boolean>(false);
300
-
298
+ const internalMouseDownRef = React.useRef(false);
301
299
  const onInternalFocus = event => {
302
300
  setFocused(true);
303
301
  if (!disabled) {
@@ -309,11 +307,12 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
309
307
  }
310
308
  };
311
309
  const onRootBlur = () => {
312
- macroTask(() => {
313
- if (!isInside(getSelectElements(), document.activeElement)) {
314
- triggerOpen(false);
315
- }
316
- });
310
+ // Delay close should check the activeElement
311
+ if (mergedOpen && !internalMouseDownRef.current) {
312
+ triggerOpen(false, {
313
+ cancelFun: () => isInside(getSelectElements(), document.activeElement)
314
+ });
315
+ }
317
316
  };
318
317
  const onInternalBlur = event => {
319
318
  setFocused(false);
@@ -335,7 +334,7 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
335
334
  onBlur?.(event);
336
335
  }
337
336
  };
338
- const onInternalMouseDown = (event, ...restArgs) => {
337
+ const onRootMouseDown = (event, ...restArgs) => {
339
338
  const {
340
339
  target
341
340
  } = event;
@@ -344,11 +343,13 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
344
343
  // We should give focus back to selector if clicked item is not focusable
345
344
  if (popupElement?.contains(target) && triggerOpen) {
346
345
  // Tell `open` not to close since it's safe in the popup
347
- triggerOpen(true, {
348
- ignoreNext: true
349
- });
346
+ triggerOpen(true);
350
347
  }
351
348
  onMouseDown?.(event, ...restArgs);
349
+ internalMouseDownRef.current = true;
350
+ macroTask(() => {
351
+ internalMouseDownRef.current = false;
352
+ });
352
353
  };
353
354
 
354
355
  // ============================ Dropdown ============================
@@ -474,7 +475,7 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
474
475
  tokenWithEnter: tokenWithEnter
475
476
  // Open
476
477
  ,
477
- onMouseDown: onInternalMouseDown
478
+ onMouseDown: onRootMouseDown
478
479
  // Components
479
480
  ,
480
481
  components: mergedComponents
@@ -499,7 +500,7 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
499
500
  empty: emptyOptions,
500
501
  onPopupVisibleChange: onTriggerVisibleChange,
501
502
  onPopupMouseEnter: onPopupMouseEnter,
502
- onPopupMouseDown: onInternalMouseDown,
503
+ onPopupMouseDown: onRootMouseDown,
503
504
  onPopupBlur: onRootBlur
504
505
  }, renderNode);
505
506
  return /*#__PURE__*/React.createElement(BaseSelectContext.Provider, {
@@ -105,12 +105,13 @@ export default /*#__PURE__*/React.forwardRef(function SelectInput(props, ref) {
105
105
  // ====================== Open ======================
106
106
  const onInternalMouseDown = useEvent(event => {
107
107
  if (!disabled) {
108
+ const inputDOM = getDOM(inputRef.current);
109
+
108
110
  // https://github.com/ant-design/ant-design/issues/56002
109
111
  // Tell `useSelectTriggerControl` to ignore this event
110
112
  // When icon is dynamic render, the parentNode will miss
111
113
  // so we need to mark the event directly
112
- event.nativeEvent._ignore_global_close = true;
113
- const inputDOM = getDOM(inputRef.current);
114
+ event.nativeEvent._ori_target = inputDOM;
114
115
  if (inputDOM && event.target !== inputDOM && !inputDOM.contains(event.target)) {
115
116
  event.preventDefault();
116
117
  }
@@ -1,11 +1,10 @@
1
1
  export declare const macroTask: (fn: VoidFunction, times?: number) => void;
2
2
  /**
3
3
  * Trigger by latest open call, if nextOpen is undefined, means toggle.
4
- * ignoreNext will skip next call in the macro task queue.
4
+ * `weak` means this call can be ignored if previous call exists.
5
5
  */
6
6
  export type TriggerOpenType = (nextOpen?: boolean, config?: {
7
- ignoreNext?: boolean;
8
- lazy?: boolean;
7
+ cancelFun?: () => boolean;
9
8
  }) => void;
10
9
  /**
11
10
  * When `open` is controlled, follow the controlled value;
@@ -17,7 +17,7 @@ export const macroTask = (fn, times = 1) => {
17
17
 
18
18
  /**
19
19
  * Trigger by latest open call, if nextOpen is undefined, means toggle.
20
- * ignoreNext will skip next call in the macro task queue.
20
+ * `weak` means this call can be ignored if previous call exists.
21
21
  */
22
22
 
23
23
  /**
@@ -41,7 +41,6 @@ export default function useOpen(propOpen, onOpen, postOpen) {
41
41
  const ssrSafeOpen = rendered ? stateOpen : false;
42
42
  const mergedOpen = postOpen(ssrSafeOpen);
43
43
  const taskIdRef = useRef(0);
44
- const taskLockRef = useRef(false);
45
44
  const triggerEvent = useEvent(nextOpen => {
46
45
  if (onOpen && mergedOpen !== nextOpen) {
47
46
  onOpen(nextOpen);
@@ -50,32 +49,29 @@ export default function useOpen(propOpen, onOpen, postOpen) {
50
49
  });
51
50
  const toggleOpen = useEvent((nextOpen, config = {}) => {
52
51
  const {
53
- ignoreNext = false
52
+ cancelFun
54
53
  } = config;
55
54
  taskIdRef.current += 1;
56
55
  const id = taskIdRef.current;
57
56
  const nextOpenVal = typeof nextOpen === 'boolean' ? nextOpen : !mergedOpen;
58
-
59
- // Since `mergedOpen` is post-processed, we need to check if the value really changed
60
- if (nextOpenVal) {
61
- if (!taskLockRef.current) {
57
+ function triggerUpdate() {
58
+ if (
59
+ // Always check if id is match
60
+ id === taskIdRef.current &&
61
+ // Check if need to cancel
62
+ !cancelFun?.()) {
62
63
  triggerEvent(nextOpenVal);
63
-
64
- // Lock if needed
65
- if (ignoreNext) {
66
- taskLockRef.current = ignoreNext;
67
- macroTask(() => {
68
- taskLockRef.current = false;
69
- }, 3);
70
- }
71
64
  }
72
- return;
73
65
  }
74
- macroTask(() => {
75
- if (id === taskIdRef.current && !taskLockRef.current) {
76
- triggerEvent(nextOpenVal);
77
- }
78
- });
66
+
67
+ // Weak update can be ignored
68
+ if (nextOpenVal) {
69
+ triggerUpdate();
70
+ } else {
71
+ macroTask(() => {
72
+ triggerUpdate();
73
+ });
74
+ }
79
75
  });
80
76
  return [mergedOpen, toggleOpen];
81
77
  }
@@ -1,2 +1,3 @@
1
+ import type { TriggerOpenType } from './useOpen';
1
2
  export declare function isInside(elements: (HTMLElement | SVGElement | undefined)[], target: HTMLElement): boolean;
2
- export default function useSelectTriggerControl(elements: () => (HTMLElement | SVGElement | undefined)[], open: boolean, triggerOpen: (open: boolean) => void, customizedTrigger: boolean): void;
3
+ export default function useSelectTriggerControl(elements: () => (HTMLElement | SVGElement | undefined)[], open: boolean, triggerOpen: TriggerOpenType, customizedTrigger: boolean): void;
@@ -13,9 +13,12 @@ export default function useSelectTriggerControl(elements, open, triggerOpen, cus
13
13
  if (target.shadowRoot && event.composed) {
14
14
  target = event.composedPath()[0] || target;
15
15
  }
16
+ if (event._ori_target) {
17
+ target = event._ori_target;
18
+ }
16
19
  if (open &&
17
20
  // Marked by SelectInput mouseDown event
18
- !event._ignore_global_close && !isInside(elements(), target)) {
21
+ !isInside(elements(), target)) {
19
22
  // Should trigger close
20
23
  triggerOpen(false);
21
24
  }
@@ -304,9 +304,7 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
304
304
  (0, _useSelectTriggerControl.default)(getSelectElements, mergedOpen, triggerOpen, !!mergedComponents.root);
305
305
 
306
306
  // ========================== Focus / Blur ==========================
307
- /** Record real focus status */
308
- // const focusRef = React.useRef<boolean>(false);
309
-
307
+ const internalMouseDownRef = React.useRef(false);
310
308
  const onInternalFocus = event => {
311
309
  setFocused(true);
312
310
  if (!disabled) {
@@ -318,11 +316,12 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
318
316
  }
319
317
  };
320
318
  const onRootBlur = () => {
321
- (0, _useOpen.macroTask)(() => {
322
- if (!(0, _useSelectTriggerControl.isInside)(getSelectElements(), document.activeElement)) {
323
- triggerOpen(false);
324
- }
325
- });
319
+ // Delay close should check the activeElement
320
+ if (mergedOpen && !internalMouseDownRef.current) {
321
+ triggerOpen(false, {
322
+ cancelFun: () => (0, _useSelectTriggerControl.isInside)(getSelectElements(), document.activeElement)
323
+ });
324
+ }
326
325
  };
327
326
  const onInternalBlur = event => {
328
327
  setFocused(false);
@@ -344,7 +343,7 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
344
343
  onBlur?.(event);
345
344
  }
346
345
  };
347
- const onInternalMouseDown = (event, ...restArgs) => {
346
+ const onRootMouseDown = (event, ...restArgs) => {
348
347
  const {
349
348
  target
350
349
  } = event;
@@ -353,11 +352,13 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
353
352
  // We should give focus back to selector if clicked item is not focusable
354
353
  if (popupElement?.contains(target) && triggerOpen) {
355
354
  // Tell `open` not to close since it's safe in the popup
356
- triggerOpen(true, {
357
- ignoreNext: true
358
- });
355
+ triggerOpen(true);
359
356
  }
360
357
  onMouseDown?.(event, ...restArgs);
358
+ internalMouseDownRef.current = true;
359
+ (0, _useOpen.macroTask)(() => {
360
+ internalMouseDownRef.current = false;
361
+ });
361
362
  };
362
363
 
363
364
  // ============================ Dropdown ============================
@@ -483,7 +484,7 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
483
484
  tokenWithEnter: tokenWithEnter
484
485
  // Open
485
486
  ,
486
- onMouseDown: onInternalMouseDown
487
+ onMouseDown: onRootMouseDown
487
488
  // Components
488
489
  ,
489
490
  components: mergedComponents
@@ -508,7 +509,7 @@ const BaseSelect = /*#__PURE__*/React.forwardRef((props, ref) => {
508
509
  empty: emptyOptions,
509
510
  onPopupVisibleChange: onTriggerVisibleChange,
510
511
  onPopupMouseEnter: onPopupMouseEnter,
511
- onPopupMouseDown: onInternalMouseDown,
512
+ onPopupMouseDown: onRootMouseDown,
512
513
  onPopupBlur: onRootBlur
513
514
  }, renderNode);
514
515
  return /*#__PURE__*/React.createElement(_useBaseProps.BaseSelectContext.Provider, {
@@ -114,12 +114,13 @@ var _default = exports.default = /*#__PURE__*/React.forwardRef(function SelectIn
114
114
  // ====================== Open ======================
115
115
  const onInternalMouseDown = (0, _util.useEvent)(event => {
116
116
  if (!disabled) {
117
+ const inputDOM = (0, _findDOMNode.getDOM)(inputRef.current);
118
+
117
119
  // https://github.com/ant-design/ant-design/issues/56002
118
120
  // Tell `useSelectTriggerControl` to ignore this event
119
121
  // When icon is dynamic render, the parentNode will miss
120
122
  // so we need to mark the event directly
121
- event.nativeEvent._ignore_global_close = true;
122
- const inputDOM = (0, _findDOMNode.getDOM)(inputRef.current);
123
+ event.nativeEvent._ori_target = inputDOM;
123
124
  if (inputDOM && event.target !== inputDOM && !inputDOM.contains(event.target)) {
124
125
  event.preventDefault();
125
126
  }
@@ -1,11 +1,10 @@
1
1
  export declare const macroTask: (fn: VoidFunction, times?: number) => void;
2
2
  /**
3
3
  * Trigger by latest open call, if nextOpen is undefined, means toggle.
4
- * ignoreNext will skip next call in the macro task queue.
4
+ * `weak` means this call can be ignored if previous call exists.
5
5
  */
6
6
  export type TriggerOpenType = (nextOpen?: boolean, config?: {
7
- ignoreNext?: boolean;
8
- lazy?: boolean;
7
+ cancelFun?: () => boolean;
9
8
  }) => void;
10
9
  /**
11
10
  * When `open` is controlled, follow the controlled value;
@@ -24,7 +24,7 @@ const macroTask = (fn, times = 1) => {
24
24
 
25
25
  /**
26
26
  * Trigger by latest open call, if nextOpen is undefined, means toggle.
27
- * ignoreNext will skip next call in the macro task queue.
27
+ * `weak` means this call can be ignored if previous call exists.
28
28
  */
29
29
  exports.macroTask = macroTask;
30
30
  /**
@@ -48,7 +48,6 @@ function useOpen(propOpen, onOpen, postOpen) {
48
48
  const ssrSafeOpen = rendered ? stateOpen : false;
49
49
  const mergedOpen = postOpen(ssrSafeOpen);
50
50
  const taskIdRef = (0, _react.useRef)(0);
51
- const taskLockRef = (0, _react.useRef)(false);
52
51
  const triggerEvent = (0, _util.useEvent)(nextOpen => {
53
52
  if (onOpen && mergedOpen !== nextOpen) {
54
53
  onOpen(nextOpen);
@@ -57,32 +56,29 @@ function useOpen(propOpen, onOpen, postOpen) {
57
56
  });
58
57
  const toggleOpen = (0, _util.useEvent)((nextOpen, config = {}) => {
59
58
  const {
60
- ignoreNext = false
59
+ cancelFun
61
60
  } = config;
62
61
  taskIdRef.current += 1;
63
62
  const id = taskIdRef.current;
64
63
  const nextOpenVal = typeof nextOpen === 'boolean' ? nextOpen : !mergedOpen;
65
-
66
- // Since `mergedOpen` is post-processed, we need to check if the value really changed
67
- if (nextOpenVal) {
68
- if (!taskLockRef.current) {
64
+ function triggerUpdate() {
65
+ if (
66
+ // Always check if id is match
67
+ id === taskIdRef.current &&
68
+ // Check if need to cancel
69
+ !cancelFun?.()) {
69
70
  triggerEvent(nextOpenVal);
70
-
71
- // Lock if needed
72
- if (ignoreNext) {
73
- taskLockRef.current = ignoreNext;
74
- macroTask(() => {
75
- taskLockRef.current = false;
76
- }, 3);
77
- }
78
71
  }
79
- return;
80
72
  }
81
- macroTask(() => {
82
- if (id === taskIdRef.current && !taskLockRef.current) {
83
- triggerEvent(nextOpenVal);
84
- }
85
- });
73
+
74
+ // Weak update can be ignored
75
+ if (nextOpenVal) {
76
+ triggerUpdate();
77
+ } else {
78
+ macroTask(() => {
79
+ triggerUpdate();
80
+ });
81
+ }
86
82
  });
87
83
  return [mergedOpen, toggleOpen];
88
84
  }
@@ -1,2 +1,3 @@
1
+ import type { TriggerOpenType } from './useOpen';
1
2
  export declare function isInside(elements: (HTMLElement | SVGElement | undefined)[], target: HTMLElement): boolean;
2
- export default function useSelectTriggerControl(elements: () => (HTMLElement | SVGElement | undefined)[], open: boolean, triggerOpen: (open: boolean) => void, customizedTrigger: boolean): void;
3
+ export default function useSelectTriggerControl(elements: () => (HTMLElement | SVGElement | undefined)[], open: boolean, triggerOpen: TriggerOpenType, customizedTrigger: boolean): void;
@@ -22,9 +22,12 @@ function useSelectTriggerControl(elements, open, triggerOpen, customizedTrigger)
22
22
  if (target.shadowRoot && event.composed) {
23
23
  target = event.composedPath()[0] || target;
24
24
  }
25
+ if (event._ori_target) {
26
+ target = event._ori_target;
27
+ }
25
28
  if (open &&
26
29
  // Marked by SelectInput mouseDown event
27
- !event._ignore_global_close && !isInside(elements(), target)) {
30
+ !isInside(elements(), target)) {
28
31
  // Should trigger close
29
32
  triggerOpen(false);
30
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rc-component/select",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "React Select",
5
5
  "engines": {
6
6
  "node": ">=8.x"