diff options
author | Gabriele Petronella <gabriele@buildo.io> | 2017-01-22 19:40:57 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-01-22 19:40:57 +0100 |
commit | 82071950413385e7b9d59fc1bf177c5d937410b5 (patch) | |
tree | 5f4f0ec49f1d1209a9bddbf276a844b5825b73f8 | |
parent | c78d7c6da77031d7810f5bc5b2b0d54021b0895e (diff) | |
parent | 2925810519bf6e25cfaee94954e4858f20bb4529 (diff) | |
download | react-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.js | 11 | ||||
-rw-r--r-- | src/TextareaAutosize.js | 86 |
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> ); |