You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
4.6 KiB

  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;
  28. const renderSuggestion = suggestion => (
  29. <span>{suggestion}</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. value: this.props.text,
  109. onKeyUp: this.handleKeyUp,
  110. onChange: this.handleChange,
  111. disabled: disabled
  112. };
  113. return (
  114. <div style={{ padding: '10px' }}>
  115. {replyArea}
  116. <Autosuggest
  117. ref='autosuggest'
  118. suggestions={this.props.suggestions}
  119. onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
  120. onSuggestionsClearRequested={this.onSuggestionsClearRequested}
  121. onSuggestionSelected={this.onSuggestionSelected}
  122. getSuggestionValue={getSuggestionValue}
  123. renderSuggestion={renderSuggestion}
  124. renderInputComponent={renderInputComponent}
  125. inputProps={inputProps}
  126. />
  127. <div style={{ marginTop: '10px', overflow: 'hidden' }}>
  128. <div style={{ float: 'right' }}><Button text='Publish' onClick={this.handleSubmit} disabled={disabled} /></div>
  129. <div style={{ float: 'right', marginRight: '16px', lineHeight: '36px' }}><CharacterCounter max={500} text={this.props.text} /></div>
  130. </div>
  131. </div>
  132. );
  133. }
  134. });
  135. export default ComposeForm;