refactor: TextArea (#40045)

* refactor: TextArea

* chore: update snapshot

* chore: update snapshot

* chore: fix lint

* chore: classes

* refactor: mentions

* chore: bump rc-textarea

* chore: code clean

* chore: code clean

* chore: UPDATE SNAPSHOT

* chore: code clean

* test: cov

* fix: textarea size
This commit is contained in:
MadCcc 2023-01-11 14:18:13 +08:00 committed by GitHub
parent 3f87a2d263
commit 642bb582eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 759 additions and 885 deletions

View File

@ -8999,7 +8999,7 @@ exports[`renders ./components/form/demo/register.tsx extend context correctly 1`
class="ant-form-item-control-input-content" class="ant-form-item-control-input-content"
> >
<div <div
class="ant-input-textarea ant-input-textarea-show-count" class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
data-count="0 / 100" data-count="0 / 100"
> >
<textarea <textarea
@ -9007,6 +9007,11 @@ exports[`renders ./components/form/demo/register.tsx extend context correctly 1`
class="ant-input" class="ant-input"
id="register_intro" id="register_intro"
/> />
<span
class="ant-input-data-count"
>
0 / 100
</span>
</div> </div>
</div> </div>
</div> </div>
@ -26427,8 +26432,8 @@ exports[`renders ./components/form/demo/validate-static.tsx extend context corre
<div <div
class="ant-form-item-control-input-content" class="ant-form-item-control-input-content"
> >
<div <span
class="ant-mentions-affix-wrapper ant-mentions-affix-wrapper-status-error ant-mentions-affix-wrapper-has-feedback" class="ant-mentions-affix-wrapper"
> >
<div <div
class="ant-mentions ant-mentions-status-error" class="ant-mentions ant-mentions-status-error"
@ -26465,7 +26470,7 @@ exports[`renders ./components/form/demo/validate-static.tsx extend context corre
</span> </span>
</span> </span>
</span> </span>
</div> </span>
</div> </div>
</div> </div>
</div> </div>
@ -26497,62 +26502,76 @@ exports[`renders ./components/form/demo/validate-static.tsx extend context corre
class="ant-form-item-control-input-content" class="ant-form-item-control-input-content"
> >
<div <div
class="ant-input-textarea ant-input-textarea-show-count ant-input-textarea-status-error ant-input-textarea-has-feedback" class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
data-count="0" data-count="0"
> >
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn ant-input-affix-wrapper-status-error ant-input-affix-wrapper-has-feedback" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper ant-input-affix-wrapper-status-error"
> >
<textarea <textarea
class="ant-input ant-input-status-error" class="ant-input ant-input-status-error"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden ant-input-clear-icon-has-suffix"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
<span
class="ant-input-textarea-suffix"
>
<span
class="ant-form-item-feedback-icon ant-form-item-feedback-icon-error"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span> </span>
</span> </span>
<span <span
class="ant-input-textarea-suffix" class="ant-input-data-count"
> >
<span 0
class="ant-form-item-feedback-icon ant-form-item-feedback-icon-error"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</div> </div>
</div> </div>

View File

@ -6098,7 +6098,7 @@ exports[`renders ./components/form/demo/register.tsx correctly 1`] = `
class="ant-form-item-control-input-content" class="ant-form-item-control-input-content"
> >
<div <div
class="ant-input-textarea ant-input-textarea-show-count" class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
data-count="0 / 100" data-count="0 / 100"
> >
<textarea <textarea
@ -6106,6 +6106,11 @@ exports[`renders ./components/form/demo/register.tsx correctly 1`] = `
class="ant-input" class="ant-input"
id="register_intro" id="register_intro"
/> />
<span
class="ant-input-data-count"
>
0 / 100
</span>
</div> </div>
</div> </div>
</div> </div>
@ -11022,8 +11027,8 @@ exports[`renders ./components/form/demo/validate-static.tsx correctly 1`] = `
<div <div
class="ant-form-item-control-input-content" class="ant-form-item-control-input-content"
> >
<div <span
class="ant-mentions-affix-wrapper ant-mentions-affix-wrapper-status-error ant-mentions-affix-wrapper-has-feedback" class="ant-mentions-affix-wrapper"
> >
<div <div
class="ant-mentions ant-mentions-status-error" class="ant-mentions ant-mentions-status-error"
@ -11060,7 +11065,7 @@ exports[`renders ./components/form/demo/validate-static.tsx correctly 1`] = `
</span> </span>
</span> </span>
</span> </span>
</div> </span>
</div> </div>
</div> </div>
</div> </div>
@ -11092,62 +11097,76 @@ exports[`renders ./components/form/demo/validate-static.tsx correctly 1`] = `
class="ant-form-item-control-input-content" class="ant-form-item-control-input-content"
> >
<div <div
class="ant-input-textarea ant-input-textarea-show-count ant-input-textarea-status-error ant-input-textarea-has-feedback" class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
data-count="0" data-count="0"
> >
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn ant-input-affix-wrapper-status-error ant-input-affix-wrapper-has-feedback" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper ant-input-affix-wrapper-status-error"
> >
<textarea <textarea
class="ant-input ant-input-status-error" class="ant-input ant-input-status-error"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden ant-input-clear-icon-has-suffix"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
<span
class="ant-input-textarea-suffix"
>
<span
class="ant-form-item-feedback-icon ant-form-item-feedback-icon-error"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span>
</span> </span>
</span> </span>
<span <span
class="ant-input-textarea-suffix" class="ant-input-data-count"
> >
<span 0
class="ant-form-item-feedback-icon ant-form-item-feedback-icon-error"
>
<span
aria-label="close-circle"
class="anticon anticon-close-circle"
role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</div> </div>
</div> </div>

View File

@ -1,137 +0,0 @@
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import classNames from 'classnames';
import * as React from 'react';
import type { DirectionType } from '../config-provider';
import type { SizeType } from '../config-provider/SizeContext';
import type { FormItemStatusContextProps } from '../form/context';
import { FormItemInputContext } from '../form/context';
import { cloneElement } from '../_util/reactNode';
import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import type { InputProps } from './Input';
const ClearableInputType = ['text', 'input'] as const;
function hasAddon(props: InputProps | ClearableInputProps) {
return !!(props.addonBefore || props.addonAfter);
}
/** This basic props required for input and textarea. */
interface BasicProps {
prefixCls: string;
inputType: typeof ClearableInputType[number];
value?: any;
allowClear?: boolean;
element: React.ReactElement;
handleReset: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
className?: string;
style?: React.CSSProperties;
disabled?: boolean;
direction?: DirectionType;
focused?: boolean;
readOnly?: boolean;
bordered: boolean;
hidden?: boolean;
}
/** This props only for input. */
export interface ClearableInputProps extends BasicProps {
size?: SizeType;
suffix?: React.ReactNode;
prefix?: React.ReactNode;
addonBefore?: React.ReactNode;
addonAfter?: React.ReactNode;
triggerFocus?: () => void;
status?: InputStatus;
hashId?: string;
}
class ClearableLabeledInput extends React.Component<ClearableInputProps> {
renderClearIcon(prefixCls: string) {
const { value, disabled, readOnly, handleReset, suffix } = this.props;
const needClear = !disabled && !readOnly && value;
const className = `${prefixCls}-clear-icon`;
return (
<CloseCircleFilled
onClick={handleReset}
// Do not trigger onBlur when clear input
// https://github.com/ant-design/ant-design/issues/31200
onMouseDown={(e) => e.preventDefault()}
className={classNames(
{
[`${className}-hidden`]: !needClear,
[`${className}-has-suffix`]: !!suffix,
},
className,
)}
role="button"
/>
);
}
renderTextAreaWithClearIcon(
prefixCls: string,
element: React.ReactElement,
statusContext: FormItemStatusContextProps,
) {
const {
value,
allowClear,
className,
style,
direction,
bordered,
hidden,
status: customStatus,
hashId,
} = this.props;
const { status: contextStatus, hasFeedback } = statusContext;
if (!allowClear) {
return cloneElement(element, {
value,
});
}
const affixWrapperCls = classNames(
`${prefixCls}-affix-wrapper`,
`${prefixCls}-affix-wrapper-textarea-with-clear-btn`,
getStatusClassNames(
`${prefixCls}-affix-wrapper`,
getMergedStatus(contextStatus, customStatus),
hasFeedback,
),
{
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
[`${prefixCls}-affix-wrapper-borderless`]: !bordered,
// className will go to addon wrapper
[`${className}`]: !hasAddon(this.props) && className,
},
hashId,
);
return (
<span className={affixWrapperCls} style={style} hidden={hidden}>
{cloneElement(element, {
style: null,
value,
})}
{this.renderClearIcon(prefixCls)}
</span>
);
}
render() {
return (
<FormItemInputContext.Consumer>
{(statusContext) => {
const { prefixCls, inputType, element } = this.props;
if (inputType === ClearableInputType[0]) {
return this.renderTextAreaWithClearIcon(prefixCls, element, statusContext);
}
}}
</FormItemInputContext.Consumer>
);
}
}
export default ClearableLabeledInput;

View File

@ -26,67 +26,6 @@ export interface InputFocusOptions extends FocusOptions {
export type { InputRef }; export type { InputRef };
export function fixControlledValue<T>(value: T) {
if (typeof value === 'undefined' || value === null) {
return '';
}
return String(value);
}
export function resolveOnChange<E extends HTMLInputElement | HTMLTextAreaElement>(
target: E,
e:
| React.ChangeEvent<E>
| React.MouseEvent<HTMLElement, MouseEvent>
| React.CompositionEvent<HTMLElement>,
onChange: undefined | ((event: React.ChangeEvent<E>) => void),
targetValue?: string,
) {
if (!onChange) {
return;
}
let event = e as React.ChangeEvent<E>;
if (e.type === 'click') {
// Clone a new target for event.
// Avoid the following usage, the setQuery method gets the original value.
//
// const [query, setQuery] = React.useState('');
// <Input
// allowClear
// value={query}
// onChange={(e)=> {
// setQuery((prevStatus) => e.target.value);
// }}
// />
const currentTarget = target.cloneNode(true) as E;
// click clear icon
event = Object.create(e, {
target: { value: currentTarget },
currentTarget: { value: currentTarget },
});
currentTarget.value = '';
onChange(event);
return;
}
// Trigger by composition event, this means we need force change the input value
if (targetValue !== undefined) {
event = Object.create(e, {
target: { value: target },
currentTarget: { value: target },
});
target.value = targetValue;
onChange(event);
return;
}
onChange(event);
}
export function triggerFocus( export function triggerFocus(
element?: HTMLInputElement | HTMLTextAreaElement, element?: HTMLInputElement | HTMLTextAreaElement,
option?: InputFocusOptions, option?: InputFocusOptions,
@ -246,41 +185,43 @@ const Input = forwardRef<InputRef, InputProps>((props, ref) => {
</NoCompactStyle> </NoCompactStyle>
) )
} }
inputClassName={classNames( classes={{
{ input: classNames(
[`${prefixCls}-sm`]: mergedSize === 'small', {
[`${prefixCls}-lg`]: mergedSize === 'large', [`${prefixCls}-sm`]: mergedSize === 'small',
[`${prefixCls}-rtl`]: direction === 'rtl', [`${prefixCls}-lg`]: mergedSize === 'large',
[`${prefixCls}-borderless`]: !bordered, [`${prefixCls}-rtl`]: direction === 'rtl',
}, [`${prefixCls}-borderless`]: !bordered,
!inputHasPrefixSuffix && getStatusClassNames(prefixCls, mergedStatus), },
hashId, !inputHasPrefixSuffix && getStatusClassNames(prefixCls, mergedStatus),
)} hashId,
affixWrapperClassName={classNames( ),
{ affixWrapper: classNames(
[`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small', {
[`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large', [`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small',
[`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl', [`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large',
[`${prefixCls}-affix-wrapper-borderless`]: !bordered, [`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
}, [`${prefixCls}-affix-wrapper-borderless`]: !bordered,
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback), },
hashId, getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback),
)} hashId,
wrapperClassName={classNames( ),
{ wrapper: classNames(
[`${prefixCls}-group-rtl`]: direction === 'rtl', {
}, [`${prefixCls}-group-rtl`]: direction === 'rtl',
hashId, },
)} hashId,
groupClassName={classNames( ),
{ group: classNames(
[`${prefixCls}-group-wrapper-sm`]: mergedSize === 'small', {
[`${prefixCls}-group-wrapper-lg`]: mergedSize === 'large', [`${prefixCls}-group-wrapper-sm`]: mergedSize === 'small',
[`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl', [`${prefixCls}-group-wrapper-lg`]: mergedSize === 'large',
}, [`${prefixCls}-group-wrapper-rtl`]: direction === 'rtl',
getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback), },
hashId, getStatusClassNames(`${prefixCls}-group-wrapper`, mergedStatus, hasFeedback),
)} hashId,
),
}}
/>, />,
); );
}); });

View File

@ -1,91 +1,58 @@
import classNames from 'classnames'; import type { TextAreaProps as RcTextAreaProps } from 'rc-textarea/lib/interface';
import type { TextAreaProps as RcTextAreaProps } from 'rc-textarea'; import type { TextAreaRef as RcTextAreaRef } from 'rc-textarea';
import RcTextArea from 'rc-textarea'; import { forwardRef } from 'react';
import type { ResizableTextAreaRef } from 'rc-textarea/lib/ResizableTextArea';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import omit from 'rc-util/lib/omit';
import * as React from 'react'; import * as React from 'react';
import { ConfigContext } from '../config-provider'; import RcTextArea from 'rc-textarea';
import DisabledContext from '../config-provider/DisabledContext'; import classNames from 'classnames';
import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
import type { BaseInputProps } from 'rc-input/lib/interface';
import { FormItemInputContext } from '../form/context';
import useStyle from './style';
import type { SizeType } from '../config-provider/SizeContext'; import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext'; import SizeContext from '../config-provider/SizeContext';
import { FormItemInputContext } from '../form/context';
import type { InputStatus } from '../_util/statusUtils'; import type { InputStatus } from '../_util/statusUtils';
import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils'; import { getMergedStatus, getStatusClassNames } from '../_util/statusUtils';
import ClearableLabeledInput from './ClearableLabeledInput';
import type { InputFocusOptions } from './Input'; import type { InputFocusOptions } from './Input';
import { fixControlledValue, resolveOnChange, triggerFocus } from './Input'; import { triggerFocus } from './Input';
import useStyle from './style'; import DisabledContext from '../config-provider/DisabledContext';
import { ConfigContext } from '../config-provider';
interface ShowCountProps { export interface TextAreaProps extends Omit<RcTextAreaProps, 'suffix'> {
formatter: (args: { value: string; count: number; maxLength?: number }) => string;
}
function fixEmojiLength(value: string, maxLength: number) {
return [...(value || '')].slice(0, maxLength).join('');
}
function setTriggerValue(
isCursorInEnd: boolean,
preValue: string,
triggerValue: string,
maxLength: number,
) {
let newTriggerValue = triggerValue;
if (isCursorInEnd) {
// 光标在尾部,直接截断
newTriggerValue = fixEmojiLength(triggerValue, maxLength!);
} else if (
[...(preValue || '')].length < triggerValue.length &&
[...(triggerValue || '')].length > maxLength!
) {
// 光标在中间,如果最后的值超过最大值,则采用原先的值
newTriggerValue = preValue;
}
return newTriggerValue;
}
export interface TextAreaProps extends RcTextAreaProps {
allowClear?: boolean;
bordered?: boolean; bordered?: boolean;
showCount?: boolean | ShowCountProps;
size?: SizeType; size?: SizeType;
disabled?: boolean;
status?: InputStatus; status?: InputStatus;
} }
export interface TextAreaRef { export interface TextAreaRef {
focus: (options?: InputFocusOptions) => void; focus: (options?: InputFocusOptions) => void;
blur: () => void; blur: () => void;
resizableTextArea?: ResizableTextAreaRef; resizableTextArea?: RcTextAreaRef['resizableTextArea'];
} }
const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>( const TextArea = forwardRef<TextAreaRef, TextAreaProps>(
( (
{ {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
bordered = true, bordered = true,
showCount = false,
maxLength,
className,
style,
size: customizeSize, size: customizeSize,
disabled: customDisabled, disabled: customDisabled,
onCompositionStart,
onCompositionEnd,
onChange,
status: customStatus, status: customStatus,
...props allowClear,
...rest
}, },
ref, ref,
) => { ) => {
const { getPrefixCls, direction } = React.useContext(ConfigContext); const { getPrefixCls, direction } = React.useContext(ConfigContext);
// ===================== Size =====================
const size = React.useContext(SizeContext); const size = React.useContext(SizeContext);
const mergedSize = customizeSize || size;
// ===================== Disabled ===================== // ===================== Disabled =====================
const disabled = React.useContext(DisabledContext); const disabled = React.useContext(DisabledContext);
const mergedDisabled = customDisabled ?? disabled; const mergedDisabled = customDisabled ?? disabled;
// ===================== Status =====================
const { const {
status: contextStatus, status: contextStatus,
hasFeedback, hasFeedback,
@ -93,87 +60,8 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
} = React.useContext(FormItemInputContext); } = React.useContext(FormItemInputContext);
const mergedStatus = getMergedStatus(contextStatus, customStatus); const mergedStatus = getMergedStatus(contextStatus, customStatus);
const innerRef = React.useRef<RcTextArea>(null); // ===================== Ref =====================
const clearableInputRef = React.useRef<ClearableLabeledInput>(null); const innerRef = React.useRef<RcTextAreaRef>(null);
const [compositing, setCompositing] = React.useState(false);
const oldCompositionValueRef = React.useRef<string>();
const oldSelectionStartRef = React.useRef<number>(0);
const [value, setValue] = useMergedState(props.defaultValue, {
value: props.value,
});
const { hidden } = props;
const handleSetValue = (val: string, callback?: () => void) => {
if (props.value === undefined) {
setValue(val);
callback?.();
}
};
// =========================== Value Update ===========================
// Max length value
const hasMaxLength = Number(maxLength) > 0;
const onInternalCompositionStart: React.CompositionEventHandler<HTMLTextAreaElement> = (e) => {
setCompositing(true);
// 拼音输入前保存一份旧值
oldCompositionValueRef.current = value as string;
// 保存旧的光标位置
oldSelectionStartRef.current = e.currentTarget.selectionStart;
onCompositionStart?.(e);
};
const onInternalCompositionEnd: React.CompositionEventHandler<HTMLTextAreaElement> = (e) => {
setCompositing(false);
let triggerValue = e.currentTarget.value;
if (hasMaxLength) {
const isCursorInEnd =
oldSelectionStartRef.current >= maxLength! + 1 ||
oldSelectionStartRef.current === oldCompositionValueRef.current?.length;
triggerValue = setTriggerValue(
isCursorInEnd,
oldCompositionValueRef.current as string,
triggerValue,
maxLength!,
);
}
// Patch composition onChange when value changed
if (triggerValue !== value) {
handleSetValue(triggerValue);
resolveOnChange(e.currentTarget, e, onChange, triggerValue);
}
onCompositionEnd?.(e);
};
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
let triggerValue = e.target.value;
if (!compositing && hasMaxLength) {
// 1. 复制粘贴超过maxlength的情况 2.未超过maxlength的情况
const isCursorInEnd =
e.target.selectionStart >= maxLength! + 1 ||
e.target.selectionStart === triggerValue.length ||
!e.target.selectionStart;
triggerValue = setTriggerValue(isCursorInEnd, value as string, triggerValue, maxLength!);
}
handleSetValue(triggerValue);
resolveOnChange(e.currentTarget, e, onChange, triggerValue);
};
// ============================== Reset ===============================
const handleReset = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
handleSetValue('');
innerRef.current?.focus();
resolveOnChange(innerRef.current?.resizableTextArea?.textArea!, e, onChange);
};
const prefixCls = getPrefixCls('input', customizePrefixCls);
// Style
const [wrapSSR, hashId] = useStyle(prefixCls);
React.useImperativeHandle(ref, () => ({ React.useImperativeHandle(ref, () => ({
resizableTextArea: innerRef.current?.resizableTextArea, resizableTextArea: innerRef.current?.resizableTextArea,
@ -183,89 +71,58 @@ const TextArea = React.forwardRef<TextAreaRef, TextAreaProps>(
blur: () => innerRef.current?.blur(), blur: () => innerRef.current?.blur(),
})); }));
const textArea = ( const prefixCls = getPrefixCls('input', customizePrefixCls);
// Allow clear
let mergedAllowClear: BaseInputProps['allowClear'];
if (typeof allowClear === 'object' && allowClear?.clearIcon) {
mergedAllowClear = allowClear;
} else if (allowClear) {
mergedAllowClear = { clearIcon: <CloseCircleFilled /> };
}
// ===================== Style =====================
const [wrapSSR, hashId] = useStyle(prefixCls);
return wrapSSR(
<RcTextArea <RcTextArea
{...omit(props, ['allowClear'])} {...rest}
disabled={mergedDisabled} disabled={mergedDisabled}
className={classNames( allowClear={mergedAllowClear}
{ classes={{
[`${prefixCls}-borderless`]: !bordered, affixWrapper: classNames(
[className!]: className && !showCount, `${prefixCls}-textarea-affix-wrapper`,
[`${prefixCls}-sm`]: size === 'small' || customizeSize === 'small',
[`${prefixCls}-lg`]: size === 'large' || customizeSize === 'large',
},
getStatusClassNames(prefixCls, mergedStatus),
hashId,
)}
style={showCount ? { resize: style?.resize } : style}
prefixCls={prefixCls}
onCompositionStart={onInternalCompositionStart}
onChange={handleChange}
onCompositionEnd={onInternalCompositionEnd}
ref={innerRef}
/>
);
let val = fixControlledValue(value) as string;
if (!compositing && hasMaxLength && (props.value === null || props.value === undefined)) {
// fix #27612 将value转为数组进行截取解决 '😂'.length === 2 等emoji表情导致的截取乱码的问题
val = fixEmojiLength(val, maxLength!);
}
// TextArea
const textareaNode = (
<ClearableLabeledInput
disabled={mergedDisabled}
{...props}
prefixCls={prefixCls}
direction={direction}
inputType="text"
value={val}
element={textArea}
handleReset={handleReset}
ref={clearableInputRef}
bordered={bordered}
status={customStatus}
style={showCount ? undefined : style}
hashId={hashId}
/>
);
// Only show text area wrapper when needed
if (showCount || hasFeedback) {
const valueLength = [...val].length;
let dataCount = '';
if (typeof showCount === 'object') {
dataCount = showCount.formatter({ value: val, count: valueLength, maxLength });
} else {
dataCount = `${valueLength}${hasMaxLength ? ` / ${maxLength}` : ''}`;
}
return (
<div
hidden={hidden}
className={classNames(
`${prefixCls}-textarea`,
{ {
[`${prefixCls}-textarea-rtl`]: direction === 'rtl', [`${prefixCls}-affix-wrapper-rtl`]: direction === 'rtl',
[`${prefixCls}-textarea-show-count`]: showCount, [`${prefixCls}-affix-wrapper-borderless`]: !bordered,
[`${prefixCls}-affix-wrapper-sm`]: mergedSize === 'small',
[`${prefixCls}-affix-wrapper-lg`]: mergedSize === 'large',
}, },
getStatusClassNames(`${prefixCls}-textarea`, mergedStatus, hasFeedback), getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus),
className,
hashId, hashId,
)} ),
style={style} countWrapper: classNames(
data-count={dataCount} `${prefixCls}-textarea`,
> `${prefixCls}-textarea-show-count`,
{textareaNode} hashId,
{hasFeedback && <span className={`${prefixCls}-textarea-suffix`}>{feedbackIcon}</span>} ),
</div> textarea: classNames(
); {
} [`${prefixCls}-borderless`]: !bordered,
[`${prefixCls}-sm`]: mergedSize === 'small',
return wrapSSR(textareaNode); [`${prefixCls}-lg`]: mergedSize === 'large',
},
getStatusClassNames(prefixCls, mergedStatus),
hashId,
),
}}
prefixCls={prefixCls}
suffix={
hasFeedback && <span className={`${prefixCls}-textarea-suffix`}>{feedbackIcon}</span>
}
ref={innerRef}
/>,
);
}, },
); );

View File

@ -4902,31 +4902,40 @@ Array [
<br />, <br />,
<br />, <br />,
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
> >
<textarea <textarea
class="ant-input" class="ant-input"
placeholder="textarea with clear icon" placeholder="textarea with clear icon"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span>, </span>,
] ]
@ -4994,31 +5003,40 @@ exports[`renders ./components/input/demo/borderless-debug.tsx extend context cor
placeholder="Unbordered" placeholder="Unbordered"
/> />
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn ant-input-affix-wrapper-borderless" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper ant-input-affix-wrapper-borderless"
> >
<textarea <textarea
class="ant-input ant-input-borderless" class="ant-input ant-input-borderless"
placeholder="Unbordered" placeholder="Unbordered"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span> </span>
<span <span
@ -5100,31 +5118,40 @@ exports[`renders ./components/input/demo/borderless-debug.tsx extend context cor
</span> </span>
</span> </span>
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
style="border:2px solid #000" style="border:2px solid #000"
> >
<textarea <textarea
class="ant-input" class="ant-input"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span> </span>
</div> </div>
@ -9707,12 +9734,17 @@ Array [
<br />, <br />,
<br />, <br />,
<div <div
class="ant-input-textarea ant-input-textarea-show-count" class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
data-count="0 / 100" data-count="0 / 100"
> >
<textarea <textarea
class="ant-input" class="ant-input"
/> />
<span
class="ant-input-data-count"
>
0 / 100
</span>
</div>, </div>,
] ]
`; `;
@ -9970,31 +10002,40 @@ Array [
The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows. The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows. The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows. The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows.
</textarea>, </textarea>,
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
style="width:93px" style="width:93px"
> >
<textarea <textarea
class="ant-input" class="ant-input"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span>, </span>,
] ]
@ -10003,7 +10044,7 @@ Array [
exports[`renders ./components/input/demo/textarea-show-count.tsx extend context correctly 1`] = ` exports[`renders ./components/input/demo/textarea-show-count.tsx extend context correctly 1`] = `
Array [ Array [
<div <div
class="ant-input-textarea ant-input-textarea-show-count" class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
data-count="0 / 100" data-count="0 / 100"
style="height:120px;margin-bottom:24px" style="height:120px;margin-bottom:24px"
> >
@ -10011,9 +10052,14 @@ Array [
class="ant-input" class="ant-input"
placeholder="can resize" placeholder="can resize"
/> />
<span
class="ant-input-data-count"
>
0 / 100
</span>
</div>, </div>,
<div <div
class="ant-input-textarea ant-input-textarea-show-count" class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
data-count="0 / 100" data-count="0 / 100"
style="height:120px;resize:none" style="height:120px;resize:none"
> >
@ -10022,6 +10068,11 @@ Array [
placeholder="disable resize" placeholder="disable resize"
style="resize:none" style="resize:none"
/> />
<span
class="ant-input-data-count"
>
0 / 100
</span>
</div>, </div>,
] ]
`; `;

View File

@ -1103,31 +1103,40 @@ Array [
<br />, <br />,
<br />, <br />,
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
> >
<textarea <textarea
class="ant-input" class="ant-input"
placeholder="textarea with clear icon" placeholder="textarea with clear icon"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span>, </span>,
] ]
@ -1195,31 +1204,40 @@ exports[`renders ./components/input/demo/borderless-debug.tsx correctly 1`] = `
placeholder="Unbordered" placeholder="Unbordered"
/> />
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn ant-input-affix-wrapper-borderless" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper ant-input-affix-wrapper-borderless"
> >
<textarea <textarea
class="ant-input ant-input-borderless" class="ant-input ant-input-borderless"
placeholder="Unbordered" placeholder="Unbordered"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span> </span>
<span <span
@ -1301,31 +1319,40 @@ exports[`renders ./components/input/demo/borderless-debug.tsx correctly 1`] = `
</span> </span>
</span> </span>
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
style="border:2px solid #000" style="border:2px solid #000"
> >
<textarea <textarea
class="ant-input" class="ant-input"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span> </span>
</div> </div>
@ -3455,12 +3482,17 @@ Array [
<br />, <br />,
<br />, <br />,
<div <div
class="ant-input-textarea ant-input-textarea-show-count" class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
data-count="0 / 100" data-count="0 / 100"
> >
<textarea <textarea
class="ant-input" class="ant-input"
/> />
<span
class="ant-input-data-count"
>
0 / 100
</span>
</div>, </div>,
] ]
`; `;
@ -3718,31 +3750,40 @@ Array [
The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows. The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows. The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows. The autoSize property applies to textarea nodes, and only the height changes automatically. In addition, autoSize can be set to an object, specifying the minimum number of rows and the maximum number of rows.
</textarea>, </textarea>,
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
style="width:93px" style="width:93px"
> >
<textarea <textarea
class="ant-input" class="ant-input"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span>, </span>,
] ]
@ -3751,7 +3792,7 @@ Array [
exports[`renders ./components/input/demo/textarea-show-count.tsx correctly 1`] = ` exports[`renders ./components/input/demo/textarea-show-count.tsx correctly 1`] = `
Array [ Array [
<div <div
class="ant-input-textarea ant-input-textarea-show-count" class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
data-count="0 / 100" data-count="0 / 100"
style="height:120px;margin-bottom:24px" style="height:120px;margin-bottom:24px"
> >
@ -3759,9 +3800,14 @@ Array [
class="ant-input" class="ant-input"
placeholder="can resize" placeholder="can resize"
/> />
<span
class="ant-input-data-count"
>
0 / 100
</span>
</div>, </div>,
<div <div
class="ant-input-textarea ant-input-textarea-show-count" class="ant-input-show-count ant-input-textarea ant-input-textarea-show-count"
data-count="0 / 100" data-count="0 / 100"
style="height:120px;resize:none" style="height:120px;resize:none"
> >
@ -3770,6 +3816,11 @@ Array [
placeholder="disable resize" placeholder="disable resize"
style="resize:none" style="resize:none"
/> />
<span
class="ant-input-data-count"
>
0 / 100
</span>
</div>, </div>,
] ]
`; `;

View File

@ -2,7 +2,7 @@
exports[`TextArea allowClear should change type when click 1`] = ` exports[`TextArea allowClear should change type when click 1`] = `
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
> >
<textarea <textarea
class="ant-input" class="ant-input"
@ -10,234 +10,306 @@ exports[`TextArea allowClear should change type when click 1`] = `
111 111
</textarea> </textarea>
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span> </span>
`; `;
exports[`TextArea allowClear should change type when click 2`] = ` exports[`TextArea allowClear should change type when click 2`] = `
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
> >
<textarea <textarea
class="ant-input" class="ant-input"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span> </span>
`; `;
exports[`TextArea allowClear should not show icon if defaultValue is undefined, null or empty string 1`] = ` exports[`TextArea allowClear should not show icon if defaultValue is undefined, null or empty string 1`] = `
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
> >
<textarea <textarea
class="ant-input" class="ant-input"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span> </span>
`; `;
exports[`TextArea allowClear should not show icon if defaultValue is undefined, null or empty string 2`] = ` exports[`TextArea allowClear should not show icon if defaultValue is undefined, null or empty string 2`] = `
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
> >
<textarea <textarea
class="ant-input" class="ant-input"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span> </span>
`; `;
exports[`TextArea allowClear should not show icon if defaultValue is undefined, null or empty string 3`] = ` exports[`TextArea allowClear should not show icon if defaultValue is undefined, null or empty string 3`] = `
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
> >
<textarea <textarea
class="ant-input" class="ant-input"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span> </span>
`; `;
exports[`TextArea allowClear should not show icon if value is undefined, null or empty string 1`] = ` exports[`TextArea allowClear should not show icon if value is undefined, null or empty string 1`] = `
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
> >
<textarea <textarea
class="ant-input" class="ant-input"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span> </span>
`; `;
exports[`TextArea allowClear should not show icon if value is undefined, null or empty string 2`] = ` exports[`TextArea allowClear should not show icon if value is undefined, null or empty string 2`] = `
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
> >
<textarea <textarea
class="ant-input" class="ant-input"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span> </span>
`; `;
exports[`TextArea allowClear should not show icon if value is undefined, null or empty string 3`] = ` exports[`TextArea allowClear should not show icon if value is undefined, null or empty string 3`] = `
<span <span
class="ant-input-affix-wrapper ant-input-affix-wrapper-textarea-with-clear-btn" class="ant-input-affix-wrapper ant-input-textarea-affix-wrapper"
> >
<textarea <textarea
class="ant-input" class="ant-input"
/> />
<span <span
aria-label="close-circle" class="ant-input-suffix"
class="anticon anticon-close-circle ant-input-clear-icon-hidden ant-input-clear-icon"
role="button"
tabindex="-1"
> >
<svg <span
aria-hidden="true" class="ant-input-clear-icon ant-input-clear-icon-hidden"
data-icon="close-circle" role="button"
fill="currentColor" tabindex="-1"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
> >
<path <span
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" aria-label="close-circle"
/> class="anticon anticon-close-circle"
</svg> role="img"
>
<svg
aria-hidden="true"
data-icon="close-circle"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"
/>
</svg>
</span>
</span>
</span> </span>
</span> </span>
`; `;

View File

@ -574,4 +574,9 @@ describe('TextArea allowClear', () => {
textareaSpy.mockRestore(); textareaSpy.mockRestore();
}); });
it('should support custom clearIcon', () => {
const { container } = render(<TextArea allowClear={{ clearIcon: 'clear' }} />);
expect(container.querySelector('.ant-input-clear-icon')?.textContent).toBe('clear');
});
}); });

View File

@ -562,19 +562,6 @@ const genAllowClearStyle = (token: InputToken): CSSObject => {
margin: `0 ${token.inputAffixPadding}px`, margin: `0 ${token.inputAffixPadding}px`,
}, },
}, },
// ======================= TextArea ========================
'&-textarea-with-clear-btn': {
padding: '0 !important',
border: '0 !important',
[`${componentCls}-clear-icon`]: {
position: 'absolute',
insetBlockStart: token.paddingXS,
insetInlineEnd: token.paddingXS,
zIndex: 1,
},
},
}; };
}; };
@ -870,53 +857,79 @@ export function initInputToken<T extends GlobalToken = GlobalToken>(token: T): I
} }
const genTextAreaStyle: GenerateStyle<InputToken> = (token) => { const genTextAreaStyle: GenerateStyle<InputToken> = (token) => {
const { componentCls, inputPaddingHorizontal, paddingLG } = token; const { componentCls, paddingLG } = token;
const textareaPrefixCls = `${componentCls}-textarea`; const textareaPrefixCls = `${componentCls}-textarea`;
return { return {
[textareaPrefixCls]: { [textareaPrefixCls]: {
position: 'relative', position: 'relative',
[`${textareaPrefixCls}-suffix`]: {
position: 'absolute',
top: 0,
insetInlineEnd: inputPaddingHorizontal,
bottom: 0,
zIndex: 1,
display: 'inline-flex',
alignItems: 'center',
margin: 'auto',
},
[`&-status-error,
&-status-warning,
&-status-success,
&-status-validating`]: {
[`&${textareaPrefixCls}-has-feedback`]: {
[`${componentCls}`]: {
paddingInlineEnd: paddingLG,
},
},
},
'&-show-count': { '&-show-count': {
// https://github.com/ant-design/ant-design/issues/33049 // https://github.com/ant-design/ant-design/issues/33049
[`> ${componentCls}`]: { [`> ${componentCls}`]: {
height: '100%', height: '100%',
}, },
'&::after': { [`${componentCls}-data-count`]: {
color: token.colorTextDescription, color: token.colorTextDescription,
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
content: 'attr(data-count)',
pointerEvents: 'none', pointerEvents: 'none',
float: 'right', float: 'right',
marginBottom: -token.fontSize * token.lineHeight,
},
'&-rtl': {
[`${componentCls}-data-count`]: {
float: 'left',
},
}, },
}, },
'&-rtl': { [`&-affix-wrapper${textareaPrefixCls}-has-feedback`]: {
'&::after': { [`${componentCls}`]: {
float: 'left', paddingInlineEnd: paddingLG,
},
},
[`&-affix-wrapper${componentCls}-affix-wrapper`]: {
padding: 0,
[`> textarea${componentCls}`]: {
fontSize: 'inherit',
border: 'none',
outline: 'none',
'&:focus': {
boxShadow: 'none !important',
},
},
[`${componentCls}-suffix`]: {
margin: 0,
'> *:not(:last-child)': {
marginInline: 0,
},
// Clear Icon
[`${componentCls}-clear-icon`]: {
position: 'absolute',
insetInlineEnd: token.paddingXS,
insetBlockStart: token.paddingXS,
},
// Feedback Icon
[`${textareaPrefixCls}-suffix`]: {
position: 'absolute',
top: 0,
insetInlineEnd: token.inputPaddingHorizontal,
bottom: 0,
zIndex: 1,
display: 'inline-flex',
alignItems: 'center',
margin: 'auto',
pointerEvents: 'none',
},
}, },
}, },
}, },

View File

@ -1,7 +1,6 @@
import type { ClearableInputProps } from './ClearableLabeledInput';
import type { InputProps } from './Input'; import type { InputProps } from './Input';
// eslint-disable-next-line import/prefer-default-export // eslint-disable-next-line import/prefer-default-export
export function hasPrefixSuffix(props: InputProps | ClearableInputProps) { export function hasPrefixSuffix(props: InputProps) {
return !!(props.prefix || props.suffix || props.allowClear); return !!(props.prefix || props.suffix || props.allowClear);
} }

View File

@ -35,7 +35,7 @@ export interface OptionProps {
[key: string]: any; [key: string]: any;
} }
export interface MentionProps extends RcMentionsProps { export interface MentionProps extends Omit<RcMentionsProps, 'suffix'> {
loading?: boolean; loading?: boolean;
status?: InputStatus; status?: InputStatus;
options?: MentionsOptionProps[]; options?: MentionsOptionProps[];
@ -44,10 +44,6 @@ export interface MentionProps extends RcMentionsProps {
export interface MentionsRef extends RcMentionsRef {} export interface MentionsRef extends RcMentionsRef {}
export interface MentionState {
focused: boolean;
}
interface MentionsConfig { interface MentionsConfig {
prefix?: string | string[]; prefix?: string | string[];
split?: string; split?: string;
@ -179,27 +175,15 @@ const InternalMentions: React.ForwardRefRenderFunction<MentionsRef, MentionProps
dropdownClassName={classNames(popupClassName, hashId)} dropdownClassName={classNames(popupClassName, hashId)}
ref={mergedRef as any} ref={mergedRef as any}
options={mergedOptions} options={mergedOptions}
suffix={hasFeedback && feedbackIcon}
classes={{
affixWrapper: classNames(hashId, className),
}}
> >
{getOptions()} {getOptions()}
</RcMentions> </RcMentions>
); );
if (hasFeedback) {
return (
<div
className={classNames(
`${prefixCls}-affix-wrapper`,
getStatusClassNames(`${prefixCls}-affix-wrapper`, mergedStatus, hasFeedback),
className,
hashId,
)}
>
{mentions}
<span className={`${prefixCls}-suffix`}>{feedbackIcon}</span>
</div>
);
}
return wrapSSR(mentions); return wrapSSR(mentions);
}; };

View File

@ -4,7 +4,7 @@ import EditOutlined from '@ant-design/icons/EditOutlined';
import classNames from 'classnames'; import classNames from 'classnames';
import copy from 'copy-to-clipboard'; import copy from 'copy-to-clipboard';
import ResizeObserver from 'rc-resize-observer'; import ResizeObserver from 'rc-resize-observer';
import type { AutoSizeType } from 'rc-textarea/lib/ResizableTextArea'; import type { AutoSizeType } from 'rc-textarea';
import toArray from 'rc-util/lib/Children/toArray'; import toArray from 'rc-util/lib/Children/toArray';
import useIsomorphicLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect'; import useIsomorphicLayoutEffect from 'rc-util/lib/hooks/useLayoutEffect';
import useMergedState from 'rc-util/lib/hooks/useMergedState'; import useMergedState from 'rc-util/lib/hooks/useMergedState';

View File

@ -1,6 +1,6 @@
import EnterOutlined from '@ant-design/icons/EnterOutlined'; import EnterOutlined from '@ant-design/icons/EnterOutlined';
import classNames from 'classnames'; import classNames from 'classnames';
import type { AutoSizeType } from 'rc-textarea/lib/ResizableTextArea'; import type { AutoSizeType } from 'rc-textarea';
import KeyCode from 'rc-util/lib/KeyCode'; import KeyCode from 'rc-util/lib/KeyCode';
import * as React from 'react'; import * as React from 'react';
import type { DirectionType } from '../config-provider'; import type { DirectionType } from '../config-provider';

View File

@ -127,9 +127,9 @@
"rc-dropdown": "~4.0.0", "rc-dropdown": "~4.0.0",
"rc-field-form": "~1.27.0", "rc-field-form": "~1.27.0",
"rc-image": "~5.13.0", "rc-image": "~5.13.0",
"rc-input": "~0.1.4", "rc-input": "~0.2.1",
"rc-input-number": "~7.4.0", "rc-input-number": "~7.4.0",
"rc-mentions": "~1.13.1", "rc-mentions": "~2.0.0",
"rc-menu": "~9.8.0", "rc-menu": "~9.8.0",
"rc-motion": "^2.6.1", "rc-motion": "^2.6.1",
"rc-notification": "~5.0.0", "rc-notification": "~5.0.0",
@ -145,7 +145,7 @@
"rc-switch": "~4.0.0", "rc-switch": "~4.0.0",
"rc-table": "~7.30.2", "rc-table": "~7.30.2",
"rc-tabs": "~12.5.1", "rc-tabs": "~12.5.1",
"rc-textarea": "~0.4.5", "rc-textarea": "~1.0.0",
"rc-tooltip": "~5.2.0", "rc-tooltip": "~5.2.0",
"rc-tree": "~5.7.0", "rc-tree": "~5.7.0",
"rc-tree-select": "~5.6.0", "rc-tree-select": "~5.6.0",