compose_form.jsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import CharacterCounter from './character_counter';
  2. import Button from '../../../components/button';
  3. import PureRenderMixin from 'react-addons-pure-render-mixin';
  4. import ImmutablePropTypes from 'react-immutable-proptypes';
  5. import ReplyIndicator from './reply_indicator';
  6. import UploadButton from './upload_button';
  7. import Autosuggest from 'react-autosuggest';
  8. const getTokenForSuggestions = (str, caretPosition) => {
  9. let word;
  10. let left = str.slice(0, caretPosition).search(/\S+$/);
  11. let right = str.slice(caretPosition).search(/\s/);
  12. if (right < 0) {
  13. word = str.slice(left);
  14. } else {
  15. word = str.slice(left, right + caretPosition);
  16. }
  17. if (!word || word.trim().length < 2 || word[0] !== '@') {
  18. return null;
  19. }
  20. word = word.trim().toLowerCase().slice(1);
  21. if (word.length > 0) {
  22. return word;
  23. } else {
  24. return null;
  25. }
  26. };
  27. const getSuggestionValue = suggestion => suggestion.completion;
  28. const renderSuggestion = suggestion => (
  29. <span>{suggestion.label}</span>
  30. );
  31. const textareaStyle = {
  32. display: 'block',
  33. boxSizing: 'border-box',
  34. width: '100%',
  35. height: '100px',
  36. resize: 'none',
  37. border: 'none',
  38. color: '#282c37',
  39. padding: '10px',
  40. fontFamily: 'Roboto',
  41. fontSize: '14px',
  42. margin: '0'
  43. };
  44. const renderInputComponent = inputProps => (
  45. <textarea {...inputProps} placeholder='What is on your mind?' className='compose-form__textarea' style={textareaStyle} />
  46. );
  47. const ComposeForm = React.createClass({
  48. propTypes: {
  49. text: React.PropTypes.string.isRequired,
  50. suggestions: React.PropTypes.array,
  51. is_submitting: React.PropTypes.bool,
  52. is_uploading: React.PropTypes.bool,
  53. in_reply_to: ImmutablePropTypes.map,
  54. onChange: React.PropTypes.func.isRequired,
  55. onSubmit: React.PropTypes.func.isRequired,
  56. onCancelReply: React.PropTypes.func.isRequired
  57. },
  58. mixins: [PureRenderMixin],
  59. handleChange (e) {
  60. this.props.onChange(e.target.value);
  61. },
  62. handleKeyUp (e) {
  63. if (e.keyCode === 13 && e.ctrlKey) {
  64. this.props.onSubmit();
  65. }
  66. },
  67. handleSubmit () {
  68. this.props.onSubmit();
  69. },
  70. componentDidUpdate (prevProps) {
  71. if (prevProps.text !== this.props.text || prevProps.in_reply_to !== this.props.in_reply_to) {
  72. const node = ReactDOM.findDOMNode(this.refs.autosuggest);
  73. const textarea = node.querySelector('textarea');
  74. if (textarea) {
  75. textarea.focus();
  76. }
  77. }
  78. },
  79. onSuggestionsClearRequested () {
  80. this.props.onClearSuggestions();
  81. },
  82. onSuggestionsFetchRequested ({ value }) {
  83. const node = ReactDOM.findDOMNode(this.refs.autosuggest);
  84. const textarea = node.querySelector('textarea');
  85. if (textarea) {
  86. const token = getTokenForSuggestions(value, textarea.selectionStart);
  87. if (token !== null) {
  88. this.props.onFetchSuggestions(token);
  89. }
  90. }
  91. },
  92. onSuggestionSelected (e, { suggestionValue, method }) {
  93. const node = ReactDOM.findDOMNode(this.refs.autosuggest);
  94. const textarea = node.querySelector('textarea');
  95. if (textarea) {
  96. const str = this.props.text;
  97. this.props.onChange([str.slice(0, textarea.selectionStart), suggestionValue, str.slice(textarea.selectionStart)].join(''));
  98. }
  99. },
  100. render () {
  101. let replyArea = '';
  102. const disabled = this.props.is_submitting || this.props.is_uploading;
  103. if (this.props.in_reply_to) {
  104. replyArea = <ReplyIndicator status={this.props.in_reply_to} onCancel={this.props.onCancelReply} />;
  105. }
  106. const inputProps = {
  107. placeholder: 'What is on your mind?',
  108. arialabel: 'What is on your mind?',
  109. value: this.props.text,
  110. onKeyUp: this.handleKeyUp,
  111. onChange: this.handleChange,
  112. disabled: disabled
  113. };
  114. return (
  115. <div style={{ padding: '10px' }}>
  116. {replyArea}
  117. <Autosuggest
  118. ref='autosuggest'
  119. suggestions={this.props.suggestions}
  120. onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
  121. onSuggestionsClearRequested={this.onSuggestionsClearRequested}
  122. onSuggestionSelected={this.onSuggestionSelected}
  123. getSuggestionValue={getSuggestionValue}
  124. renderSuggestion={renderSuggestion}
  125. renderInputComponent={renderInputComponent}
  126. inputProps={inputProps}
  127. />
  128. <div style={{ marginTop: '10px', overflow: 'hidden' }}>
  129. <div style={{ float: 'right' }}><Button text='Publish' onClick={this.handleSubmit} disabled={disabled} /></div>
  130. <div style={{ float: 'right', marginRight: '16px', lineHeight: '36px' }}><CharacterCounter max={500} text={this.props.text} /></div>
  131. </div>
  132. </div>
  133. );
  134. }
  135. });
  136. export default ComposeForm;