Skip to content

Commit 91fee67

Browse files
committed
add emoji command
1 parent 57c23fb commit 91fee67

File tree

11 files changed

+251
-0
lines changed

11 files changed

+251
-0
lines changed

src/commands/Emoji/Emoji.jsx

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import _ from 'lodash'
2+
import $ from 'jquery'
3+
import React from 'react'
4+
import classnames from 'classnames'
5+
import { mountReactComponent } from 'commands/mount'
6+
import * as Emojilib from 'emojilib'
7+
8+
import 'react-spinner/react-spinner.css'
9+
import styles from './Emoji.scss'
10+
import * as Types from 'types'
11+
import * as Search from 'components/Search'
12+
import Container from 'components/Container'
13+
14+
// set IDs for keys
15+
_.map(Emojilib.lib, (v, k) => {
16+
v.id = k
17+
return v
18+
})
19+
20+
let doesSupportEmoji = () => {
21+
if (!document.createElement('canvas').getContext) return
22+
var context = document.createElement('canvas').getContext('2d')
23+
if (typeof context.fillText != 'function') return
24+
// :smile: String.fromCharCode(55357) + String.fromCharCode(56835)
25+
let smile = String.fromCodePoint(0x1F604)
26+
27+
context.textBaseline = "top"
28+
context.font = "32px Arial"
29+
context.fillText(smile, 0, 0)
30+
return context.getImageData(16, 16, 1, 1).data[0] !== 0
31+
}
32+
33+
let EMOJIS_BY_CATEGORY = _.reduce(Emojilib.lib, (memo, v, k) => {
34+
let category = v.category
35+
if (!memo[category]) {
36+
memo[category] = []
37+
}
38+
memo[category].push(v)
39+
return memo
40+
}, {})
41+
42+
let EmojiItem = (props) => {
43+
return (
44+
<div className={styles.emojiItem} onClick={props.onClick}>
45+
<span className={styles.emojiChar}>
46+
{props.char}
47+
</span>
48+
</div>
49+
)
50+
}
51+
52+
let EmojiCategoriesHeaderItem = (props) => {
53+
let classes = classnames(styles.emojiCategoriesHeaderItem, {
54+
[styles['isActive']]: props.active == props.type
55+
})
56+
return (
57+
<div className={classes} onClick={props.onSelect.bind(null, props.type)} >
58+
<img src={require(`./icons/${props.type}.png`)} />
59+
</div>
60+
)
61+
}
62+
63+
let EmojiCategoriesHeader = (props) => {
64+
var oldProps = props
65+
return (
66+
<div className={styles.emojiCategoriesHeader}>
67+
<EmojiCategoriesHeaderItem type="people" {...props} />
68+
<EmojiCategoriesHeaderItem type="animals_and_nature" {...props} />
69+
<EmojiCategoriesHeaderItem type="food_and_drink" {...props} />
70+
<EmojiCategoriesHeaderItem type="activity" {...props} />
71+
<EmojiCategoriesHeaderItem type="travel_and_places" {...props} />
72+
<EmojiCategoriesHeaderItem type="objects" {...props} />
73+
<EmojiCategoriesHeaderItem type="symbols" {...props} />
74+
<EmojiCategoriesHeaderItem type="flags" {...props} />
75+
</div>
76+
)
77+
}
78+
79+
let EmojiContainer = (props) => {
80+
let items = _.map(props.items, (v) => {
81+
return (
82+
<EmojiItem
83+
onClick={props.onSelect.bind(null, v)}
84+
key={v.id}
85+
{...v}
86+
/>
87+
)
88+
})
89+
return (
90+
<div className={styles.emojiContainer}>
91+
{ items }
92+
</div>
93+
)
94+
}
95+
96+
class EmojiCategories extends React.Component {
97+
constructor() {
98+
super()
99+
this.state = { active: 'people' }
100+
this.onSelectCategory = this.onSelectCategory.bind(this)
101+
}
102+
103+
onSelectCategory(category) {
104+
this.setState({ active: category })
105+
}
106+
107+
render() {
108+
109+
return (
110+
<div className={styles.emojiCategories}>
111+
<EmojiCategoriesHeader
112+
items={this.props.items}
113+
active={this.state.active}
114+
onSelect={this.onSelectCategory}
115+
/>
116+
<EmojiContainer
117+
items={EMOJIS_BY_CATEGORY[this.state.active]}
118+
onSelect={this.props.onSelect}
119+
/>
120+
</div>
121+
)
122+
}
123+
}
124+
EmojiCategories.defaultProps = {
125+
items: EMOJIS_BY_CATEGORY
126+
}
127+
128+
let EmojiSearch = (props) => {
129+
let items = _.reduce(Emojilib.lib, (memo, v, k) => {
130+
if (k.indexOf(props.query) > -1) {
131+
memo.push(v)
132+
}
133+
return memo
134+
}, [])
135+
136+
return (
137+
<div className={styles.emojiSearch}>
138+
<EmojiContainer
139+
items={items}
140+
onSelect={props.onSelect}
141+
/>
142+
</div>
143+
)
144+
}
145+
146+
class Emoji extends React.Component {
147+
constructor() {
148+
super()
149+
this.state = { query: "" }
150+
this.onSelect = this.onSelect.bind(this)
151+
this.onSearch = this.onSearch.bind(this)
152+
}
153+
154+
onSelect(result) {
155+
this.props.onDone(result.char)
156+
}
157+
158+
onSearch(query) {
159+
this.setState({ query: query })
160+
}
161+
162+
render() {
163+
let render
164+
if (this.props.doesSupportEmoji) {
165+
if (this.state.query != "") {
166+
render = <EmojiSearch query={this.state.query} onSelect={this.onSelect}/>
167+
} else {
168+
render = <EmojiCategories onSelect={this.onSelect}/>
169+
}
170+
} else {
171+
render = <p className={styles.noSupport}>Your browser does not support emoji.</p>
172+
}
173+
174+
return (
175+
<Container className={styles.emoji} {...this.props}>
176+
<Search.Input onEsc={this.props.onDone} onSearch={this.onSearch}/>
177+
{ render }
178+
</Container>
179+
)
180+
}
181+
}
182+
Emoji.defaultProps = {
183+
doesSupportEmoji: doesSupportEmoji()
184+
}
185+
Emoji.propTypes = {
186+
onDone: React.PropTypes.func.isRequired
187+
}
188+
189+
export let match = 'emoji'
190+
export let icon = require('./Emoji.png')
191+
export let mount = mountReactComponent.bind(null, Emoji)

src/commands/Emoji/Emoji.png

1.3 KB
Loading

src/commands/Emoji/Emoji.scss

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
.emoji {
2+
height: 290px;
3+
}
4+
.emojiContainer {
5+
overflow: scroll;
6+
height: 165px;
7+
padding: 10px 0;
8+
text-align: center;
9+
border-top: none;
10+
text-align: left;
11+
}
12+
.emojiItem {
13+
display: inline-block;
14+
width: 20px;
15+
height: 20px;
16+
padding: 2px;
17+
line-height: 20px;
18+
overflow: hidden;
19+
cursor: pointer;
20+
&:hover {
21+
background: #eee;
22+
}
23+
}
24+
.emojiCategoriesHeader {
25+
}
26+
.emojiCategoriesHeaderItem {
27+
padding: 5px 15px;
28+
width: 47.5px;
29+
display: inline-block;
30+
text-align: center;
31+
border: 1px solid #eee;
32+
33+
&:not(:last-child) {
34+
border-right: none;
35+
}
36+
37+
&:hover {
38+
cursor: pointer;
39+
img {
40+
opacity: .7;
41+
}
42+
}
43+
44+
img {
45+
vertical-align: middle;
46+
opacity: .5;
47+
height: 16px;
48+
}
49+
50+
&.isActive {
51+
background: #eee;
52+
img {
53+
opacity: 1;
54+
}
55+
}
56+
}
57+
.noSupport {
58+
text-align: center;
59+
color: #aaa;
60+
}
2.38 KB
Loading
2.24 KB
Loading

src/commands/Emoji/icons/flags.png

1.38 KB
Loading
1.99 KB
Loading
1.8 KB
Loading
2.07 KB
Loading
2.09 KB
Loading

0 commit comments

Comments
 (0)