diff --git a/components/input/Input.tsx b/components/input/Input.tsx index 7effb5ff2f..ff128a7541 100644 --- a/components/input/Input.tsx +++ b/components/input/Input.tsx @@ -7,6 +7,7 @@ import Search from './Search'; import TextArea from './TextArea'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import Password from './Password'; +import Icon from '../icon'; import { Omit, tuple } from '../_util/type'; function fixControlledValue(value: T) { @@ -27,6 +28,7 @@ export interface InputProps addonAfter?: React.ReactNode; prefix?: React.ReactNode; suffix?: React.ReactNode; + allowClear?: Boolean; } export default class Input extends React.Component { @@ -38,6 +40,7 @@ export default class Input extends React.Component { static defaultProps = { type: 'text', disabled: false, + allowClear: false, }; static propTypes = { @@ -59,6 +62,11 @@ export default class Input extends React.Component { onBlur: PropTypes.func, prefix: PropTypes.node, suffix: PropTypes.node, + allowClear: PropTypes.bool, + }; + + state = { + value: '', }; input: HTMLInputElement; @@ -98,6 +106,64 @@ export default class Input extends React.Component { this.input = node; }; + setValue( + value: string, + e: React.ChangeEvent | React.MouseEvent, + ) { + const { onChange } = this.props; + if (!('value' in this.props)) { + this.setState({ value }); + } + if (onChange) { + onChange( + e.target + ? (e as React.ChangeEvent) + : { + ...e, + target: this.input, + currentTarget: this.input, + }, + ); + } + } + + handleReset = (e: React.MouseEvent) => { + this.setValue('', e); + }; + + handleChange = (e: React.ChangeEvent) => { + this.setValue(e.target.value, e); + }; + + renderClearIcon(prefixCls: string) { + const { allowClear } = this.props; + const { value } = this.state; + if (!allowClear || value === undefined || value === '') { + return null; + } + return ( + + ); + } + + renderSuffix(prefixCls: string) { + const { suffix, allowClear } = this.props; + if (suffix || allowClear) { + return ( + + {this.renderClearIcon(prefixCls)} + {suffix} + + ); + } + return null; + } + renderLabeledInput(prefixCls: string, children: React.ReactElement) { const props = this.props; // Not wrap when there is not addons @@ -110,7 +176,6 @@ export default class Input extends React.Component { const addonBefore = props.addonBefore ? ( {props.addonBefore} ) : null; - const addonAfter = props.addonAfter ? ( {props.addonAfter} ) : null; @@ -139,7 +204,9 @@ export default class Input extends React.Component { renderLabeledIcon(prefixCls: string, children: React.ReactElement) { const { props } = this; - if (!('prefix' in props || 'suffix' in props)) { + const suffix = this.renderSuffix(prefixCls); + + if (!('prefix' in props) && !suffix) { return children; } @@ -147,10 +214,6 @@ export default class Input extends React.Component { {props.prefix} ) : null; - const suffix = props.suffix ? ( - {props.suffix} - ) : null; - const affixWrapperCls = classNames(props.className, `${prefixCls}-affix-wrapper`, { [`${prefixCls}-affix-wrapper-sm`]: props.size === 'small', [`${prefixCls}-affix-wrapper-lg`]: props.size === 'large', @@ -168,7 +231,8 @@ export default class Input extends React.Component { } renderInput(prefixCls: string) { - const { value, className } = this.props; + const { className } = this.props; + const { value } = this.state; // Fix https://fb.me/react-unknown-prop const otherProps = omit(this.props, [ 'prefixCls', @@ -177,6 +241,7 @@ export default class Input extends React.Component { 'addonAfter', 'prefix', 'suffix', + 'allowClear', ]); if ('value' in this.props) { @@ -189,6 +254,8 @@ export default class Input extends React.Component { prefixCls, `; +exports[`renders ./components/input/demo/allowClear.md correctly 1`] = ` + + + + +`; + exports[`renders ./components/input/demo/autosize-textarea.md correctly 1`] = `