1 import React
from 'react';
2 import PropTypes
from 'prop-types';
3 import { defineMessages
, injectIntl
} from 'react-intl';
4 import { Picker
, Emoji
} from 'emoji-mart';
5 import { Overlay
} from 'react-overlays';
6 import classNames
from 'classnames';
7 import ImmutablePropTypes
from 'react-immutable-proptypes';
9 const messages
= defineMessages({
10 emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' },
11 emoji_search: { id: 'emoji_button.search', defaultMessage: 'Search...' },
12 emoji_not_found: { id: 'emoji_button.not_found', defaultMessage: 'No emojos!! (╯°□°)╯︵ ┻━┻' },
13 custom: { id: 'emoji_button.custom', defaultMessage: 'Custom' },
14 recent: { id: 'emoji_button.recent', defaultMessage: 'Frequently used' },
15 search_results: { id: 'emoji_button.search_results', defaultMessage: 'Search results' },
16 people: { id: 'emoji_button.people', defaultMessage: 'People' },
17 nature: { id: 'emoji_button.nature', defaultMessage: 'Nature' },
18 food: { id: 'emoji_button.food', defaultMessage: 'Food & Drink' },
19 activity: { id: 'emoji_button.activity', defaultMessage: 'Activity' },
20 travel: { id: 'emoji_button.travel', defaultMessage: 'Travel & Places' },
21 objects: { id: 'emoji_button.objects', defaultMessage: 'Objects' },
22 symbols: { id: 'emoji_button.symbols', defaultMessage: 'Symbols' },
23 flags: { id: 'emoji_button.flags', defaultMessage: 'Flags' },
26 const assetHost
= process
.env
.CDN_HOST
|| '';
28 const backgroundImageFn
= () => `${assetHost}/emoji/sheet.png`;
30 class ModifierPickerMenu
extends React
.PureComponent
{
33 active: PropTypes
.bool
,
34 onSelect: PropTypes
.func
.isRequired
,
35 onClose: PropTypes
.func
.isRequired
,
38 handleClick
= (e
) => {
39 const modifier
= [].slice
.call(e
.currentTarget
.parentNode
.children
).indexOf(e
.target
) + 1;
40 this.props
.onSelect(modifier
);
43 componentWillReceiveProps (nextProps
) {
44 if (nextProps
.active
) {
45 this.attachListeners();
47 this.removeListeners();
51 componentWillUnmount () {
52 this.removeListeners();
55 handleDocumentClick
= e
=> {
56 if (this.node
&& !this.node
.contains(e
.target
)) {
62 document
.addEventListener('click', this.handleDocumentClick
, false);
63 document
.addEventListener('touchend', this.handleDocumentClick
, false);
67 document
.removeEventListener('click', this.handleDocumentClick
, false);
68 document
.removeEventListener('touchend', this.handleDocumentClick
, false);
76 const { active
} = this.props
;
79 <div className
='emoji-picker-dropdown__modifiers__menu' style
={{ display: active
? 'block' : 'none' }} ref
={this.setRef
}>
80 <button onClick
={this.handleClick
}><Emoji emoji
='fist' set='twitter' size
={22} sheetSize
={32} skin
={1} backgroundImageFn
={backgroundImageFn
} /></button
>
81 <button onClick
={this.handleClick
}><Emoji emoji
='fist' set='twitter' size
={22} sheetSize
={32} skin
={2} backgroundImageFn
={backgroundImageFn
} /></button
>
82 <button onClick
={this.handleClick
}><Emoji emoji
='fist' set='twitter' size
={22} sheetSize
={32} skin
={3} backgroundImageFn
={backgroundImageFn
} /></button
>
83 <button onClick
={this.handleClick
}><Emoji emoji
='fist' set='twitter' size
={22} sheetSize
={32} skin
={4} backgroundImageFn
={backgroundImageFn
} /></button
>
84 <button onClick
={this.handleClick
}><Emoji emoji
='fist' set='twitter' size
={22} sheetSize
={32} skin
={5} backgroundImageFn
={backgroundImageFn
} /></button
>
85 <button onClick
={this.handleClick
}><Emoji emoji
='fist' set='twitter' size
={22} sheetSize
={32} skin
={6} backgroundImageFn
={backgroundImageFn
} /></button
>
92 class ModifierPicker
extends React
.PureComponent
{
95 active: PropTypes
.bool
,
96 modifier: PropTypes
.number
,
97 onChange: PropTypes
.func
,
98 onClose: PropTypes
.func
,
99 onOpen: PropTypes
.func
,
102 handleClick
= () => {
103 if (this.props
.active
) {
104 this.props
.onClose();
110 handleSelect
= modifier
=> {
111 this.props
.onChange(modifier
);
112 this.props
.onClose();
116 const { active
, modifier
} = this.props
;
119 <div className
='emoji-picker-dropdown__modifiers'>
120 <Emoji emoji
='fist' set='twitter' size
={22} sheetSize
={32} skin
={modifier
} onClick
={this.handleClick
} backgroundImageFn
={backgroundImageFn
} />
121 <ModifierPickerMenu active
={active
} onSelect
={this.handleSelect
} onClose
={this.props
.onClose
} />
129 class EmojiPickerMenu
extends React
.PureComponent
{
132 custom_emojis: ImmutablePropTypes
.list
,
133 onClose: PropTypes
.func
.isRequired
,
134 onPick: PropTypes
.func
.isRequired
,
135 style: PropTypes
.object
,
136 placement: PropTypes
.string
,
137 arrowOffsetLeft: PropTypes
.string
,
138 arrowOffsetTop: PropTypes
.string
,
139 intl: PropTypes
.object
.isRequired
,
142 static defaultProps
= {
152 handleDocumentClick
= e
=> {
153 if (this.node
&& !this.node
.contains(e
.target
)) {
154 this.props
.onClose();
158 componentDidMount () {
159 document
.addEventListener('click', this.handleDocumentClick
, false);
160 document
.addEventListener('touchend', this.handleDocumentClick
, false);
163 componentWillUnmount () {
164 document
.removeEventListener('click', this.handleDocumentClick
, false);
165 document
.removeEventListener('touchend', this.handleDocumentClick
, false);
173 const { intl
} = this.props
;
176 search: intl
.formatMessage(messages
.emoji_search
),
177 notfound: intl
.formatMessage(messages
.emoji_not_found
),
179 search: intl
.formatMessage(messages
.search_results
),
180 recent: intl
.formatMessage(messages
.recent
),
181 people: intl
.formatMessage(messages
.people
),
182 nature: intl
.formatMessage(messages
.nature
),
183 foods: intl
.formatMessage(messages
.food
),
184 activity: intl
.formatMessage(messages
.activity
),
185 places: intl
.formatMessage(messages
.travel
),
186 objects: intl
.formatMessage(messages
.objects
),
187 symbols: intl
.formatMessage(messages
.symbols
),
188 flags: intl
.formatMessage(messages
.flags
),
189 custom: intl
.formatMessage(messages
.custom
),
194 handleClick
= emoji
=> {
196 emoji
.native = emoji
.colons
;
199 this.props
.onClose();
200 this.props
.onPick(emoji
);
203 handleModifierOpen
= () => {
204 this.setState({ modifierOpen: true });
207 handleModifierClose
= () => {
208 this.setState({ modifierOpen: false });
211 handleModifierChange
= modifier
=> {
212 if (modifier
!== this.state
.modifier
) {
213 this.setState({ modifier
});
218 const { style
, intl
} = this.props
;
219 const title
= intl
.formatMessage(messages
.emoji
);
220 const { modifierOpen
, modifier
} = this.state
;
223 <div className
={classNames('emoji-picker-dropdown__menu', { selecting: modifierOpen
})} style
={style
} ref
={this.setRef
}>
232 i18n
={this.getI18n()}
233 onClick
={this.handleClick
}
235 backgroundImageFn
={backgroundImageFn
}
239 active
={modifierOpen
}
241 onOpen
={this.handleModifierOpen
}
242 onClose
={this.handleModifierClose
}
243 onChange
={this.handleModifierChange
}
252 export default class EmojiPickerDropdown
extends React
.PureComponent
{
255 custom_emojis: ImmutablePropTypes
.list
,
256 intl: PropTypes
.object
.isRequired
,
257 onPickEmoji: PropTypes
.func
.isRequired
,
268 onShowDropdown
= () => {
269 this.setState({ active: true });
272 onHideDropdown
= () => {
273 this.setState({ active: false });
277 if (!e
.key
|| e
.key
=== 'Enter') {
278 if (this.state
.active
) {
279 this.onHideDropdown();
281 this.onShowDropdown();
286 handleKeyDown
= e
=> {
287 if (e
.key
=== 'Escape') {
288 this.onHideDropdown();
292 setTargetRef
= c
=> {
301 const { intl
, onPickEmoji
} = this.props
;
302 const title
= intl
.formatMessage(messages
.emoji
);
303 const { active
} = this.state
;
306 <div className
='emoji-picker-dropdown' onKeyDown
={this.handleKeyDown
}>
307 <div ref
={this.setTargetRef
} className
='emoji-button' title
={title
} aria
-label
={title
} aria
-expanded
={active
} role
='button' onClick
={this.onToggle
} onKeyDown
={this.onToggle
} tabIndex
={0}>
311 src
={`${assetHost}/emoji/1f602.svg`}
315 <Overlay show
={active
} placement
='bottom' target
={this.findTarget
}>
317 custom_emojis
={this.props
.custom_emojis
}
318 onClose
={this.onHideDropdown
}