mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-28 21:19:37 +08:00
767d2d0638
* add allow clear to textarea revert textarea md refactor textarea css fix textarea props bug add test and update snapshot update doc add margin refactor textarea to reuse fixControlledValue and setValue method add clearable input to reuse code refactor clearable input finish refactor refactor clearable input refactor input and textArea finish refactor update test snapshots fix input props bug fix dom issue fix textarea dom issue fix textarea test bug fix textarea dom issue refactor code and fix textarea dom issue fix input dom issue fix typo error update test snapshots fix ci failed issues adjust margin * remove useless code fix ci failed issue * fix textarea padding issue * update snapshots * fix padding issue
252 lines
6.7 KiB
TypeScript
252 lines
6.7 KiB
TypeScript
import * as React from 'react';
|
|
import * as PropTypes from 'prop-types';
|
|
import { polyfill } from 'react-lifecycles-compat';
|
|
import classNames from 'classnames';
|
|
import omit from 'omit.js';
|
|
import Group from './Group';
|
|
import Search from './Search';
|
|
import TextArea from './TextArea';
|
|
import Password from './Password';
|
|
import { Omit, tuple } from '../_util/type';
|
|
import ClearableLabeledInput, { hasPrefixSuffix } from './ClearableLabeledInput';
|
|
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
|
import warning from '../_util/warning';
|
|
|
|
export const InputSizes = tuple('small', 'default', 'large');
|
|
|
|
export interface InputProps
|
|
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size' | 'prefix'> {
|
|
prefixCls?: string;
|
|
size?: (typeof InputSizes)[number];
|
|
onPressEnter?: React.KeyboardEventHandler<HTMLInputElement>;
|
|
addonBefore?: React.ReactNode;
|
|
addonAfter?: React.ReactNode;
|
|
prefix?: React.ReactNode;
|
|
suffix?: React.ReactNode;
|
|
allowClear?: boolean;
|
|
}
|
|
|
|
export function fixControlledValue<T>(value: T) {
|
|
if (typeof value === 'undefined' || value === null) {
|
|
return '';
|
|
}
|
|
return value;
|
|
}
|
|
|
|
export function resolveOnChange(
|
|
target: HTMLInputElement | HTMLTextAreaElement,
|
|
e:
|
|
| React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
|
|
| React.MouseEvent<HTMLElement, MouseEvent>,
|
|
onChange?: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void,
|
|
) {
|
|
if (onChange) {
|
|
let event = e;
|
|
if (e.type === 'click') {
|
|
// click clear icon
|
|
event = Object.create(e);
|
|
event.target = target;
|
|
event.currentTarget = target;
|
|
const originalInputValue = target.value;
|
|
// change target ref value cause e.target.value should be '' when clear input
|
|
target.value = '';
|
|
onChange(event as React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>);
|
|
// reset target ref value
|
|
target.value = originalInputValue;
|
|
return;
|
|
}
|
|
onChange(event as React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>);
|
|
}
|
|
}
|
|
|
|
export function getInputClassName(
|
|
prefixCls: string,
|
|
size?: (typeof InputSizes)[number],
|
|
disabled?: boolean,
|
|
) {
|
|
return classNames(prefixCls, {
|
|
[`${prefixCls}-sm`]: size === 'small',
|
|
[`${prefixCls}-lg`]: size === 'large',
|
|
[`${prefixCls}-disabled`]: disabled,
|
|
});
|
|
}
|
|
|
|
export interface InputState {
|
|
value: any;
|
|
}
|
|
|
|
class Input extends React.Component<InputProps, InputState> {
|
|
static Group: typeof Group;
|
|
|
|
static Search: typeof Search;
|
|
|
|
static TextArea: typeof TextArea;
|
|
|
|
static Password: typeof Password;
|
|
|
|
static defaultProps = {
|
|
type: 'text',
|
|
};
|
|
|
|
static propTypes = {
|
|
type: PropTypes.string,
|
|
id: PropTypes.string,
|
|
size: PropTypes.oneOf(InputSizes),
|
|
maxLength: PropTypes.number,
|
|
disabled: PropTypes.bool,
|
|
value: PropTypes.any,
|
|
defaultValue: PropTypes.any,
|
|
className: PropTypes.string,
|
|
addonBefore: PropTypes.node,
|
|
addonAfter: PropTypes.node,
|
|
prefixCls: PropTypes.string,
|
|
onPressEnter: PropTypes.func,
|
|
onKeyDown: PropTypes.func,
|
|
onKeyUp: PropTypes.func,
|
|
onFocus: PropTypes.func,
|
|
onBlur: PropTypes.func,
|
|
prefix: PropTypes.node,
|
|
suffix: PropTypes.node,
|
|
allowClear: PropTypes.bool,
|
|
};
|
|
|
|
input: HTMLInputElement;
|
|
|
|
clearableInput: ClearableLabeledInput;
|
|
|
|
constructor(props: InputProps) {
|
|
super(props);
|
|
const value = typeof props.value === 'undefined' ? props.defaultValue : props.value;
|
|
this.state = {
|
|
value,
|
|
};
|
|
}
|
|
|
|
static getDerivedStateFromProps(nextProps: InputProps) {
|
|
if ('value' in nextProps) {
|
|
return {
|
|
value: nextProps.value,
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Since polyfill `getSnapshotBeforeUpdate` need work with `componentDidUpdate`.
|
|
// We keep an empty function here.
|
|
componentDidUpdate() {}
|
|
|
|
getSnapshotBeforeUpdate(prevProps: InputProps) {
|
|
if (hasPrefixSuffix(prevProps) !== hasPrefixSuffix(this.props)) {
|
|
warning(
|
|
this.input !== document.activeElement,
|
|
'Input',
|
|
`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`,
|
|
);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
focus() {
|
|
this.input.focus();
|
|
}
|
|
|
|
blur() {
|
|
this.input.blur();
|
|
}
|
|
|
|
select() {
|
|
this.input.select();
|
|
}
|
|
|
|
saveClearableInput = (input: ClearableLabeledInput) => {
|
|
this.clearableInput = input;
|
|
};
|
|
|
|
saveInput = (input: HTMLInputElement) => {
|
|
this.input = input;
|
|
};
|
|
|
|
setValue(value: string, callback?: () => void) {
|
|
if (!('value' in this.props)) {
|
|
this.setState({ value }, callback);
|
|
}
|
|
}
|
|
|
|
handleReset = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
|
this.setValue('', () => {
|
|
this.focus();
|
|
});
|
|
resolveOnChange(this.input, e, this.props.onChange);
|
|
};
|
|
|
|
renderInput = (prefixCls: string) => {
|
|
const { className, addonBefore, addonAfter, size, disabled } = this.props;
|
|
// Fix https://fb.me/react-unknown-prop
|
|
const otherProps = omit(this.props, [
|
|
'prefixCls',
|
|
'onPressEnter',
|
|
'addonBefore',
|
|
'addonAfter',
|
|
'prefix',
|
|
'suffix',
|
|
'allowClear',
|
|
// Input elements must be either controlled or uncontrolled,
|
|
// specify either the value prop, or the defaultValue prop, but not both.
|
|
'defaultValue',
|
|
'size',
|
|
'inputType',
|
|
]);
|
|
return (
|
|
<input
|
|
{...otherProps}
|
|
onChange={this.handleChange}
|
|
onKeyDown={this.handleKeyDown}
|
|
className={classNames(getInputClassName(prefixCls, size, disabled), {
|
|
[className!]: className && !addonBefore && !addonAfter,
|
|
})}
|
|
ref={this.saveInput}
|
|
/>
|
|
);
|
|
};
|
|
|
|
handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
this.setValue(e.target.value);
|
|
resolveOnChange(this.input, e, this.props.onChange);
|
|
};
|
|
|
|
handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
const { onPressEnter, onKeyDown } = this.props;
|
|
if (e.keyCode === 13 && onPressEnter) {
|
|
onPressEnter(e);
|
|
}
|
|
if (onKeyDown) {
|
|
onKeyDown(e);
|
|
}
|
|
};
|
|
|
|
renderComponent = ({ getPrefixCls }: ConfigConsumerProps) => {
|
|
const { value } = this.state;
|
|
const { prefixCls: customizePrefixCls } = this.props;
|
|
const prefixCls = getPrefixCls('input', customizePrefixCls);
|
|
return (
|
|
<ClearableLabeledInput
|
|
{...this.props}
|
|
prefixCls={prefixCls}
|
|
inputType="input"
|
|
value={fixControlledValue(value)}
|
|
element={this.renderInput(prefixCls)}
|
|
handleReset={this.handleReset}
|
|
ref={this.saveClearableInput}
|
|
/>
|
|
);
|
|
};
|
|
|
|
render() {
|
|
return <ConfigConsumer>{this.renderComponent}</ConfigConsumer>;
|
|
}
|
|
}
|
|
|
|
polyfill(Input);
|
|
|
|
export default Input;
|