Skip to content

Commit 5033561

Browse files
authored
chore: adjust order of call (#1183)
* chore: use weak instead * chore: adjust * chore: adjust * chore: clean up * chore: clean up
1 parent b04dc0f commit 5033561

File tree

5 files changed

+45
-46
lines changed

5 files changed

+45
-46
lines changed

src/BaseSelect/index.tsx

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import type { RefTriggerProps } from '../SelectTrigger';
2121
import SelectTrigger from '../SelectTrigger';
2222
import { getSeparatedContent, isValidCount } from '../utils/valueUtil';
2323
import Polite from './Polite';
24-
import useOpen, { macroTask } from '../hooks/useOpen';
24+
import useOpen from '../hooks/useOpen';
2525
import { useEvent } from '@rc-component/util';
2626
import type { SelectInputRef } from '../SelectInput';
2727
import SelectInput from '../SelectInput';
@@ -564,11 +564,12 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
564564
};
565565

566566
const onRootBlur = () => {
567-
macroTask(() => {
568-
if (!isInside(getSelectElements(), document.activeElement as HTMLElement)) {
569-
triggerOpen(false);
570-
}
571-
});
567+
// Delay close should check the activeElement
568+
if (mergedOpen) {
569+
triggerOpen(false, {
570+
cancelFun: () => isInside(getSelectElements(), document.activeElement as HTMLElement),
571+
});
572+
}
572573
};
573574

574575
const onInternalBlur: React.FocusEventHandler<HTMLElement> = (event) => {
@@ -593,16 +594,14 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
593594
}
594595
};
595596

596-
const onInternalMouseDown: React.MouseEventHandler<HTMLDivElement> = (event, ...restArgs) => {
597+
const onRootMouseDown: React.MouseEventHandler<HTMLDivElement> = (event, ...restArgs) => {
597598
const { target } = event;
598599
const popupElement: HTMLDivElement = triggerRef.current?.getPopupElement();
599600

600601
// We should give focus back to selector if clicked item is not focusable
601602
if (popupElement?.contains(target as HTMLElement) && triggerOpen) {
602603
// Tell `open` not to close since it's safe in the popup
603-
triggerOpen(true, {
604-
ignoreNext: true,
605-
});
604+
triggerOpen(true);
606605
}
607606

608607
onMouseDown?.(event, ...restArgs);
@@ -747,7 +746,7 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
747746
// Token handling
748747
tokenWithEnter={tokenWithEnter}
749748
// Open
750-
onMouseDown={onInternalMouseDown}
749+
onMouseDown={onRootMouseDown}
751750
// Components
752751
components={mergedComponents}
753752
/>
@@ -774,7 +773,7 @@ const BaseSelect = React.forwardRef<BaseSelectRef, BaseSelectProps>((props, ref)
774773
empty={emptyOptions}
775774
onPopupVisibleChange={onTriggerVisibleChange}
776775
onPopupMouseEnter={onPopupMouseEnter}
777-
onPopupMouseDown={onInternalMouseDown}
776+
onPopupMouseDown={onRootMouseDown}
778777
onPopupBlur={onRootBlur}
779778
>
780779
{renderNode}

src/SelectInput/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,13 +170,14 @@ export default React.forwardRef<SelectInputRef, SelectInputProps>(function Selec
170170
// ====================== Open ======================
171171
const onInternalMouseDown: SelectInputProps['onMouseDown'] = useEvent((event) => {
172172
if (!disabled) {
173+
const inputDOM = getDOM(inputRef.current);
174+
173175
// https://github.com/ant-design/ant-design/issues/56002
174176
// Tell `useSelectTriggerControl` to ignore this event
175177
// When icon is dynamic render, the parentNode will miss
176178
// so we need to mark the event directly
177-
(event.nativeEvent as any)._ignore_global_close = true;
179+
(event.nativeEvent as any)._ori_target = inputDOM;
178180

179-
const inputDOM = getDOM(inputRef.current);
180181
if (inputDOM && event.target !== inputDOM && !inputDOM.contains(event.target as Node)) {
181182
event.preventDefault();
182183
}

src/hooks/useOpen.ts

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@ export const macroTask = (fn: VoidFunction, times = 1) => {
2020

2121
/**
2222
* Trigger by latest open call, if nextOpen is undefined, means toggle.
23-
* ignoreNext will skip next call in the macro task queue.
23+
* `weak` means this call can be ignored if previous call exists.
2424
*/
2525
export type TriggerOpenType = (
2626
nextOpen?: boolean,
2727
config?: {
28-
ignoreNext?: boolean;
29-
lazy?: boolean;
28+
cancelFun?: () => boolean;
3029
},
3130
) => void;
3231

@@ -58,7 +57,6 @@ export default function useOpen(
5857
const mergedOpen = postOpen(ssrSafeOpen);
5958

6059
const taskIdRef = useRef(0);
61-
const taskLockRef = useRef(false);
6260

6361
const triggerEvent = useEvent((nextOpen: boolean) => {
6462
if (onOpen && mergedOpen !== nextOpen) {
@@ -68,35 +66,32 @@ export default function useOpen(
6866
});
6967

7068
const toggleOpen = useEvent<TriggerOpenType>((nextOpen, config = {}) => {
71-
const { ignoreNext = false } = config;
69+
const { cancelFun } = config;
7270

7371
taskIdRef.current += 1;
7472
const id = taskIdRef.current;
7573

7674
const nextOpenVal = typeof nextOpen === 'boolean' ? nextOpen : !mergedOpen;
7775

78-
// Since `mergedOpen` is post-processed, we need to check if the value really changed
79-
if (nextOpenVal) {
80-
if (!taskLockRef.current) {
76+
function triggerUpdate() {
77+
if (
78+
// Always check if id is match
79+
id === taskIdRef.current &&
80+
// Check if need to cancel
81+
!cancelFun?.()
82+
) {
8183
triggerEvent(nextOpenVal);
82-
83-
// Lock if needed
84-
if (ignoreNext) {
85-
taskLockRef.current = ignoreNext;
86-
87-
macroTask(() => {
88-
taskLockRef.current = false;
89-
}, 3);
90-
}
9184
}
92-
return;
9385
}
9486

95-
macroTask(() => {
96-
if (id === taskIdRef.current && !taskLockRef.current) {
97-
triggerEvent(nextOpenVal);
98-
}
99-
});
87+
// Weak update can be ignored
88+
if (nextOpenVal) {
89+
triggerUpdate();
90+
} else {
91+
macroTask(() => {
92+
triggerUpdate();
93+
});
94+
}
10095
});
10196

10297
return [mergedOpen, toggleOpen] as const;

src/hooks/useSelectTriggerControl.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as React from 'react';
22
import { useEvent } from '@rc-component/util';
3+
import type { TriggerOpenType } from './useOpen';
34

45
export function isInside(elements: (HTMLElement | SVGElement | undefined)[], target: HTMLElement) {
56
return elements
@@ -10,7 +11,7 @@ export function isInside(elements: (HTMLElement | SVGElement | undefined)[], tar
1011
export default function useSelectTriggerControl(
1112
elements: () => (HTMLElement | SVGElement | undefined)[],
1213
open: boolean,
13-
triggerOpen: (open: boolean) => void,
14+
triggerOpen: TriggerOpenType,
1415
customizedTrigger: boolean,
1516
) {
1617
const onGlobalMouseDown = useEvent((event: MouseEvent) => {
@@ -25,10 +26,13 @@ export default function useSelectTriggerControl(
2526
target = (event.composedPath()[0] || target) as HTMLElement;
2627
}
2728

29+
if ((event as any)._ori_target) {
30+
target = (event as any)._ori_target;
31+
}
32+
2833
if (
2934
open &&
3035
// Marked by SelectInput mouseDown event
31-
!(event as any)._ignore_global_close &&
3236
!isInside(elements(), target)
3337
) {
3438
// Should trigger close

tests/Select.test.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ describe('Select.Basic', () => {
603603
</Select>,
604604
);
605605

606-
keyDown(container.querySelector('input'), 40);
606+
keyDown(container.querySelector('input'), KeyCode.DOWN);
607607
expectOpen(container);
608608
});
609609

@@ -2566,9 +2566,9 @@ describe('Select.Basic', () => {
25662566
await waitFakeTimer();
25672567
expectOpen(container, true);
25682568

2569-
keyDown(inputElem!, 40);
2570-
keyUp(inputElem!, 40);
2571-
keyDown(inputElem!, 13);
2569+
keyDown(inputElem!, KeyCode.DOWN);
2570+
keyUp(inputElem!, KeyCode.DOWN);
2571+
keyDown(inputElem!, KeyCode.ENTER);
25722572

25732573
await waitFakeTimer();
25742574
expect(onBlur).toHaveBeenCalledTimes(1);
@@ -2579,9 +2579,9 @@ describe('Select.Basic', () => {
25792579
await waitFakeTimer();
25802580
expectOpen(container, true);
25812581

2582-
keyDown(inputElem!, 40);
2583-
keyUp(inputElem!, 40);
2584-
keyDown(inputElem!, 13);
2582+
keyDown(inputElem!, KeyCode.DOWN);
2583+
keyUp(inputElem!, KeyCode.DOWN);
2584+
keyDown(inputElem!, KeyCode.ENTER);
25852585

25862586
await waitFakeTimer();
25872587
expect(onBlur).toHaveBeenCalledTimes(2);

0 commit comments

Comments
 (0)