Fix #372 - Emoji picker
This commit is contained in:
parent
6a1b738e0b
commit
89fc2d7f48
8 changed files with 315 additions and 11 deletions
|
@ -15,6 +15,7 @@ import UnlistedToggleContainer from '../containers/unlisted_toggle_container';
|
|||
import SpoilerToggleContainer from '../containers/spoiler_toggle_container';
|
||||
import PrivateToggleContainer from '../containers/private_toggle_container';
|
||||
import SensitiveToggleContainer from '../containers/sensitive_toggle_container';
|
||||
import EmojiPickerDropdown from './emoji_picker_dropdown';
|
||||
|
||||
const messages = defineMessages({
|
||||
placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
|
||||
|
@ -48,6 +49,7 @@ const ComposeForm = React.createClass({
|
|||
onSuggestionSelected: React.PropTypes.func.isRequired,
|
||||
onChangeSpoilerText: React.PropTypes.func.isRequired,
|
||||
onPaste: React.PropTypes.func.isRequired,
|
||||
onPickEmoji: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
mixins: [PureRenderMixin],
|
||||
|
@ -76,6 +78,7 @@ const ComposeForm = React.createClass({
|
|||
},
|
||||
|
||||
onSuggestionSelected (tokenStart, token, value) {
|
||||
this._restoreCaret = null;
|
||||
this.props.onSuggestionSelected(tokenStart, token, value);
|
||||
},
|
||||
|
||||
|
@ -88,8 +91,18 @@ const ComposeForm = React.createClass({
|
|||
// If replying to zero or one users, places the cursor at the end of the textbox.
|
||||
// If replying to more than one user, selects any usernames past the first;
|
||||
// this provides a convenient shortcut to drop everyone else from the conversation.
|
||||
const selectionEnd = this.props.text.length;
|
||||
const selectionStart = (this.props.preselectDate !== prevProps.preselectDate) ? (this.props.text.search(/\s/) + 1) : selectionEnd;
|
||||
let selectionEnd, selectionStart;
|
||||
|
||||
if (this.props.preselectDate !== prevProps.preselectDate) {
|
||||
selectionEnd = this.props.text.length;
|
||||
selectionStart = this.props.text.search(/\s/) + 1;
|
||||
} else if (typeof this._restoreCaret === 'number') {
|
||||
selectionStart = this._restoreCaret;
|
||||
selectionEnd = this._restoreCaret;
|
||||
} else {
|
||||
selectionEnd = this.props.text.length;
|
||||
selectionStart = selectionEnd;
|
||||
}
|
||||
|
||||
this.autosuggestTextarea.textarea.setSelectionRange(selectionStart, selectionEnd);
|
||||
this.autosuggestTextarea.textarea.focus();
|
||||
|
@ -100,6 +113,12 @@ const ComposeForm = React.createClass({
|
|||
this.autosuggestTextarea = c;
|
||||
},
|
||||
|
||||
handleEmojiPick (data) {
|
||||
const position = this.autosuggestTextarea.textarea.selectionStart;
|
||||
this._restoreCaret = position + data.shortname.length + 1;
|
||||
this.props.onPickEmoji(position, data);
|
||||
},
|
||||
|
||||
render () {
|
||||
const { intl, needsPrivacyWarning, mentionedDomains, onPaste } = this.props;
|
||||
const disabled = this.props.is_submitting || this.props.is_uploading;
|
||||
|
@ -156,7 +175,10 @@ const ComposeForm = React.createClass({
|
|||
<div style={{ marginTop: '10px', overflow: 'hidden' }}>
|
||||
<div style={{ float: 'right' }}><Button text={publishText} onClick={this.handleSubmit} disabled={disabled} /></div>
|
||||
<div style={{ float: 'right', marginRight: '16px', lineHeight: '36px' }}><CharacterCounter max={500} text={[this.props.spoiler_text, this.props.text].join('')} /></div>
|
||||
<UploadButtonContainer style={{ paddingTop: '4px' }} />
|
||||
<div style={{ display: 'flex', paddingTop: '4px' }}>
|
||||
<UploadButtonContainer />
|
||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SpoilerToggleContainer />
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown';
|
||||
import EmojiPicker from 'emojione-picker';
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
emoji: { id: 'emoji_button.label', defaultMessage: 'Emoji' }
|
||||
});
|
||||
|
||||
const settings = {
|
||||
imageType: 'png',
|
||||
sprites: false,
|
||||
imagePathPNG: '/emoji/'
|
||||
};
|
||||
|
||||
const EmojiPickerDropdown = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
intl: React.PropTypes.object.isRequired,
|
||||
onPickEmoji: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
mixins: [PureRenderMixin],
|
||||
|
||||
setRef (c) {
|
||||
this.dropdown = c;
|
||||
},
|
||||
|
||||
handleChange (data) {
|
||||
this.dropdown.hide();
|
||||
this.props.onPickEmoji(data);
|
||||
},
|
||||
|
||||
render () {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<Dropdown ref={this.setRef} style={{ marginLeft: '5px' }}>
|
||||
<DropdownTrigger className='icon-button' title={intl.formatMessage(messages.emoji)} style={{ fontSize: `24px`, width: `24px`, lineHeight: `24px`, marginTop: '-1px', display: 'block', marginLeft: '2px' }}>
|
||||
<i className={`fa fa-smile-o`} style={{ verticalAlign: 'middle' }} />
|
||||
</DropdownTrigger>
|
||||
|
||||
<DropdownContent>
|
||||
<EmojiPicker emojione={settings} onChange={this.handleChange} />
|
||||
</DropdownContent>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
export default injectIntl(EmojiPickerDropdown);
|
|
@ -9,6 +9,7 @@ import {
|
|||
fetchComposeSuggestions,
|
||||
selectComposeSuggestion,
|
||||
changeComposeSpoilerText,
|
||||
insertEmojiCompose
|
||||
} from '../../../actions/compose';
|
||||
|
||||
const getMentionedUsernames = createSelector(state => state.getIn(['compose', 'text']), text => text.match(/(?:^|[^\/\w])@([a-z0-9_]+@[a-z0-9\.\-]+)/ig));
|
||||
|
@ -70,6 +71,10 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
dispatch(uploadCompose(files));
|
||||
},
|
||||
|
||||
onPickEmoji (position, data) {
|
||||
dispatch(insertEmojiCompose(position, data));
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ComposeForm);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue