summaryrefslogtreecommitdiffstats
path: root/webapp/components/textbox.jsx
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-03-14 08:50:46 -0400
committerChristopher Speller <crspeller@gmail.com>2016-03-16 18:02:55 -0400
commit12896bd23eeba79884245c1c29fdc568cf21a7fa (patch)
tree4e7f83d3e2564b9b89d669e9f7905ff11768b11a /webapp/components/textbox.jsx
parent29fe6a3d13c9c7aa490fffebbe5d1b5fdf1e3090 (diff)
downloadchat-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.jsx267
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
+};