1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
import React from 'react';
import PropTypes from 'prop-types';
import autosize from 'autosize';
import getLineHeight from 'line-height';
const UPDATE = 'autosize:update',
DESTROY = 'autosize:destroy',
RESIZED = 'autosize:resized';
/** A light replacement for built-in textarea component
* which automaticaly adjusts its height to match the content
* @param onResize - called whenever the textarea resizes
* @param rows - minimum number of visible rows
* @param maxRows - maximum number of visible rows
* @param innerRef - called with the ref to the DOM node
*/
export default class TextareaAutosize extends React.Component {
static defaultProps = {
rows: 1
};
state = {
lineHeight: null
}
componentDidMount() {
const { onResize, maxRows } = this.props;
if (typeof maxRows === 'number') {
this.updateLineHeight();
// this trick is needed to force "autosize" to activate the scrollbar
setTimeout(() => autosize(this.textarea));
} else {
autosize(this.textarea);
}
if (onResize) {
this.textarea.addEventListener(RESIZED, this.props.onResize);
}
}
componentWillUnmount() {
if (this.props.onResize) {
this.textarea.removeEventListener(RESIZED, this.props.onResize);
}
this.dispatchEvent(DESTROY);
}
dispatchEvent = (EVENT_TYPE) => {
const event = document.createEvent('Event');
event.initEvent(EVENT_TYPE, true, false);
this.textarea.dispatchEvent(event);
};
getValue = ({ valueLink, value }) => valueLink ? valueLink.value : value;
updateLineHeight = () => {
this.setState({
lineHeight: getLineHeight(this.textarea)
});
}
onChange = e => {
this.currentValue = e.target.value;
this.props.onChange && this.props.onChange(e);
}
saveDOMNodeRef = ref => {
const { innerRef } = this.props;
if (innerRef) {
innerRef(ref);
}
this.textarea = ref;
}
getLocals = () => {
const {
props: { onResize, maxRows, onChange, style, innerRef, ...props }, // eslint-disable-line no-unused-vars
state: { lineHeight },
saveDOMNodeRef
} = this;
const maxHeight = maxRows && lineHeight ? lineHeight * maxRows : null;
return {
...props,
saveDOMNodeRef,
style: maxHeight ? { ...style, maxHeight } : style,
onChange: this.onChange
};
}
render() {
const { children, saveDOMNodeRef, ...locals } = this.getLocals();
return (
<textarea {...locals} ref={saveDOMNodeRef}>
{children}
</textarea>
);
}
componentDidUpdate() {
if (this.getValue(this.props) !== this.currentValue) {
this.dispatchEvent(UPDATE);
}
}
}
TextareaAutosize.propTypes = {
rows: PropTypes.number,
maxRows: PropTypes.number,
onResize: PropTypes.func,
innerRef: PropTypes.func
};
|