2017-11-17 14:38:54 +08:00
|
|
|
import * as React from 'react';
|
2020-06-13 14:24:56 +08:00
|
|
|
import RcTextArea, { TextAreaProps as RcTextAreaProps, ResizableTextArea } from 'rc-textarea';
|
|
|
|
import omit from 'omit.js';
|
2020-07-16 00:25:47 +08:00
|
|
|
import classNames from 'classnames';
|
2019-11-01 18:19:29 +08:00
|
|
|
import ClearableLabeledInput from './ClearableLabeledInput';
|
2018-12-05 19:12:18 +08:00
|
|
|
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
2019-11-01 18:19:29 +08:00
|
|
|
import { fixControlledValue, resolveOnChange } from './Input';
|
2020-10-21 14:31:16 +08:00
|
|
|
import SizeContext, { SizeType } from '../config-provider/SizeContext';
|
2017-05-22 14:44:58 +08:00
|
|
|
|
2020-06-13 14:24:56 +08:00
|
|
|
export interface TextAreaProps extends RcTextAreaProps {
|
2019-11-01 18:19:29 +08:00
|
|
|
allowClear?: boolean;
|
2020-07-16 00:25:47 +08:00
|
|
|
bordered?: boolean;
|
2020-10-10 11:30:58 +08:00
|
|
|
showCount?: boolean;
|
|
|
|
maxLength?: number;
|
2020-10-12 20:39:51 +08:00
|
|
|
size?: SizeType;
|
2017-05-22 14:44:58 +08:00
|
|
|
}
|
|
|
|
|
2017-11-01 11:47:22 +08:00
|
|
|
export interface TextAreaState {
|
2019-11-01 18:19:29 +08:00
|
|
|
value: any;
|
2020-09-09 14:41:00 +08:00
|
|
|
/** `value` from prev props */
|
|
|
|
prevValue: any;
|
2017-11-01 11:47:22 +08:00
|
|
|
}
|
|
|
|
|
2018-11-27 13:46:42 +08:00
|
|
|
class TextArea extends React.Component<TextAreaProps, TextAreaState> {
|
2019-11-01 18:19:29 +08:00
|
|
|
resizableTextArea: ResizableTextArea;
|
2019-08-05 18:38:10 +08:00
|
|
|
|
2019-11-01 18:19:29 +08:00
|
|
|
clearableInput: ClearableLabeledInput;
|
2017-09-17 15:48:44 +08:00
|
|
|
|
2019-11-01 18:19:29 +08:00
|
|
|
constructor(props: TextAreaProps) {
|
|
|
|
super(props);
|
|
|
|
const value = typeof props.value === 'undefined' ? props.defaultValue : props.value;
|
|
|
|
this.state = {
|
|
|
|
value,
|
2020-09-09 14:41:00 +08:00
|
|
|
// eslint-disable-next-line react/no-unused-state
|
|
|
|
prevValue: props.value,
|
2019-11-01 18:19:29 +08:00
|
|
|
};
|
|
|
|
}
|
2017-09-17 15:48:44 +08:00
|
|
|
|
2020-09-09 14:41:00 +08:00
|
|
|
static getDerivedStateFromProps(nextProps: TextAreaProps, { prevValue }: TextAreaState) {
|
|
|
|
const newState: Partial<TextAreaState> = { prevValue: nextProps.value };
|
|
|
|
if (nextProps.value !== undefined || prevValue !== nextProps.value) {
|
|
|
|
newState.value = nextProps.value;
|
2019-11-01 18:19:29 +08:00
|
|
|
}
|
2020-09-09 14:41:00 +08:00
|
|
|
return newState;
|
2017-05-22 14:44:58 +08:00
|
|
|
}
|
|
|
|
|
2019-11-01 18:19:29 +08:00
|
|
|
setValue(value: string, callback?: () => void) {
|
2020-08-21 22:14:28 +08:00
|
|
|
if (this.props.value === undefined) {
|
2019-11-01 18:19:29 +08:00
|
|
|
this.setState({ value }, callback);
|
2018-11-27 13:46:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-17 19:12:42 +08:00
|
|
|
focus = () => {
|
2019-11-01 18:19:29 +08:00
|
|
|
this.resizableTextArea.textArea.focus();
|
2020-02-17 19:12:42 +08:00
|
|
|
};
|
2019-11-01 18:19:29 +08:00
|
|
|
|
|
|
|
blur() {
|
|
|
|
this.resizableTextArea.textArea.blur();
|
2019-08-06 15:29:13 +08:00
|
|
|
}
|
|
|
|
|
2020-06-13 14:24:56 +08:00
|
|
|
saveTextArea = (textarea: RcTextArea) => {
|
|
|
|
this.resizableTextArea = textarea?.resizableTextArea;
|
2018-12-07 20:02:01 +08:00
|
|
|
};
|
2017-05-22 14:44:58 +08:00
|
|
|
|
2019-11-01 18:19:29 +08:00
|
|
|
saveClearableInput = (clearableInput: ClearableLabeledInput) => {
|
|
|
|
this.clearableInput = clearableInput;
|
|
|
|
};
|
|
|
|
|
|
|
|
handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
2020-06-13 14:24:56 +08:00
|
|
|
this.setValue(e.target.value);
|
2019-11-01 18:19:29 +08:00
|
|
|
resolveOnChange(this.resizableTextArea.textArea, e, this.props.onChange);
|
2018-12-07 20:02:01 +08:00
|
|
|
};
|
2017-05-22 14:44:58 +08:00
|
|
|
|
2019-11-01 18:19:29 +08:00
|
|
|
handleReset = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
|
|
|
this.setValue('', () => {
|
|
|
|
this.focus();
|
2019-08-05 18:38:10 +08:00
|
|
|
});
|
2019-11-01 18:19:29 +08:00
|
|
|
resolveOnChange(this.resizableTextArea.textArea, e, this.props.onChange);
|
2018-12-07 20:02:01 +08:00
|
|
|
};
|
2017-05-22 14:44:58 +08:00
|
|
|
|
2020-10-12 20:39:51 +08:00
|
|
|
renderTextArea = (prefixCls: string, bordered: boolean, size?: SizeType) => {
|
2020-10-21 14:31:16 +08:00
|
|
|
const { showCount, className, style, size: customizeSize } = this.props;
|
2020-10-19 14:03:19 +08:00
|
|
|
|
2019-11-01 18:19:29 +08:00
|
|
|
return (
|
2020-06-13 14:24:56 +08:00
|
|
|
<RcTextArea
|
2020-10-12 20:39:51 +08:00
|
|
|
{...omit(this.props, ['allowClear', 'bordered', 'showCount', 'size'])}
|
2020-10-19 14:03:19 +08:00
|
|
|
className={classNames({
|
|
|
|
[`${prefixCls}-borderless`]: !bordered,
|
|
|
|
[className!]: className && !showCount,
|
2020-10-21 14:31:16 +08:00
|
|
|
[`${prefixCls}-sm`]: size === 'small' || customizeSize === 'small',
|
|
|
|
[`${prefixCls}-lg`]: size === 'large' || customizeSize === 'large',
|
2020-10-19 14:03:19 +08:00
|
|
|
})}
|
|
|
|
style={showCount ? null : style}
|
2019-11-01 18:19:29 +08:00
|
|
|
prefixCls={prefixCls}
|
|
|
|
onChange={this.handleChange}
|
|
|
|
ref={this.saveTextArea}
|
|
|
|
/>
|
2019-10-11 18:12:28 +08:00
|
|
|
);
|
2019-11-01 18:19:29 +08:00
|
|
|
};
|
2019-10-11 18:12:28 +08:00
|
|
|
|
2020-05-09 18:02:08 +08:00
|
|
|
renderComponent = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
2020-10-10 11:30:58 +08:00
|
|
|
let value = fixControlledValue(this.state?.value);
|
|
|
|
const {
|
|
|
|
prefixCls: customizePrefixCls,
|
|
|
|
bordered = true,
|
|
|
|
showCount = false,
|
|
|
|
maxLength,
|
2020-10-19 14:03:19 +08:00
|
|
|
className,
|
|
|
|
style,
|
2020-10-10 11:30:58 +08:00
|
|
|
} = this.props;
|
2020-10-19 14:03:19 +08:00
|
|
|
|
2019-11-01 18:19:29 +08:00
|
|
|
const prefixCls = getPrefixCls('input', customizePrefixCls);
|
2020-10-19 14:03:19 +08:00
|
|
|
|
|
|
|
// Max length value
|
2020-10-10 11:30:58 +08:00
|
|
|
const hasMaxLength = Number(maxLength) > 0;
|
|
|
|
value = hasMaxLength ? value.slice(0, maxLength) : value;
|
|
|
|
|
2020-10-19 14:03:19 +08:00
|
|
|
// TextArea
|
2020-10-21 14:31:16 +08:00
|
|
|
let textareaNode = (size?: SizeType) => (
|
2020-10-19 14:03:19 +08:00
|
|
|
<ClearableLabeledInput
|
|
|
|
{...this.props}
|
|
|
|
prefixCls={prefixCls}
|
|
|
|
direction={direction}
|
|
|
|
inputType="text"
|
|
|
|
value={value}
|
2020-10-21 14:31:16 +08:00
|
|
|
element={this.renderTextArea(prefixCls, bordered, size)}
|
2020-10-19 14:03:19 +08:00
|
|
|
handleReset={this.handleReset}
|
|
|
|
ref={this.saveClearableInput}
|
|
|
|
triggerFocus={this.focus}
|
|
|
|
bordered={bordered}
|
|
|
|
/>
|
2017-05-22 14:44:58 +08:00
|
|
|
);
|
2020-10-19 14:03:19 +08:00
|
|
|
|
|
|
|
// Only show text area wrapper when needed
|
|
|
|
if (showCount) {
|
|
|
|
const valueLength = [...value].length;
|
|
|
|
const dataCount = `${valueLength}${hasMaxLength ? ` / ${maxLength}` : ''}`;
|
|
|
|
|
2020-10-21 14:31:16 +08:00
|
|
|
textareaNode = (size?: SizeType) => (
|
2020-10-19 14:03:19 +08:00
|
|
|
<div
|
|
|
|
className={classNames(
|
|
|
|
`${prefixCls}-textarea`,
|
|
|
|
{
|
|
|
|
[`${prefixCls}-textarea-rtl`]: direction === 'rtl',
|
|
|
|
},
|
|
|
|
`${prefixCls}-textarea-show-count`,
|
|
|
|
className,
|
|
|
|
)}
|
|
|
|
style={style}
|
|
|
|
data-count={dataCount}
|
|
|
|
>
|
2020-10-21 14:31:16 +08:00
|
|
|
{textareaNode(size)}
|
2020-10-19 14:03:19 +08:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-10-21 14:31:16 +08:00
|
|
|
return <SizeContext.Consumer>{textareaNode}</SizeContext.Consumer>;
|
2018-12-07 20:02:01 +08:00
|
|
|
};
|
2018-12-05 19:12:18 +08:00
|
|
|
|
|
|
|
render() {
|
2019-11-01 18:19:29 +08:00
|
|
|
return <ConfigConsumer>{this.renderComponent}</ConfigConsumer>;
|
2018-12-05 19:12:18 +08:00
|
|
|
}
|
2017-05-22 14:44:58 +08:00
|
|
|
}
|
2018-11-27 13:46:42 +08:00
|
|
|
|
|
|
|
export default TextArea;
|