Skip to content

Commit dc11c70

Browse files
committed
refactor: bind onMouseDown to the outer div
1 parent 6775691 commit dc11c70

File tree

4 files changed

+22
-57
lines changed

4 files changed

+22
-57
lines changed

packages/components/input/Input.tsx

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,6 @@ const Input = forwardRefWithStatics(
122122
// inputPreRef 用于预存输入框宽度,应用在 auto width 模式中
123123
const inputPreRef: React.RefObject<HTMLInputElement> = useRef(null);
124124
const wrapperRef: React.RefObject<HTMLDivElement> = useRef(null);
125-
const isClickingIconRef = useRef(false);
126125

127126
const [isHover, toggleIsHover] = useState(false);
128127
const [isFocused, toggleIsFocused] = useState(false);
@@ -139,14 +138,15 @@ const Input = forwardRefWithStatics(
139138
const prefixIconContent = renderIcon(classPrefix, 'prefix', parseTNode(prefixIcon));
140139
let suffixIconNew = suffixIcon;
141140

142-
if (isShowClearIcon)
141+
if (isShowClearIcon) {
143142
suffixIconNew = (
144143
<CloseCircleFilledIcon
145144
className={`${classPrefix}-input__suffix-clear`}
146-
onMouseDown={handleMouseDown}
145+
onMouseDown={handleIconMouseDown}
147146
onClick={handleClear}
148147
/>
149148
);
149+
}
150150
if (type === 'password' && typeof suffixIcon === 'undefined') {
151151
const PASSWORD_ICON_MAP = {
152152
password: BrowseOffIcon,
@@ -155,11 +155,7 @@ const Input = forwardRefWithStatics(
155155
const PasswordIcon = PASSWORD_ICON_MAP[renderType];
156156
if (PasswordIcon) {
157157
suffixIconNew = (
158-
<PasswordIcon
159-
className={`${classPrefix}-input__suffix-clear`}
160-
onMouseDown={handleMouseDown}
161-
onClick={togglePasswordVisible}
162-
/>
158+
<PasswordIcon className={`${classPrefix}-input__suffix-clear`} onClick={togglePasswordVisible} />
163159
);
164160
}
165161
}
@@ -273,6 +269,7 @@ const Input = forwardRefWithStatics(
273269
})}
274270
onMouseEnter={handleMouseEnter}
275271
onMouseLeave={handleMouseLeave}
272+
onMouseDown={handleMouseDown}
276273
onWheel={(e) => onWheel?.({ e })}
277274
onClick={(e) => {
278275
inputRef.current?.focus();
@@ -308,7 +305,6 @@ const Input = forwardRefWithStatics(
308305

309306
requestAnimationFrame(() => {
310307
inputEl?.setSelectionRange(cursorPosition, cursorPosition);
311-
isClickingIconRef.current = false;
312308
});
313309
}
314310

@@ -328,20 +324,20 @@ const Input = forwardRefWithStatics(
328324
onChange(newStr, { e, trigger });
329325
}
330326
}
331-
// 添加MouseDown阻止冒泡,防止點擊Clear value會導致彈窗閃爍一下
332-
// https://github.com/Tencent/tdesign-react/issues/2320
333-
function handleMouseDown(e: React.MouseEvent<SVGSVGElement, globalThis.MouseEvent>) {
327+
function handleIconMouseDown(e: React.MouseEvent<SVGSVGElement>) {
328+
e.preventDefault();
329+
// 阻止冒泡,防止点击 icon 会导致弹窗闪烁一下
330+
// https://github.com/Tencent/tdesign-react/issues/2320
334331
e.stopPropagation();
335-
// 兼容React16
332+
// 兼容 React 16
336333
e.nativeEvent.stopImmediatePropagation();
337-
isClickingIconRef.current = true;
334+
}
335+
function handleMouseDown(e: React.MouseEvent<HTMLDivElement>) {
336+
e.preventDefault(); // 防止焦点转移
338337
}
339338
function handleClear(e: React.MouseEvent<SVGSVGElement>) {
340339
onChange?.('', { e, trigger: 'clear' });
341340
onClear?.({ e });
342-
requestAnimationFrame(() => {
343-
isClickingIconRef.current = false;
344-
});
345341
}
346342
function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
347343
const {
@@ -382,7 +378,6 @@ const Input = forwardRefWithStatics(
382378
}
383379

384380
function handleFocus(e: React.FocusEvent<HTMLInputElement>) {
385-
if (isClickingIconRef.current) return;
386381
const {
387382
currentTarget: { value },
388383
} = e;
@@ -392,7 +387,6 @@ const Input = forwardRefWithStatics(
392387
}
393388

394389
function handleBlur(e: React.FocusEvent<HTMLInputElement>) {
395-
if (isClickingIconRef.current) return;
396390
const {
397391
currentTarget: { value },
398392
} = e;

packages/components/input/__tests__/input.test.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { render, fireEvent, vi, act, mockTimeout } from '@test/utils';
2+
import { render, fireEvent, vi, act } from '@test/utils';
33
import userEvent from '@testing-library/user-event';
44
import Input from '../Input';
55

@@ -47,7 +47,6 @@ describe('Input 组件测试', () => {
4747
fireEvent.mouseUp(clearIcon);
4848
fireEvent.click(clearIcon);
4949
expect(blurFn).toBeCalledTimes(0);
50-
await mockTimeout(() => true);
5150
fireEvent.blur(InputDom);
5251
expect(blurFn).toBeCalledTimes(1);
5352
});

packages/components/select-input/useMultiple.tsx

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ export default function useMultiple(props: SelectInputProps) {
3737
const [tInputValue, setTInputValue] = useControlled(props, 'inputValue', props.onInputChange);
3838

3939
const tagInputRef = useRef<InputRef>(null);
40-
const blurTimeoutRef = useRef(null);
4140

4241
const iKeys: SelectInputKeys = { ...DEFAULT_KEYS, ...props.keys };
4342

@@ -61,30 +60,17 @@ export default function useMultiple(props: SelectInputProps) {
6160

6261
const renderSelectMultiple = (p: RenderSelectMultipleParams) => {
6362
const handleBlur = (value: SelectInputValue, context: { e: React.FocusEvent<HTMLInputElement> }) => {
64-
if (blurTimeoutRef.current) {
65-
clearTimeout(blurTimeoutRef.current);
63+
if (!p.popupVisible) {
64+
p.onInnerBlur(context);
65+
} else if (!props.panel) {
66+
props.onBlur?.(value, { e: context.e, inputValue: tInputValue, tagInputValue: tags });
6667
}
67-
// 强制把 popupVisible 设置为 false 时,点击 input,会出现 blur -> focus 的情况,因此忽略前面短暂的 blur 事件
68-
blurTimeoutRef.current = setTimeout(() => {
69-
if (blurTimeoutRef.current) {
70-
if (!p.popupVisible) {
71-
p.onInnerBlur(context);
72-
} else if (!props.panel) {
73-
props.onBlur?.(value, { e: context.e, inputValue: tInputValue, tagInputValue: tags });
74-
}
75-
}
76-
blurTimeoutRef.current = null;
77-
}, 150);
7868
};
7969

8070
const handleFocus = (
8171
val: TagInputValue,
8272
context: { e: React.FocusEvent<HTMLInputElement>; inputValue: string },
8373
) => {
84-
if (blurTimeoutRef.current) {
85-
clearTimeout(blurTimeoutRef.current);
86-
blurTimeoutRef.current = null;
87-
}
8874
props.onFocus?.(props.value, { ...context, tagInputValue: val });
8975
};
9076

packages/components/select-input/useSingle.tsx

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ export default function useSingle(props: TdSelectInputProps) {
4848
const { classPrefix } = useConfig();
4949

5050
const inputRef = useRef<InputRef>(null);
51-
const blurTimeoutRef = useRef(null);
5251

5352
const [inputValue, setInputValue] = useControlled(props, 'inputValue', props.onInputChange);
5453

@@ -78,27 +77,14 @@ export default function useSingle(props: TdSelectInputProps) {
7877
const displayedValue = popupVisible && props.allowInput ? inputValue : getInputValue(value, keys);
7978

8079
const handleBlur = (value, ctx) => {
81-
if (blurTimeoutRef.current) {
82-
clearTimeout(blurTimeoutRef.current);
80+
if (!popupVisible) {
81+
onInnerBlur(ctx);
82+
} else if (!props.panel) {
83+
props.onBlur?.(value, { e: ctx.e, inputValue: value });
8384
}
84-
// 强制把 popupVisible 设置为 false 时,点击 input,会出现 blur -> focus 的情况,因此忽略前面短暂的 blur 事件
85-
blurTimeoutRef.current = setTimeout(() => {
86-
if (blurTimeoutRef.current) {
87-
if (!popupVisible) {
88-
onInnerBlur(ctx);
89-
} else if (!props.panel) {
90-
props.onBlur?.(value, { e: ctx.e, inputValue: value });
91-
}
92-
}
93-
blurTimeoutRef.current = null;
94-
}, 150);
9585
};
9686

9787
const handleFocus = (val, context) => {
98-
if (blurTimeoutRef.current) {
99-
clearTimeout(blurTimeoutRef.current);
100-
blurTimeoutRef.current = null;
101-
}
10288
props.onFocus?.(value, { ...context, inputValue: val });
10389
// focus might not need to change input value. it will caught some curious errors in tree-select
10490
// !popupVisible && setInputValue(getInputValue(value, keys), { ...context, trigger: 'input' });

0 commit comments

Comments
 (0)