summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGabriele Petronella <gabriele@buildo.io>2017-01-22 19:40:57 +0100
committerGitHub <noreply@github.com>2017-01-22 19:40:57 +0100
commit82071950413385e7b9d59fc1bf177c5d937410b5 (patch)
tree5f4f0ec49f1d1209a9bddbf276a844b5825b73f8
parentc78d7c6da77031d7810f5bc5b2b0d54021b0895e (diff)
parent2925810519bf6e25cfaee94954e4858f20bb4529 (diff)
downloadreact-autosize-textarea-82071950413385e7b9d59fc1bf177c5d937410b5.zip
react-autosize-textarea-82071950413385e7b9d59fc1bf177c5d937410b5.tar.gz
react-autosize-textarea-82071950413385e7b9d59fc1bf177c5d937410b5.tar.bz2
Merge pull request #26 from buildo/25-max_rows
#25: Max Rows (closes #25)
-rw-r--r--examples/examples.js11
-rw-r--r--src/TextareaAutosize.js86
2 files changed, 93 insertions, 4 deletions
diff --git a/examples/examples.js b/examples/examples.js
index 622268d..c79bf11 100644
--- a/examples/examples.js
+++ b/examples/examples.js
@@ -5,6 +5,7 @@ import TextareaAutosize from '../src/TextareaAutosize';
class Example extends React.Component {
textareaStyle = {
+ width: 300,
padding: '10px 8px',
border: '1px solid rgba(39, 41, 43, .15)',
borderRadius: 4,
@@ -27,11 +28,19 @@ class Example extends React.Component {
<h2>Minimum height is 3 "rows"</h2>
<TextareaAutosize
- rows='3'
+ rows={3}
style={this.textareaStyle}
placeholder='minimun height is 3 rows'
/>
+ <h2>Maximum height is 3 "rows"</h2>
+ <TextareaAutosize
+ maxRows={3}
+ style={this.textareaStyle}
+ placeholder='maximum height is 5 rows'
+ defaultValue={'this\nis\na\nlong\ninitial\ntext'}
+ />
+
<h2>Prefilled with initial value</h2>
<TextareaAutosize
style={this.textareaStyle}
diff --git a/src/TextareaAutosize.js b/src/TextareaAutosize.js
index 8677c6a..50133d7 100644
--- a/src/TextareaAutosize.js
+++ b/src/TextareaAutosize.js
@@ -7,12 +7,16 @@ const UPDATE = 'autosize:update',
RESIZED = 'autosize:resized';
export const Props = {
+ rows: t.maybe(t.Integer),
+ maxRows: t.maybe(t.Integer),
onResize: t.maybe(t.Function)
};
/** 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
*/
@props(Props, { strict: false })
export default class TextareaAutosize extends React.Component {
@@ -21,9 +25,24 @@ export default class TextareaAutosize extends React.Component {
rows: 1
};
+ state = {
+ maxHeight: null
+ }
+
componentDidMount() {
+ const { value, defaultValue, onResize } = this.props;
+
autosize(this.textarea);
- if (this.props.onResize) {
+
+ if (this.hasReachedMaxRows(value || defaultValue)) {
+ this.updateMaxHeight(value || defaultValue);
+
+ // this trick is needed to force "autosize" to activate the scrollbar
+ this.dispatchEvent(DESTROY);
+ setTimeout(() => autosize(this.textarea));
+ }
+
+ if (onResize) {
this.textarea.addEventListener(RESIZED, this.props.onResize);
}
}
@@ -44,10 +63,71 @@ export default class TextareaAutosize extends React.Component {
getValue = ({ valueLink, value }) => valueLink ? valueLink.value : value;
+ hasReachedMaxRows = (value) => {
+ const { maxRows } = this.props;
+
+ const numberOfRows = (value || '').split('\n').length;
+
+ return t.Number.is(maxRows) && numberOfRows >= maxRows;
+ }
+
+ updateMaxHeight = (value) => {
+ const {
+ props: { maxRows },
+ state: { maxHeight }
+ } = this;
+
+ const hasReachedMaxRows = this.hasReachedMaxRows(value);
+
+ if (!maxHeight && hasReachedMaxRows) {
+ const numberOfRows = (value || '').split('\n').length;
+ const computedStyle = window.getComputedStyle(this.textarea);
+
+ const paddingTop = parseFloat(computedStyle.getPropertyValue('padding-top'), 10);
+ const paddingBottom = parseFloat(computedStyle.getPropertyValue('padding-top'), 10);
+ const verticalPadding = (paddingTop || 0) + (paddingBottom || 0);
+
+ const borderTopWidth = parseInt(computedStyle.getPropertyValue('border-top-width'), 10);
+ const borderBottomWidth = parseInt(computedStyle.getPropertyValue('border-bottom-width'), 10);
+ const verticalBorderWidth = (borderTopWidth || 0) + (borderBottomWidth || 0);
+
+ const height = this.textarea.offsetHeight - verticalPadding - verticalBorderWidth;
+
+ this.setState({
+ maxHeight: height / numberOfRows * maxRows
+ });
+
+ return true;
+ } else if (maxHeight && !hasReachedMaxRows) {
+ this.setState({ maxHeight: null });
+
+ return false;
+ }
+
+ }
+
+ onChange = e => {
+ this.updateMaxHeight(e.target.value);
+ this.props.onChange && this.props.onChange(e);
+ }
+
+ getLocals = () => {
+ const {
+ props: { onResize, maxRows, onChange, style, ...props }, // eslint-disable-line no-unused-vars
+ state: { maxHeight }
+ } = this;
+
+ return {
+ ...props,
+ style: maxHeight ? { ...style, maxHeight } : style,
+ onChange: this.onChange
+ };
+ }
+
render() {
- const { children, onResize, ...props } = this.props; // eslint-disable-line no-unused-vars
+ const { children, ...locals } = this.getLocals();
return (
- <textarea {...props} ref={(ref) => { this.textarea = ref; }}>
+ <textarea {...locals} ref={(ref) => { this.textarea = ref; }}>
{children}
</textarea>
);