diff options
author | Christopher Speller <crspeller@gmail.com> | 2016-03-14 08:50:46 -0400 |
---|---|---|
committer | Christopher Speller <crspeller@gmail.com> | 2016-03-16 18:02:55 -0400 |
commit | 12896bd23eeba79884245c1c29fdc568cf21a7fa (patch) | |
tree | 4e7f83d3e2564b9b89d669e9f7905ff11768b11a /webapp/components/textbox.jsx | |
parent | 29fe6a3d13c9c7aa490fffebbe5d1b5fdf1e3090 (diff) | |
download | chat-12896bd23eeba79884245c1c29fdc568cf21a7fa.tar.gz chat-12896bd23eeba79884245c1c29fdc568cf21a7fa.tar.bz2 chat-12896bd23eeba79884245c1c29fdc568cf21a7fa.zip |
Converting to Webpack. Stage 1.
Diffstat (limited to 'webapp/components/textbox.jsx')
-rw-r--r-- | webapp/components/textbox.jsx | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/webapp/components/textbox.jsx b/webapp/components/textbox.jsx new file mode 100644 index 000000000..1a395072e --- /dev/null +++ b/webapp/components/textbox.jsx @@ -0,0 +1,267 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import $ from 'jquery'; +import ReactDOM from 'react-dom'; +import AtMentionProvider from './suggestion/at_mention_provider.jsx'; +import CommandProvider from './suggestion/command_provider.jsx'; +import EmoticonProvider from './suggestion/emoticon_provider.jsx'; +import SuggestionList from './suggestion/suggestion_list.jsx'; +import SuggestionBox from './suggestion/suggestion_box.jsx'; +import ErrorStore from 'stores/error_store.jsx'; + +import * as TextFormatting from 'utils/text_formatting.jsx'; +import * as Utils from 'utils/utils.jsx'; +import Constants from 'utils/constants.jsx'; + +import {FormattedMessage} from 'react-intl'; + +const PreReleaseFeatures = Constants.PRE_RELEASE_FEATURES; + +import React from 'react'; + +export default class Textbox extends React.Component { + constructor(props) { + super(props); + + this.focus = this.focus.bind(this); + this.getStateFromStores = this.getStateFromStores.bind(this); + this.onRecievedError = this.onRecievedError.bind(this); + this.handleKeyPress = this.handleKeyPress.bind(this); + this.handleKeyDown = this.handleKeyDown.bind(this); + this.resize = this.resize.bind(this); + this.showPreview = this.showPreview.bind(this); + + this.state = { + connection: '' + }; + + this.suggestionProviders = [new AtMentionProvider(), new EmoticonProvider()]; + if (props.supportsCommands) { + this.suggestionProviders.push(new CommandProvider()); + } + } + + getStateFromStores() { + const error = ErrorStore.getLastError(); + + if (error) { + return {message: error.message}; + } + + return {message: null}; + } + + componentDidMount() { + ErrorStore.addChangeListener(this.onRecievedError); + + this.resize(); + } + + componentWillUnmount() { + ErrorStore.removeChangeListener(this.onRecievedError); + } + + onRecievedError() { + const errorCount = ErrorStore.getConnectionErrorCount(); + + if (errorCount > 1) { + this.setState({connection: 'bad-connection'}); + } else { + this.setState({connection: ''}); + } + } + + componentDidUpdate() { + this.resize(); + } + + handleKeyPress(e) { + this.props.onKeyPress(e); + } + + handleKeyDown(e) { + if (this.props.onKeyDown) { + this.props.onKeyDown(e); + } + } + + focus() { + this.refs.message.getTextbox().focus(); + } + + resize() { + const textbox = this.refs.message.getTextbox(); + const $textbox = $(textbox); + const $wrapper = $(ReactDOM.findDOMNode(this.refs.wrapper)); + + const padding = parseInt($textbox.css('padding-bottom'), 10) + parseInt($textbox.css('padding-top'), 10); + const borders = parseInt($textbox.css('border-bottom-width'), 10) + parseInt($textbox.css('border-top-width'), 10); + const maxHeight = parseInt($textbox.css('max-height'), 10) - borders; + + // set the height to auto and remove the scrollbar so we can get the actual size of the contents + $textbox.css('height', 'auto').css('overflow-y', 'hidden'); + + let height = textbox.scrollHeight - padding; + + if (height + padding > maxHeight) { + height = maxHeight - padding; + + // turn scrollbar on and move over attachment icon to compensate for that + $textbox.css('overflow-y', 'scroll'); + $wrapper.closest('.post-body__cell').addClass('scroll'); + } else { + $wrapper.closest('.post-body__cell').removeClass('scroll'); + } + + // set the textarea to be the proper height + $textbox.height(height); + + // set the wrapper height to match the height of the textbox including padding and borders + $wrapper.height(height + padding + borders); + + if (this.state.preview) { + $(ReactDOM.findDOMNode(this.refs.preview)).height(height + borders); + } + } + + showPreview(e) { + e.preventDefault(); + e.target.blur(); + this.setState({preview: !this.state.preview}); + this.resize(); + } + + render() { + const hasText = this.props.messageText.length > 0; + + let previewLink = null; + if (Utils.isFeatureEnabled(PreReleaseFeatures.MARKDOWN_PREVIEW)) { + previewLink = ( + <a + onClick={this.showPreview} + className='textbox-preview-link' + > + {this.state.preview ? ( + <FormattedMessage + id='textbox.edit' + defaultMessage='Edit message' + /> + ) : ( + <FormattedMessage + id='textbox.preview' + defaultMessage='Preview' + /> + )} + </a> + ); + } + + let helpText = ( + <div + style={{visibility: hasText ? 'visible' : 'hidden', opacity: hasText ? '0.5' : '0'}} + className='help_format_text' + > + <b> + <FormattedMessage + id='textbox.bold' + defaultMessage='**bold**' + /> + </b> + <i> + <FormattedMessage + id='textbox.italic' + defaultMessage='_italic_' + /> + </i> + <span>~~<strike> + <FormattedMessage + id='textbox.strike' + defaultMessage='strike' + /> + </strike>~~ </span> + <code> + <FormattedMessage + id='textbox.inlinecode' + defaultMessage='`inline code`' + /> + </code> + <code> + <FormattedMessage + id='textbox.preformatted' + defaultMessage='```preformatted```' + /> + </code> + <span> + <FormattedMessage + id='textbox.quote' + defaultMessage='>quote' + /> + </span> + </div> + ); + + return ( + <div + ref='wrapper' + className='textarea-wrapper' + > + <SuggestionBox + id={this.props.id} + ref='message' + className={`form-control custom-textarea ${this.state.connection}`} + type='textarea' + spellCheck='true' + autoComplete='off' + autoCorrect='off' + rows='1' + maxLength={Constants.MAX_POST_LEN} + placeholder={this.props.createMessage} + value={this.props.messageText} + onUserInput={this.props.onUserInput} + onKeyPress={this.handleKeyPress} + onKeyDown={this.handleKeyDown} + style={{visibility: this.state.preview ? 'hidden' : 'visible'}} + listComponent={SuggestionList} + providers={this.suggestionProviders} + /> + <div + ref='preview' + className='form-control custom-textarea textbox-preview-area' + style={{display: this.state.preview ? 'block' : 'none'}} + dangerouslySetInnerHTML={{__html: this.state.preview ? TextFormatting.formatText(this.props.messageText) : ''}} + > + </div> + {helpText} + <div className='help__text'> + {previewLink} + <a + target='_blank' + href='http://docs.mattermost.com/help/getting-started/messaging-basics.html' + className='textbox-help-link' + > + <FormattedMessage + id='textbox.help' + defaultMessage='Help' + /> + </a> + </div> + </div> + ); + } +} + +Textbox.defaultProps = { + supportsCommands: true +}; + +Textbox.propTypes = { + id: React.PropTypes.string.isRequired, + channelId: React.PropTypes.string, + messageText: React.PropTypes.string.isRequired, + onUserInput: React.PropTypes.func.isRequired, + onKeyPress: React.PropTypes.func.isRequired, + createMessage: React.PropTypes.string.isRequired, + onKeyDown: React.PropTypes.func, + supportsCommands: React.PropTypes.bool.isRequired +}; |