1 import React
from 'react';
2 import AutosuggestAccountContainer
from '../features/compose/containers/autosuggest_account_container';
3 import AutosuggestEmoji
from './autosuggest_emoji';
4 import ImmutablePropTypes
from 'react-immutable-proptypes';
5 import PropTypes
from 'prop-types';
6 import { isRtl
} from '../rtl';
7 import ImmutablePureComponent
from 'react-immutable-pure-component';
8 import Textarea
from 'react-textarea-autosize';
9 import classNames
from 'classnames';
11 const textAtCursorMatchesToken
= (str
, caretPosition
) => {
14 let left
= str
.slice(0, caretPosition
).search(/[^\s\u200B]+$/);
15 let right
= str
.slice(caretPosition
).search(/[\s\u200B]/);
18 word
= str
.slice(left
);
20 word
= str
.slice(left
, right
+ caretPosition
);
23 if (!word
|| word
.trim().length
< 3 || ['@', ':'].indexOf(word
[0]) === -1) {
27 word
= word
.trim().toLowerCase();
29 if (word
.length
> 0) {
30 return [left
+ 1, word
];
36 export default class AutosuggestTextarea
extends ImmutablePureComponent
{
39 value: PropTypes
.string
,
40 suggestions: ImmutablePropTypes
.list
,
41 disabled: PropTypes
.bool
,
42 placeholder: PropTypes
.string
,
43 onSuggestionSelected: PropTypes
.func
.isRequired
,
44 onSuggestionsClearRequested: PropTypes
.func
.isRequired
,
45 onSuggestionsFetchRequested: PropTypes
.func
.isRequired
,
46 onChange: PropTypes
.func
.isRequired
,
47 onKeyUp: PropTypes
.func
,
48 onKeyDown: PropTypes
.func
,
49 onPaste: PropTypes
.func
.isRequired
,
50 autoFocus: PropTypes
.bool
,
53 static defaultProps
= {
58 suggestionsHidden: false,
59 selectedSuggestion: 0,
65 const [ tokenStart
, token
] = textAtCursorMatchesToken(e
.target
.value
, e
.target
.selectionStart
);
67 if (token
!== null && this.state
.lastToken
!== token
) {
68 this.setState({ lastToken: token
, selectedSuggestion: 0, tokenStart
});
69 this.props
.onSuggestionsFetchRequested(token
);
70 } else if (token
=== null) {
71 this.setState({ lastToken: null });
72 this.props
.onSuggestionsClearRequested();
75 this.props
.onChange(e
);
79 const { suggestions
, disabled
} = this.props
;
80 const { selectedSuggestion
, suggestionsHidden
} = this.state
;
89 if (!suggestionsHidden
) {
91 this.setState({ suggestionsHidden: true });
96 if (suggestions
.size
> 0 && !suggestionsHidden
) {
98 this.setState({ selectedSuggestion: Math
.min(selectedSuggestion
+ 1, suggestions
.size
- 1) });
103 if (suggestions
.size
> 0 && !suggestionsHidden
) {
105 this.setState({ selectedSuggestion: Math
.max(selectedSuggestion
- 1, 0) });
112 if (this.state
.lastToken
!== null && suggestions
.size
> 0 && !suggestionsHidden
) {
115 this.props
.onSuggestionSelected(this.state
.tokenStart
, this.state
.lastToken
, suggestions
.get(selectedSuggestion
));
121 if (e
.defaultPrevented
|| !this.props
.onKeyDown
) {
125 this.props
.onKeyDown(e
);
129 if (e
.key
=== 'Escape' && this.state
.suggestionsHidden
) {
130 document
.querySelector('.ui').parentElement
.focus();
133 if (this.props
.onKeyUp
) {
134 this.props
.onKeyUp(e
);
139 this.setState({ suggestionsHidden: true });
142 onSuggestionClick
= (e
) => {
143 const suggestion
= this.props
.suggestions
.get(e
.currentTarget
.getAttribute('data-index'));
145 this.props
.onSuggestionSelected(this.state
.tokenStart
, this.state
.lastToken
, suggestion
);
146 this.textarea
.focus();
149 componentWillReceiveProps (nextProps
) {
150 if (nextProps
.suggestions
!== this.props
.suggestions
&& nextProps
.suggestions
.size
> 0 && this.state
.suggestionsHidden
) {
151 this.setState({ suggestionsHidden: false });
155 setTextarea
= (c
) => {
160 if (e
.clipboardData
&& e
.clipboardData
.files
.length
=== 1) {
161 this.props
.onPaste(e
.clipboardData
.files
);
166 renderSuggestion
= (suggestion
, i
) => {
167 const { selectedSuggestion
} = this.state
;
170 if (typeof suggestion
=== 'object') {
171 inner
= <AutosuggestEmoji emoji
={suggestion
} />;
174 inner
= <AutosuggestAccountContainer id
={suggestion
} />;
179 <div role
='button' tabIndex
='0' key
={key
} data
-index
={i
} className
={classNames('autosuggest-textarea__suggestions__item', { selected: i
=== selectedSuggestion
})} onMouseDown
={this.onSuggestionClick
}>
186 const { value
, suggestions
, disabled
, placeholder
, autoFocus
} = this.props
;
187 const { suggestionsHidden
} = this.state
;
188 const style
= { direction: 'ltr' };
191 style
.direction
= 'rtl';
195 <div className
='autosuggest-textarea'>
197 <span style
={{ display: 'none' }}>{placeholder
}</span
>
200 inputRef
={this.setTextarea
}
201 className
='autosuggest-textarea__textarea'
203 placeholder
={placeholder
}
204 autoFocus
={autoFocus
}
206 onChange
={this.onChange
}
207 onKeyDown
={this.onKeyDown
}
208 onKeyUp
={this.onKeyUp
}
210 onPaste
={this.onPaste
}
215 <div className
={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
216 {suggestions
.map(this.renderSuggestion
)}