Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 32 additions & 20 deletions packages/components/input/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
import React, { useState, useRef, useImperativeHandle, useEffect } from 'react';
import classNames from 'classnames';
import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
import {
BrowseIcon as TdBrowseIcon,
BrowseOffIcon as TdBrowseOffIcon,
CloseCircleFilledIcon as TdCloseCircleFilledIcon,
} from 'tdesign-icons-react';
import classNames from 'classnames';
import { isFunction } from 'lodash-es';
import useLayoutEffect from '../hooks/useLayoutEffect';

import forwardRefWithStatics from '../_util/forwardRefWithStatics';
import parseTNode from '../_util/parseTNode';
import useConfig from '../hooks/useConfig';
import useGlobalIcon from '../hooks/useGlobalIcon';
import { TdInputProps } from './type';
import { StyledProps, TNode, TElement } from '../common';
import InputGroup from './InputGroup';
import useControlled from '../hooks/useControlled';
import useDefaultProps from '../hooks/useDefaultProps';
import useGlobalIcon from '../hooks/useGlobalIcon';
import useLayoutEffect from '../hooks/useLayoutEffect';
import { useLocaleReceiver } from '../locale/LocalReceiver';
import { inputDefaultProps } from './defaultProps';
import parseTNode from '../_util/parseTNode';
import InputGroup from './InputGroup';
import useLengthLimit from './useLengthLimit';
import useDefaultProps from '../hooks/useDefaultProps';

import type { StyledProps, TElement, TNode } from '../common';
import type { TdInputProps } from './type';

export interface InputProps extends TdInputProps, StyledProps {
showInput?: boolean; // 控制透传readonly同时是否展示input 默认保留 因为正常Input需要撑开宽度
Expand Down Expand Up @@ -114,15 +116,16 @@ const Input = forwardRefWithStatics(
});

const { classPrefix, input: inputConfig } = useConfig();

const composingRef = useRef(false);
const inputRef: React.RefObject<HTMLInputElement> = useRef(null);
// inputPreRef 用于预存输入框宽度,应用在 auto width 模式中
const inputPreRef: React.RefObject<HTMLInputElement> = useRef(null);
const wrapperRef: React.RefObject<HTMLDivElement> = useRef(null);

const [isHover, toggleIsHover] = useState(false);
const [isFocused, toggleIsFocused] = useState(false);
const [renderType, setRenderType] = useState(type);

const [composingValue, setComposingValue] = useState<string>('');

// 组件内部 input 原生控件是否处于 readonly 状态,当整个组件 readonly 时,或者处于不可输入时
Expand All @@ -135,21 +138,25 @@ const Input = forwardRefWithStatics(
const prefixIconContent = renderIcon(classPrefix, 'prefix', parseTNode(prefixIcon));
let suffixIconNew = suffixIcon;

if (isShowClearIcon)
if (isShowClearIcon) {
suffixIconNew = (
<CloseCircleFilledIcon
className={`${classPrefix}-input__suffix-clear`}
onMouseDown={handleMouseDown}
onMouseDown={handleIconMouseDown}
onClick={handleClear}
/>
);
}
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 = (
<BrowseOffIcon className={`${classPrefix}-input__suffix-clear`} onClick={togglePasswordVisible} />
<PasswordIcon className={`${classPrefix}-input__suffix-clear`} onClick={togglePasswordVisible} />
);
} else if (renderType === 'text') {
suffixIconNew = <BrowseIcon className={`${classPrefix}-input__suffix-clear`} onClick={togglePasswordVisible} />;
}
}

Expand Down Expand Up @@ -262,6 +269,7 @@ const Input = forwardRefWithStatics(
})}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onMouseDown={handleMouseDown}
onWheel={(e) => onWheel?.({ e })}
onClick={(e) => {
inputRef.current?.focus();
Expand Down Expand Up @@ -316,13 +324,17 @@ const Input = forwardRefWithStatics(
onChange(newStr, { e, trigger });
}
}
// 添加MouseDown阻止冒泡,防止點擊Clear value會導致彈窗閃爍一下
// https://github.com/Tencent/tdesign-react/issues/2320
function handleMouseDown(e: React.MouseEvent<SVGSVGElement, globalThis.MouseEvent>) {
function handleIconMouseDown(e: React.MouseEvent<SVGSVGElement>) {
e.preventDefault();
// 阻止冒泡,防止点击 icon 会导致弹窗闪烁一下
// https://github.com/Tencent/tdesign-react/issues/2320
e.stopPropagation();
// 兼容React16
// 兼容 React 16
e.nativeEvent.stopImmediatePropagation();
}
function handleMouseDown(e: React.MouseEvent<HTMLDivElement>) {
e.preventDefault(); // 防止焦点转移
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handleMousedown 阻止默认行为估计会有很多没有考虑的影响 还是input这么底层的组件本身

}
function handleClear(e: React.MouseEvent<SVGSVGElement>) {
onChange?.('', { e, trigger: 'clear' });
onClear?.({ e });
Expand Down
22 changes: 4 additions & 18 deletions packages/components/select-input/useMultiple.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export default function useMultiple(props: SelectInputProps) {
const [tInputValue, setTInputValue] = useControlled(props, 'inputValue', props.onInputChange);

const tagInputRef = useRef<InputRef>(null);
const blurTimeoutRef = useRef(null);

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

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

const renderSelectMultiple = (p: RenderSelectMultipleParams) => {
const handleBlur = (value: SelectInputValue, context: { e: React.FocusEvent<HTMLInputElement> }) => {
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<HTMLInputElement>; inputValue: string },
) => {
if (blurTimeoutRef.current) {
clearTimeout(blurTimeoutRef.current);
blurTimeoutRef.current = null;
}
props.onFocus?.(props.value, { ...context, tagInputValue: val });
};

Expand Down
22 changes: 4 additions & 18 deletions packages/components/select-input/useSingle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export default function useSingle(props: TdSelectInputProps) {
const { classPrefix } = useConfig();

const inputRef = useRef<InputRef>(null);
const blurTimeoutRef = useRef(null);

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

Expand Down Expand Up @@ -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' });
Expand Down
Loading