diff --git a/packages/components/input/Input.tsx b/packages/components/input/Input.tsx index 9b1727d118..5705701eaf 100644 --- a/packages/components/input/Input.tsx +++ b/packages/components/input/Input.tsx @@ -105,7 +105,6 @@ const Input = forwardRefWithStatics( onChange: onChangeFromProps, ...restProps } = props; - const readOnlyProp = readOnly || readonly; const [value, onChange] = useControlled(props, 'value', onChangeFromProps); const { limitNumber, getValueByLimitNumber, tStatus } = useLengthLimit({ @@ -118,17 +117,19 @@ const Input = forwardRefWithStatics( }); const { classPrefix, input: inputConfig } = useConfig(); + const composingRef = useRef(false); const inputRef: React.RefObject = useRef(null); // inputPreRef 用于预存输入框宽度,应用在 auto width 模式中 const inputPreRef: React.RefObject = useRef(null); const wrapperRef: React.RefObject = useRef(null); + const [isHover, toggleIsHover] = useState(false); const [isFocused, toggleIsFocused] = useState(false); const [renderType, setRenderType] = useState(type); - const [composingValue, setComposingValue] = useState(''); + const readOnlyProp = readOnly || readonly; // 组件内部 input 原生控件是否处于 readonly 状态,当整个组件 readonly 时,或者处于不可输入时 const isInnerInputReadonly = readOnlyProp || !allowInput; const isValueEnabled = value && !disabled; @@ -140,14 +141,23 @@ const Input = forwardRefWithStatics( let suffixIconNew = suffixIcon; if (isShowClearIcon) - suffixIconNew = ; + suffixIconNew = ( + + ); if (type === 'password' && typeof suffixIcon === 'undefined') { - if (renderType === 'password') { + const PASSWORD_ICON_MAP = { + password: BrowseOffIcon, + text: BrowseIcon, + }; + const PasswordIcon = PASSWORD_ICON_MAP[renderType]; + if (PasswordIcon) { suffixIconNew = ( - + ); - } else if (renderType === 'text') { - suffixIconNew = ; } } @@ -260,6 +270,7 @@ const Input = forwardRefWithStatics( })} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} + onMouseDown={handleMouseDown} onWheel={(e) => onWheel?.({ e })} onClick={(e) => { inputRef.current?.focus(); @@ -314,6 +325,19 @@ const Input = forwardRefWithStatics( onChange(newStr, { e, trigger }); } } + function handleIconMouseDown(e: React.MouseEvent) { + e.preventDefault(); + // 阻止冒泡,防止点击 icon 会导致弹窗闪烁一下 + // https://github.com/Tencent/tdesign-react/issues/2320 + e.stopPropagation(); + // 兼容 React 16 + e.nativeEvent.stopImmediatePropagation(); + } + function handleMouseDown(e: React.MouseEvent) { + if (e.target !== inputRef.current) { + e.preventDefault(); // 避免焦点转移 + } + } function handleClear(e: React.MouseEvent) { onChange?.('', { e, trigger: 'clear' }); onClear?.({ e }); @@ -381,12 +405,12 @@ const Input = forwardRefWithStatics( } function handleMouseEnter(e: React.MouseEvent) { - !readOnly && toggleIsHover(true); + !readOnlyProp && toggleIsHover(true); onMouseenter?.({ e }); } function handleMouseLeave(e: React.MouseEvent) { - !readOnly && toggleIsHover(false); + !readOnlyProp && toggleIsHover(false); onMouseleave?.({ e }); } diff --git a/packages/components/select-input/useMultiple.tsx b/packages/components/select-input/useMultiple.tsx index 66dcf839a6..41f3ddb979 100644 --- a/packages/components/select-input/useMultiple.tsx +++ b/packages/components/select-input/useMultiple.tsx @@ -39,7 +39,6 @@ export default function useMultiple(props: SelectInputProps) { const [tInputValue, setTInputValue] = useControlled(props, 'inputValue', props.onInputChange); const tagInputRef = useRef(null); - const blurTimeoutRef = useRef(null); const iKeys: SelectInputKeys = { ...DEFAULT_KEYS, ...props.keys }; @@ -63,30 +62,17 @@ export default function useMultiple(props: SelectInputProps) { const renderSelectMultiple = (p: RenderSelectMultipleParams) => { const handleBlur = (value: SelectInputValue, context: { e: React.FocusEvent }) => { - if (blurTimeoutRef.current) { - clearTimeout(blurTimeoutRef.current); + if (!p.popupVisible) { + p.onInnerBlur(context); + } else if (!props.panel) { + props.onBlur?.(value, { e: context.e, inputValue: tInputValue, tagInputValue: tags }); } - // 强制把 popupVisible 设置为 false 时,点击 input,会出现 blur -> focus 的情况,因此忽略前面短暂的 blur 事件 - blurTimeoutRef.current = setTimeout(() => { - if (blurTimeoutRef.current) { - if (!p.popupVisible) { - p.onInnerBlur(context); - } else if (!props.panel) { - props.onBlur?.(value, { e: context.e, inputValue: tInputValue, tagInputValue: tags }); - } - } - blurTimeoutRef.current = null; - }, 150); }; const handleFocus = ( val: TagInputValue, context: { e: React.FocusEvent; inputValue: string }, ) => { - if (blurTimeoutRef.current) { - clearTimeout(blurTimeoutRef.current); - blurTimeoutRef.current = null; - } props.onFocus?.(props.value, { ...context, tagInputValue: val }); }; diff --git a/packages/components/select-input/useSingle.tsx b/packages/components/select-input/useSingle.tsx index 9e92324767..c14cf99fd0 100644 --- a/packages/components/select-input/useSingle.tsx +++ b/packages/components/select-input/useSingle.tsx @@ -48,7 +48,6 @@ export default function useSingle(props: TdSelectInputProps) { const { classPrefix } = useConfig(); const inputRef = useRef(null); - const blurTimeoutRef = useRef(null); const [inputValue, setInputValue] = useControlled(props, 'inputValue', props.onInputChange); @@ -78,27 +77,14 @@ export default function useSingle(props: TdSelectInputProps) { const displayedValue = popupVisible && props.allowInput ? inputValue : getInputValue(value, keys); const handleBlur = (value, ctx) => { - if (blurTimeoutRef.current) { - clearTimeout(blurTimeoutRef.current); + if (!popupVisible) { + onInnerBlur(ctx); + } else if (!props.panel) { + props.onBlur?.(value, { e: ctx.e, inputValue: value }); } - // 强制把 popupVisible 设置为 false 时,点击 input,会出现 blur -> focus 的情况,因此忽略前面短暂的 blur 事件 - blurTimeoutRef.current = setTimeout(() => { - if (blurTimeoutRef.current) { - if (!popupVisible) { - onInnerBlur(ctx); - } else if (!props.panel) { - props.onBlur?.(value, { e: ctx.e, inputValue: value }); - } - } - blurTimeoutRef.current = null; - }, 150); }; const handleFocus = (val, context) => { - if (blurTimeoutRef.current) { - clearTimeout(blurTimeoutRef.current); - blurTimeoutRef.current = null; - } props.onFocus?.(value, { ...context, inputValue: val }); // focus might not need to change input value. it will caught some curious errors in tree-select // !popupVisible && setInputValue(getInputValue(value, keys), { ...context, trigger: 'input' });