ant-design/components/input/TextArea.tsx

171 lines
4.5 KiB
TypeScript
Raw Normal View History

import * as React from 'react';
2017-05-22 14:44:58 +08:00
import omit from 'omit.js';
import classNames from 'classnames';
import { polyfill } from 'react-lifecycles-compat';
import ResizeObserver from 'resize-observer-polyfill';
2017-05-22 14:44:58 +08:00
import calculateNodeHeight from './calculateNodeHeight';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
2017-05-22 14:44:58 +08:00
2017-11-21 19:51:31 +08:00
function onNextFrame(cb: () => void) {
2017-05-22 14:44:58 +08:00
if (window.requestAnimationFrame) {
return window.requestAnimationFrame(cb);
}
return window.setTimeout(cb, 1);
}
2017-11-21 19:51:31 +08:00
function clearNextFrameAction(nextFrameId: number) {
2017-05-22 14:44:58 +08:00
if (window.cancelAnimationFrame) {
window.cancelAnimationFrame(nextFrameId);
} else {
window.clearTimeout(nextFrameId);
}
}
export interface AutoSizeType {
minRows?: number;
maxRows?: number;
}
export type HTMLTextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
export interface TextAreaProps extends HTMLTextareaProps {
prefixCls?: string;
2017-05-22 14:44:58 +08:00
autosize?: boolean | AutoSizeType;
onPressEnter?: React.KeyboardEventHandler<HTMLTextAreaElement>;
2017-05-22 14:44:58 +08:00
}
export interface TextAreaState {
textareaStyles?: React.CSSProperties;
}
class TextArea extends React.Component<TextAreaProps, TextAreaState> {
2017-05-22 14:44:58 +08:00
nextFrameActionId: number;
resizeObserver: ResizeObserver | null;
2017-09-17 15:48:44 +08:00
2017-05-22 14:44:58 +08:00
state = {
textareaStyles: {},
2017-05-22 14:44:58 +08:00
};
2017-09-17 15:48:44 +08:00
private textAreaRef: HTMLTextAreaElement;
2017-05-22 14:44:58 +08:00
componentDidMount() {
this.resizeTextarea();
this.updateResizeObserverHook();
2017-05-22 14:44:58 +08:00
}
componentDidUpdate(prevProps: TextAreaProps) {
2017-05-22 14:44:58 +08:00
// Re-render with the new content then recalculate the height as required.
if (prevProps.value !== this.props.value) {
this.resizeOnNextFrame();
}
this.updateResizeObserverHook();
}
componentWillUnmount() {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
}
resizeOnNextFrame = () => {
if (this.nextFrameActionId) {
clearNextFrameAction(this.nextFrameActionId);
}
this.nextFrameActionId = onNextFrame(this.resizeTextarea);
2018-12-07 20:02:01 +08:00
};
// We will update hooks if `autosize` prop change
updateResizeObserverHook() {
if (!this.resizeObserver && this.props.autosize) {
// Add resize observer
this.resizeObserver = new ResizeObserver(this.resizeOnNextFrame);
this.resizeObserver.observe(this.textAreaRef);
} else if (this.resizeObserver && !this.props.autosize) {
// Remove resize observer
this.resizeObserver.disconnect();
this.resizeObserver = null;
2017-05-22 14:44:58 +08:00
}
}
focus() {
this.textAreaRef.focus();
}
blur() {
this.textAreaRef.blur();
}
resizeTextarea = () => {
const { autosize } = this.props;
if (!autosize || !this.textAreaRef) {
return;
}
const minRows = autosize ? (autosize as AutoSizeType).minRows : null;
const maxRows = autosize ? (autosize as AutoSizeType).maxRows : null;
const textareaStyles = calculateNodeHeight(this.textAreaRef, false, minRows, maxRows);
this.setState({ textareaStyles });
2018-12-07 20:02:01 +08:00
};
2017-05-22 14:44:58 +08:00
2017-11-21 19:51:31 +08:00
handleTextareaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
2017-05-22 14:44:58 +08:00
if (!('value' in this.props)) {
this.resizeTextarea();
}
const { onChange } = this.props;
if (onChange) {
onChange(e);
}
2018-12-07 20:02:01 +08:00
};
2017-05-22 14:44:58 +08:00
2017-11-21 19:51:31 +08:00
handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
2017-05-22 14:44:58 +08:00
const { onPressEnter, onKeyDown } = this.props;
if (e.keyCode === 13 && onPressEnter) {
onPressEnter(e);
}
if (onKeyDown) {
onKeyDown(e);
}
2018-12-07 20:02:01 +08:00
};
2017-05-22 14:44:58 +08:00
2017-11-21 19:51:31 +08:00
saveTextAreaRef = (textArea: HTMLTextAreaElement) => {
2017-05-22 14:44:58 +08:00
this.textAreaRef = textArea;
2018-12-07 20:02:01 +08:00
};
2017-05-22 14:44:58 +08:00
renderTextArea = ({ getPrefixCls }: ConfigConsumerProps) => {
const { prefixCls: customizePrefixCls, className, disabled } = this.props;
const { ...props } = this.props;
2018-12-07 20:02:01 +08:00
const otherProps = omit(props, ['prefixCls', 'onPressEnter', 'autosize']);
const prefixCls = getPrefixCls('input', customizePrefixCls);
const cls = classNames(prefixCls, className, {
[`${prefixCls}-disabled`]: disabled,
});
2017-05-22 14:44:58 +08:00
const style = {
...props.style,
...this.state.textareaStyles,
};
// Fix https://github.com/ant-design/ant-design/issues/6776
// Make sure it could be reset when using form.getFieldDecorator
if ('value' in otherProps) {
otherProps.value = otherProps.value || '';
}
2017-05-22 14:44:58 +08:00
return (
<textarea
{...otherProps}
className={cls}
2017-05-22 14:44:58 +08:00
style={style}
onKeyDown={this.handleKeyDown}
onChange={this.handleTextareaChange}
ref={this.saveTextAreaRef}
/>
);
2018-12-07 20:02:01 +08:00
};
render() {
2018-12-07 20:02:01 +08:00
return <ConfigConsumer>{this.renderTextArea}</ConfigConsumer>;
}
2017-05-22 14:44:58 +08:00
}
polyfill(TextArea);
export default TextArea;