ant-design/components/input/Input.tsx

264 lines
7.8 KiB
TypeScript
Raw Normal View History

import React, { forwardRef, useContext, useEffect, useRef } from 'react';
2016-03-31 17:46:35 +08:00
import classNames from 'classnames';
import type { InputRef, InputProps as RcInputProps } from 'rc-input';
import RcInput from 'rc-input';
import { composeRef } from 'rc-util/lib/ref';
import getAllowClear from '../_util/getAllowClear';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import { devUseWarning } from '../_util/warning';
import { ConfigContext } from '../config-provider';
import DisabledContext from '../config-provider/DisabledContext';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useSize from '../config-provider/hooks/useSize';
import type { SizeType } from '../config-provider/SizeContext';
import { FormItemInputContext, NoFormStyle } from '../form/context';
import type { Variant } from '../form/hooks/useVariants';
import useVariant from '../form/hooks/useVariants';
feat: add Space.Compact (#37652) * feat: add Space.Compact * feat: update input style * feat: add CompactItem for context memoize * feat: add demo * feat: update button style * feat: update input style * feat: 提取 getCompactClassNames 公用方法 * feat: update button style * feat: update picker * feat: add block prop for Space.Compact * feat: use Space.Compact for Input#addonBefore/After * feat: update addon * refactor: compact * feat: update * feat: update * feat: migrate styles to compact for new Input/Input.Search * feat: organize input demo * feat: add more button compact demo * feat: resolve select border collapse * feat: fix input addon select style * feat: add input addon demo in Space * feat: add useCompactItemContext hook * feat: update compact-item style * feat: rollback input#addon implements * feat: rollback input#addon and input.search style * feat: select border collapse * feat: add Space.Compact demo * feat: Space.Compact vertical for Button * refactor: useCompactItemContext * feat: update Button vertical demo * feat: rtl for compact mixin * feat: rtl for compact button * feat: input rtl * feat: add docs for Space.Compact * test: add some tests for Space.Compact * test: add tests * feat: select style * feat: handle select focus style * feat: add useCompactItemContext for Picker and Cascader * style: add compact-item style for cascader * feat: support nested Space.Compact * style: Input.Search in Space.Compact * chore: clean * feat: add useCompactItemContext for TreeSelect * fix: lint issues * fix: style lint * docs: update demos for Space.Compact * docs: update demo * fix: add deps-lint-skip for space * fix: add deps-lint-skip for space * fix: handle edge case for useCompactItemContext * test: add search test case * chore: + bundlesize * style: merge button compact style into one file * style: improve style for nested compact * fix: stylelint * docs: update Space.Compact docs * test: update demo snapshot * test: update input debug test snapshot * docs: update doccs for Space.Compact * test: improve test cases for Compact * docs: clean demos for Compact * refactor: improve Space.Compact * fix: add useCompactItemContext for Input.Search * style: improve compact border-radius implementation * refactor: use context to make nested compact works * fix: input-number focused style * refactor: remove useless style * refactor: improve style for vertical compact * test: update snapshot * test: update snapshot for input * refactor: improve compact-item-border-radius * fix: search group in RTL * style: improve style for button compact * style: use after * fix: stylelint * chore: update snapshot * style: improve button compact * refactor: improve btn-icon-only style in compact
2022-10-18 16:23:10 +08:00
import { NoCompactStyle, useCompactItemContext } from '../space/Compact';
import useRemovePasswordTimeout from './hooks/useRemovePasswordTimeout';
import useStyle from './style';
import { hasPrefixSuffix } from './utils';
export interface InputFocusOptions extends FocusOptions {
cursor?: 'start' | 'end' | 'all';
}
export type { InputRef };
export function triggerFocus(
element?: HTMLInputElement | HTMLTextAreaElement,
option?: InputFocusOptions,
) {
if (!element) {
return;
}
element.focus(option);
// Selection content
const { cursor } = option || {};
if (cursor) {
const len = element.value.length;
switch (cursor) {
case 'start':
element.setSelectionRange(0, 0);
break;
case 'end':
element.setSelectionRange(len, len);
break;
default:
element.setSelectionRange(0, len);
}
}
}
export interface InputProps
extends Omit<
RcInputProps,
'wrapperClassName' | 'groupClassName' | 'inputClassName' | 'affixWrapperClassName' | 'classes'
> {
rootClassName?: string;
size?: SizeType;
disabled?: boolean;
status?: InputStatus;
/** @deprecated Use `variant="borderless"` instead. */
bordered?: boolean;
/**
* @since 5.13.0
* @default "outlined"
*/
2023-12-22 10:05:15 +08:00
variant?: Variant;
[key: `data-${string}`]: string | undefined;
}
const Input = forwardRef<InputRef, InputProps>((props, ref) => {
const {
prefixCls: customizePrefixCls,
bordered = true,
status: customStatus,
size: customSize,
disabled: customDisabled,
onBlur,
onFocus,
suffix,
allowClear,
addonAfter,
addonBefore,
feat: add Space.Compact (#37652) * feat: add Space.Compact * feat: update input style * feat: add CompactItem for context memoize * feat: add demo * feat: update button style * feat: update input style * feat: 提取 getCompactClassNames 公用方法 * feat: update button style * feat: update picker * feat: add block prop for Space.Compact * feat: use Space.Compact for Input#addonBefore/After * feat: update addon * refactor: compact * feat: update * feat: update * feat: migrate styles to compact for new Input/Input.Search * feat: organize input demo * feat: add more button compact demo * feat: resolve select border collapse * feat: fix input addon select style * feat: add input addon demo in Space * feat: add useCompactItemContext hook * feat: update compact-item style * feat: rollback input#addon implements * feat: rollback input#addon and input.search style * feat: select border collapse * feat: add Space.Compact demo * feat: Space.Compact vertical for Button * refactor: useCompactItemContext * feat: update Button vertical demo * feat: rtl for compact mixin * feat: rtl for compact button * feat: input rtl * feat: add docs for Space.Compact * test: add some tests for Space.Compact * test: add tests * feat: select style * feat: handle select focus style * feat: add useCompactItemContext for Picker and Cascader * style: add compact-item style for cascader * feat: support nested Space.Compact * style: Input.Search in Space.Compact * chore: clean * feat: add useCompactItemContext for TreeSelect * fix: lint issues * fix: style lint * docs: update demos for Space.Compact * docs: update demo * fix: add deps-lint-skip for space * fix: add deps-lint-skip for space * fix: handle edge case for useCompactItemContext * test: add search test case * chore: + bundlesize * style: merge button compact style into one file * style: improve style for nested compact * fix: stylelint * docs: update Space.Compact docs * test: update demo snapshot * test: update input debug test snapshot * docs: update doccs for Space.Compact * test: improve test cases for Compact * docs: clean demos for Compact * refactor: improve Space.Compact * fix: add useCompactItemContext for Input.Search * style: improve compact border-radius implementation * refactor: use context to make nested compact works * fix: input-number focused style * refactor: remove useless style * refactor: improve style for vertical compact * test: update snapshot * test: update snapshot for input * refactor: improve compact-item-border-radius * fix: search group in RTL * style: improve style for button compact * style: use after * fix: stylelint * chore: update snapshot * style: improve button compact * refactor: improve btn-icon-only style in compact
2022-10-18 16:23:10 +08:00
className,
style,
styles,
rootClassName,
onChange,
classNames: classes,
variant: customVariant,
...rest
} = props;
if (process.env.NODE_ENV !== 'production') {
const { deprecated } = devUseWarning('Input');
deprecated(!('bordered' in props), 'bordered', 'variant');
}
const { getPrefixCls, direction, input } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('input', customizePrefixCls);
const inputRef = useRef<InputRef>(null);
// Style
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId, cssVarCls] = useStyle(prefixCls, rootCls);
feat: add Space.Compact (#37652) * feat: add Space.Compact * feat: update input style * feat: add CompactItem for context memoize * feat: add demo * feat: update button style * feat: update input style * feat: 提取 getCompactClassNames 公用方法 * feat: update button style * feat: update picker * feat: add block prop for Space.Compact * feat: use Space.Compact for Input#addonBefore/After * feat: update addon * refactor: compact * feat: update * feat: update * feat: migrate styles to compact for new Input/Input.Search * feat: organize input demo * feat: add more button compact demo * feat: resolve select border collapse * feat: fix input addon select style * feat: add input addon demo in Space * feat: add useCompactItemContext hook * feat: update compact-item style * feat: rollback input#addon implements * feat: rollback input#addon and input.search style * feat: select border collapse * feat: add Space.Compact demo * feat: Space.Compact vertical for Button * refactor: useCompactItemContext * feat: update Button vertical demo * feat: rtl for compact mixin * feat: rtl for compact button * feat: input rtl * feat: add docs for Space.Compact * test: add some tests for Space.Compact * test: add tests * feat: select style * feat: handle select focus style * feat: add useCompactItemContext for Picker and Cascader * style: add compact-item style for cascader * feat: support nested Space.Compact * style: Input.Search in Space.Compact * chore: clean * feat: add useCompactItemContext for TreeSelect * fix: lint issues * fix: style lint * docs: update demos for Space.Compact * docs: update demo * fix: add deps-lint-skip for space * fix: add deps-lint-skip for space * fix: handle edge case for useCompactItemContext * test: add search test case * chore: + bundlesize * style: merge button compact style into one file * style: improve style for nested compact * fix: stylelint * docs: update Space.Compact docs * test: update demo snapshot * test: update input debug test snapshot * docs: update doccs for Space.Compact * test: improve test cases for Compact * docs: clean demos for Compact * refactor: improve Space.Compact * fix: add useCompactItemContext for Input.Search * style: improve compact border-radius implementation * refactor: use context to make nested compact works * fix: input-number focused style * refactor: remove useless style * refactor: improve style for vertical compact * test: update snapshot * test: update snapshot for input * refactor: improve compact-item-border-radius * fix: search group in RTL * style: improve style for button compact * style: use after * fix: stylelint * chore: update snapshot * style: improve button compact * refactor: improve btn-icon-only style in compact
2022-10-18 16:23:10 +08:00
// ===================== Compact Item =====================
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
2022-03-16 16:04:01 +08:00
// ===================== Size =====================
const mergedSize = useSize((ctx) => customSize ?? compactSize ?? ctx);
// ===================== Disabled =====================
const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled ?? disabled;
// ===================== Status =====================
const { status: contextStatus, hasFeedback, feedbackIcon } = useContext(FormItemInputContext);
const mergedStatus = getMergedStatus(contextStatus, customStatus);
// ===================== Focus warning =====================
const inputHasPrefixSuffix = hasPrefixSuffix(props) || !!hasFeedback;
const prevHasPrefixSuffix = useRef<boolean>(inputHasPrefixSuffix);
/* eslint-disable react-hooks/rules-of-hooks */
if (process.env.NODE_ENV !== 'production') {
const warning = devUseWarning('Input');
useEffect(() => {
if (inputHasPrefixSuffix && !prevHasPrefixSuffix.current) {
warning(
document.activeElement === inputRef.current?.input,
'usage',
`When Input is focused, dynamic add or remove prefix / suffix will make it lose focus caused by dom structure change. Read more: https://ant.design/components/input/#FAQ`,
);
}
prevHasPrefixSuffix.current = inputHasPrefixSuffix;
}, [inputHasPrefixSuffix]);
}
/* eslint-enable */
// ===================== Remove Password value =====================
const removePasswordTimeout = useRemovePasswordTimeout(inputRef, true);
const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
removePasswordTimeout();
onBlur?.(e);
};
const handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
removePasswordTimeout();
onFocus?.(e);
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
removePasswordTimeout();
onChange?.(e);
};
const suffixNode = (hasFeedback || suffix) && (
<>
{suffix}
{hasFeedback && feedbackIcon}
</>
);
2024-05-22 19:30:07 +08:00
const getAddon = (addon: React.ReactNode) =>
addon && (
<NoCompactStyle>
<NoFormStyle override status>
{addon}
</NoFormStyle>
</NoCompactStyle>
);
const mergedAllowClear = getAllowClear(allowClear ?? input?.allowClear);
2023-12-22 10:05:15 +08:00
const [variant, enableVariantCls] = useVariant(customVariant, bordered);
return wrapCSSVar(
<RcInput
ref={composeRef(ref, inputRef)}
prefixCls={prefixCls}
autoComplete={input?.autoComplete}
{...rest}
disabled={mergedDisabled}
onBlur={handleBlur}
onFocus={handleFocus}
style={{ ...input?.style, ...style }}
styles={{ ...input?.styles, ...styles }}
suffix={suffixNode}
allowClear={mergedAllowClear}
className={classNames(
className,
rootClassName,
cssVarCls,
rootCls,
compactItemClassnames,
input?.className,
)}
onChange={handleChange}
2024-05-22 19:30:07 +08:00
addonBefore={getAddon(addonBefore)}
addonAfter={getAddon(addonAfter)}
classNames={{
...classes,
...input?.classNames,
input: classNames(
{
[`${prefixCls}-sm`]: mergedSize === 'small',
[`${prefixCls}-lg`]: mergedSize === 'large',
[`${prefixCls}-rtl`]: direction === 'rtl',
},
classes?.input,
input?.classNames?.input,
hashId,
),
variant: classNames(
{
[`${prefixCls}-${variant}`]: enableVariantCls,
},
getStatusClassNames(prefixCls, mergedStatus),
),
affixWrapper: classNames(
{
[`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small',
[`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large',
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
},
hashId,
),
wrapper: classNames(
{
[`${prefixCls}-group-rtl`]: direction === 'rtl',
},
hashId,
),
groupWrapper: classNames(
{
[`${prefixCls}-group-wrapper-sm`]: mergedSize === 'small',
[`${prefixCls}-group-wrapper-lg`]: mergedSize === 'large',
[`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl',
[`${prefixCls}-group-wrapper-${variant}`]: enableVariantCls,
},
getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback),
hashId,
),
}}
/>,
);
});
2018-12-26 22:47:55 +08:00
if (process.env.NODE_ENV !== 'production') {
Input.displayName = 'Input';
}
2018-12-26 22:47:55 +08:00
export default Input;