diff options
Diffstat (limited to 'webapp')
-rw-r--r-- | webapp/components/autosize_textarea.jsx | 90 | ||||
-rw-r--r-- | webapp/components/edit_post_modal.jsx | 2 | ||||
-rw-r--r-- | webapp/components/suggestion/suggestion_box.jsx | 56 | ||||
-rw-r--r-- | webapp/components/textbox.jsx | 17 | ||||
-rw-r--r-- | webapp/package.json | 1 | ||||
-rw-r--r-- | webapp/sass/components/_modal.scss | 2 | ||||
-rw-r--r-- | webapp/sass/layout/_post.scss | 26 |
7 files changed, 138 insertions, 56 deletions
diff --git a/webapp/components/autosize_textarea.jsx b/webapp/components/autosize_textarea.jsx new file mode 100644 index 000000000..45807fda0 --- /dev/null +++ b/webapp/components/autosize_textarea.jsx @@ -0,0 +1,90 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; + +export default class AutosizeTextarea extends React.Component { + static propTypes = { + value: React.PropTypes.string, + placeholder: React.PropTypes.string, + onHeightChange: React.PropTypes.func + } + + constructor(props) { + super(props); + + this.height = 0; + } + + get value() { + return this.refs.textarea.value; + } + + set value(value) { + this.refs.textarea.value = value; + } + + componentDidUpdate() { + this.recalculateSize(); + } + + recalculateSize() { + const height = this.refs.reference.scrollHeight; + + if (height > 0 && height !== this.height) { + const textarea = this.refs.textarea; + + const style = getComputedStyle(textarea); + const borderWidth = parseInt(style.borderTopWidth, 10) + parseInt(style.borderBottomWidth, 10); + + // Directly change the height to avoid circular rerenders + textarea.style.height = String(height + borderWidth) + 'px'; + + this.height = height; + + if (this.props.onHeightChange) { + this.props.onHeightChange(height, parseInt(style.maxHeight, 10)); + } + } + } + + getDOMNode() { + return this.refs.textarea; + } + + render() { + window.forceUpdate = this.forceUpdate.bind(this); + const { + value, + placeholder, + ...otherProps + } = this.props; + + const heightProps = {}; + if (this.height <= 0) { + // Set an initial number of rows so that the textarea doesn't appear too large when its first rendered + heightProps.rows = 1; + } else { + heightProps.height = this.height; + } + + return ( + <div> + <textarea + ref='textarea' + {...heightProps} + {...this.props} + /> + <div style={{height: 0, overflow: 'hidden'}}> + <textarea + ref='reference' + style={{height: 'auto', width: '100%'}} + value={value || placeholder} + rows='1' + {...otherProps} + /> + </div> + </div> + ); + } +}
\ No newline at end of file diff --git a/webapp/components/edit_post_modal.jsx b/webapp/components/edit_post_modal.jsx index cd33dd113..6ed2b81b2 100644 --- a/webapp/components/edit_post_modal.jsx +++ b/webapp/components/edit_post_modal.jsx @@ -156,6 +156,8 @@ export default class EditPostModal extends React.Component { onModalShown() { this.refs.editbox.focus(); + + this.refs.editbox.recalculateSize(); } onModalHide() { diff --git a/webapp/components/suggestion/suggestion_box.jsx b/webapp/components/suggestion/suggestion_box.jsx index 590fdae04..21bfd3dc3 100644 --- a/webapp/components/suggestion/suggestion_box.jsx +++ b/webapp/components/suggestion/suggestion_box.jsx @@ -9,7 +9,7 @@ import * as GlobalActions from 'actions/global_actions.jsx'; import SuggestionStore from 'stores/suggestion_store.jsx'; import * as Utils from 'utils/utils.jsx'; -import TextareaAutosize from 'react-autosize-textarea'; +import AutosizeTextarea from 'components/autosize_textarea.jsx'; const KeyCodes = Constants.KeyCodes; @@ -46,14 +46,17 @@ export default class SuggestionBox extends React.Component { } getTextbox() { - // this is to support old code that looks at the input/textarea DOM nodes - let textbox = this.refs.textbox; - - if (!(textbox instanceof HTMLElement)) { - textbox = ReactDOM.findDOMNode(textbox); + if (this.props.type === 'textarea') { + return this.refs.textbox.getDOMNode(); } - return textbox; + return this.refs.textbox; + } + + recalculateSize() { + if (this.props.type === 'textarea') { + this.refs.textbox.recalculateSize(); + } } handleDocumentClick(e) { @@ -71,7 +74,7 @@ export default class SuggestionBox extends React.Component { } handleChange(e) { - const textbox = ReactDOM.findDOMNode(this.refs.textbox); + const textbox = this.getTextbox(); const caret = Utils.getCaretPosition(textbox); const pretext = textbox.value.substring(0, caret); @@ -83,7 +86,7 @@ export default class SuggestionBox extends React.Component { } handleCompleteWord(term, matchedPretext) { - const textbox = ReactDOM.findDOMNode(this.refs.textbox); + const textbox = this.getTextbox(); const caret = Utils.getCaretPosition(textbox); const text = this.props.value; const pretext = text.substring(0, caret); @@ -150,48 +153,57 @@ export default class SuggestionBox extends React.Component { } render() { + const { + type, + listComponent, + listStyle, + renderDividers, + ...props + } = this.props; + let textbox = null; - if (this.props.type === 'input') { + if (type === 'input') { textbox = ( <input ref='textbox' type='text' - {...this.props} - onChange={this.handleChange} + {...props} + onInput={this.handleChange} onKeyDown={this.handleKeyDown} /> ); - } else if (this.props.type === 'search') { + } else if (type === 'search') { textbox = ( <input ref='textbox' type='search' - {...this.props} - onChange={this.handleChange} + {...props} + onInput={this.handleChange} onKeyDown={this.handleKeyDown} /> ); - } else if (this.props.type === 'textarea') { + } else if (type === 'textarea') { textbox = ( - <TextareaAutosize + <AutosizeTextarea id={this.suggestionId} ref='textbox' - {...this.props} - onChange={this.handleChange} + {...props} + onInput={this.handleChange} onKeyDown={this.handleKeyDown} /> ); } - const SuggestionListComponent = this.props.listComponent; + // This needs to be upper case so React doesn't think it's an html tag + const SuggestionListComponent = listComponent; return ( <div> {textbox} <SuggestionListComponent suggestionId={this.suggestionId} - location={this.props.listStyle} - renderDividers={this.props.renderDividers} + location={listStyle} + renderDividers={renderDividers} /> </div> ); diff --git a/webapp/components/textbox.jsx b/webapp/components/textbox.jsx index f11ef20ad..9192cd4f9 100644 --- a/webapp/components/textbox.jsx +++ b/webapp/components/textbox.jsx @@ -25,6 +25,7 @@ export default class Textbox extends React.Component { super(props); this.focus = this.focus.bind(this); + this.recalculateSize = this.recalculateSize.bind(this); this.getStateFromStores = this.getStateFromStores.bind(this); this.onRecievedError = this.onRecievedError.bind(this); this.handleKeyPress = this.handleKeyPress.bind(this); @@ -91,13 +92,10 @@ export default class Textbox extends React.Component { } } - handleHeightChange(height) { - const textbox = $(this.refs.message.getTextbox()); + handleHeightChange(height, maxHeight) { const wrapper = $(this.refs.wrapper); - const maxHeight = parseInt(textbox.css('max-height'), 10); - - // move over attachment icon to compensate for the scrollbar + // Move over attachment icon to compensate for the scrollbar if (height > maxHeight) { wrapper.closest('.post-body__cell').addClass('scroll'); } else { @@ -106,7 +104,14 @@ export default class Textbox extends React.Component { } focus() { - this.refs.message.getTextbox().focus(); + const textbox = this.refs.message.getTextbox(); + + textbox.focus(); + Utils.placeCaretAtEnd(textbox); + } + + recalculateSize() { + this.refs.message.recalculateSize(); } showPreview(e) { diff --git a/webapp/package.json b/webapp/package.json index 6e4a7c17a..338829559 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -24,7 +24,6 @@ "perfect-scrollbar": "0.6.12", "react": "15.3.2", "react-addons-pure-render-mixin": "15.3.2", - "react-autosize-textarea": "0.3.3", "react-bootstrap": "0.30.3", "react-custom-scrollbars": "4.0.0", "react-dom": "15.3.2", diff --git a/webapp/sass/components/_modal.scss b/webapp/sass/components/_modal.scss index 7a44e3b96..3faf78bc6 100644 --- a/webapp/sass/components/_modal.scss +++ b/webapp/sass/components/_modal.scss @@ -21,7 +21,7 @@ .custom-textarea { max-height: 60vh; - min-height: 20em; + min-height: 8em; } .suggestion-list { diff --git a/webapp/sass/layout/_post.scss b/webapp/sass/layout/_post.scss index efba5b6f1..4a995a4d6 100644 --- a/webapp/sass/layout/_post.scss +++ b/webapp/sass/layout/_post.scss @@ -22,31 +22,6 @@ color: #d04444 !important; } -.textarea-div { - border: 0; - color: rgba(0,0,0,0); - height: auto; - left: 1px; - line-height: 20px; - min-height: 36px; - position: absolute; - top: 0px; - white-space: pre-wrap; - word-break: break-word; - word-wrap: normal; -} - -body.ios { - .textarea-div { - -webkit-overflow-scrolling: auto; - padding: 7px 17px 7px 15px; - } -} - -.textarea-div::-webkit-scrollbar { - display: none; -} - .textarea-wrapper { min-height: 36px; position: relative; @@ -390,7 +365,6 @@ body.ios { .custom-textarea { bottom: 0; - line-height: 1.5; max-height: 162px !important; padding-right: 28px; padding-top: 8px; |