- {Emoji({ key: emoji.id, emoji: emoji, ...emojiProps })}
+ {NimbleEmoji({
+ key: emoji.id,
+ emoji: emoji,
+ data: this.data,
+ ...emojiProps,
+ })}
@@ -66,7 +73,7 @@ export default class Preview extends React.PureComponent {
{idleEmoji &&
idleEmoji.length &&
- Emoji({ emoji: idleEmoji, ...emojiProps })}
+ NimbleEmoji({ emoji: idleEmoji, data: this.data, ...emojiProps })}
diff --git a/src/components/search.js b/src/components/search.js
index 71e8dc4..21f3cdb 100644
--- a/src/components/search.js
+++ b/src/components/search.js
@@ -1,11 +1,14 @@
import React from 'react'
import PropTypes from 'prop-types'
-import emojiIndex from '../utils/emoji-index'
+
+import NimbleEmojiIndex from '../utils/emoji-index/nimble-emoji-index'
export default class Search extends React.PureComponent {
constructor(props) {
super(props)
+ this.data = props.data
+ this.emojiIndex = new NimbleEmojiIndex(this.data)
this.setRef = this.setRef.bind(this)
this.handleChange = this.handleChange.bind(this)
}
@@ -14,7 +17,7 @@ export default class Search extends React.PureComponent {
var value = this.input.value
this.props.onSearch(
- emojiIndex.search(value, {
+ this.emojiIndex.search(value, {
emojisToShowFilter: this.props.emojisToShowFilter,
maxResults: this.props.maxResults,
include: this.props.include,
diff --git a/src/data/index.js b/src/data/index.js
deleted file mode 100644
index dc0a446..0000000
--- a/src/data/index.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import buildSearch from '../utils/build-search'
-import data from './data'
-
-function uncompress(list) {
- for (var short_name in list) {
- var datum = list[short_name]
-
- if (!datum.short_names) datum.short_names = []
- datum.short_names.unshift(short_name)
-
- datum.sheet_x = datum.sheet[0]
- datum.sheet_y = datum.sheet[1]
- delete datum.sheet
-
- if (!datum.text) datum.text = ''
- if (datum.added_in !== null && !datum.added_in) datum.added_in = '6.0'
-
- datum.search = buildSearch({
- short_names: datum.short_names,
- name: datum.name,
- keywords: datum.keywords,
- emoticons: datum.emoticons,
- })
- }
-}
-
-uncompress(data.emojis)
-uncompress(data.skins)
-
-export default data
diff --git a/src/index.js b/src/index.js
index e831839..10c0f6d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,6 +1,14 @@
-import emojiIndex from './utils/emoji-index'
+import emojiIndex from './utils/emoji-index/emoji-index'
import store from './utils/store'
import frequently from './utils/frequently'
-export { Picker, Emoji, Category } from './components'
+export {
+ Picker,
+ NimblePicker,
+ Emoji,
+ NimbleEmoji,
+ Category,
+} from './components'
+
+export { NimbleEmojiIndex } from './utils/emoji-index/nimble-emoji-index'
export { emojiIndex, store, frequently }
diff --git a/src/utils/build-search.js b/src/utils/build-search.js
deleted file mode 100644
index c2ca655..0000000
--- a/src/utils/build-search.js
+++ /dev/null
@@ -1,26 +0,0 @@
-export default (data) => {
- const search = []
-
- var addToSearch = (strings, split) => {
- if (!strings) {
- return
- }
-
- ;(Array.isArray(strings) ? strings : [strings]).forEach((string) => {
- ;(split ? string.split(/[-|_|\s]+/) : [string]).forEach((s) => {
- s = s.toLowerCase()
-
- if (search.indexOf(s) == -1) {
- search.push(s)
- }
- })
- })
- }
-
- addToSearch(data.short_names, true)
- addToSearch(data.name, true)
- addToSearch(data.keywords, false)
- addToSearch(data.emoticons, false)
-
- return search.join(',')
-}
diff --git a/src/utils/data.js b/src/utils/data.js
new file mode 100644
index 0000000..00e7623
--- /dev/null
+++ b/src/utils/data.js
@@ -0,0 +1,106 @@
+const mapping = {
+ name: 'a',
+ unified: 'b',
+ non_qualified: 'c',
+ has_img_apple: 'd',
+ has_img_google: 'e',
+ has_img_twitter: 'f',
+ has_img_emojione: 'g',
+ has_img_facebook: 'h',
+ has_img_messenger: 'i',
+ keywords: 'j',
+ sheet: 'k',
+ emoticons: 'l',
+ text: 'm',
+ short_names: 'n',
+ added_in: 'o',
+}
+
+const buildSearch = (emoji) => {
+ const search = []
+
+ var addToSearch = (strings, split) => {
+ if (!strings) {
+ return
+ }
+
+ ;(Array.isArray(strings) ? strings : [strings]).forEach((string) => {
+ ;(split ? string.split(/[-|_|\s]+/) : [string]).forEach((s) => {
+ s = s.toLowerCase()
+
+ if (search.indexOf(s) == -1) {
+ search.push(s)
+ }
+ })
+ })
+ }
+
+ addToSearch(emoji.short_names, true)
+ addToSearch(emoji.name, true)
+ addToSearch(emoji.keywords, false)
+ addToSearch(emoji.emoticons, false)
+
+ return search.join(',')
+}
+
+const compress = (emoji) => {
+ emoji.short_names = emoji.short_names.filter((short_name) => {
+ return short_name !== emoji.short_name
+ })
+ delete emoji.short_name
+
+ emoji.sheet = [emoji.sheet_x, emoji.sheet_y]
+ delete emoji.sheet_x
+ delete emoji.sheet_y
+
+ emoji.added_in = parseInt(emoji.added_in)
+ if (emoji.added_in === 6) {
+ delete emoji.added_in
+ }
+
+ for (let key in mapping) {
+ emoji[mapping[key]] = emoji[key]
+ delete emoji[key]
+ }
+
+ for (let key in emoji) {
+ let value = emoji[key]
+
+ if (Array.isArray(value) && !value.length) {
+ delete emoji[key]
+ } else if (typeof value === 'string' && !value.length) {
+ delete emoji[key]
+ } else if (value === null) {
+ delete emoji[key]
+ }
+ }
+}
+
+const uncompress = (data) => {
+ data.compressed = false
+
+ for (let id in data.emojis) {
+ let emoji = data.emojis[id]
+
+ for (let key in mapping) {
+ emoji[key] = emoji[mapping[key]]
+ delete emoji[mapping[key]]
+ }
+
+ if (!emoji.short_names) emoji.short_names = []
+ emoji.short_names.unshift(id)
+
+ emoji.sheet_x = emoji.sheet[0]
+ emoji.sheet_y = emoji.sheet[1]
+ delete emoji.sheet
+
+ if (!emoji.text) emoji.text = ''
+
+ if (!emoji.added_in) emoji.added_in = 6
+ emoji.added_in = emoji.added_in.toFixed(1)
+
+ emoji.search = buildSearch(emoji)
+ }
+}
+
+module.exports = { buildSearch, compress, uncompress }
diff --git a/src/utils/emoji-index.js b/src/utils/emoji-index.js
deleted file mode 100644
index 950d15d..0000000
--- a/src/utils/emoji-index.js
+++ /dev/null
@@ -1,180 +0,0 @@
-import data from '../data'
-import { getData, getSanitizedData, intersect } from '.'
-
-var originalPool = {}
-var index = {}
-var emojisList = {}
-var emoticonsList = {}
-var customEmojisList = []
-
-for (let emoji in data.emojis) {
- let emojiData = data.emojis[emoji],
- { short_names, emoticons } = emojiData,
- id = short_names[0]
-
- if (emoticons) {
- emoticons.forEach((emoticon) => {
- if (emoticonsList[emoticon]) {
- return
- }
-
- emoticonsList[emoticon] = id
- })
- }
-
- emojisList[id] = getSanitizedData(id)
- originalPool[id] = emojiData
-}
-
-function clearCustomEmojis(pool) {
- customEmojisList.forEach((emoji) => {
- let emojiId = emoji.id || emoji.short_names[0]
-
- delete pool[emojiId]
- delete emojisList[emojiId]
- })
-}
-
-function addCustomToPool(custom, pool) {
- if (customEmojisList.length) clearCustomEmojis(pool)
-
- custom.forEach((emoji) => {
- let emojiId = emoji.id || emoji.short_names[0]
-
- if (emojiId && !pool[emojiId]) {
- pool[emojiId] = getData(emoji)
- emojisList[emojiId] = getSanitizedData(emoji)
- }
- })
-
- customEmojisList = custom
- index = {}
-}
-
-function search(
- value,
- { emojisToShowFilter, maxResults, include, exclude, custom = [] } = {},
-) {
- if (customEmojisList != custom) addCustomToPool(custom, originalPool)
-
- maxResults || (maxResults = 75)
- include || (include = [])
- exclude || (exclude = [])
-
- var results = null,
- pool = originalPool
-
- if (value.length) {
- if (value == '-' || value == '-1') {
- return [emojisList['-1']]
- }
-
- var values = value.toLowerCase().split(/[\s|,|\-|_]+/),
- allResults = []
-
- if (values.length > 2) {
- values = [values[0], values[1]]
- }
-
- if (include.length || exclude.length) {
- pool = {}
-
- data.categories.forEach((category) => {
- let isIncluded =
- include && include.length ? include.indexOf(category.id) > -1 : true
- let isExcluded =
- exclude && exclude.length ? exclude.indexOf(category.id) > -1 : false
- if (!isIncluded || isExcluded) {
- return
- }
-
- category.emojis.forEach(
- (emojiId) => (pool[emojiId] = data.emojis[emojiId]),
- )
- })
-
- if (custom.length) {
- let customIsIncluded =
- include && include.length ? include.indexOf('custom') > -1 : true
- let customIsExcluded =
- exclude && exclude.length ? exclude.indexOf('custom') > -1 : false
- if (customIsIncluded && !customIsExcluded) {
- addCustomToPool(custom, pool)
- }
- }
- }
-
- allResults = values
- .map((value) => {
- var aPool = pool,
- aIndex = index,
- length = 0
-
- for (let charIndex = 0; charIndex < value.length; charIndex++) {
- const char = value[charIndex]
- length++
-
- aIndex[char] || (aIndex[char] = {})
- aIndex = aIndex[char]
-
- if (!aIndex.results) {
- let scores = {}
-
- aIndex.results = []
- aIndex.pool = {}
-
- for (let id in aPool) {
- let emoji = aPool[id],
- { search } = emoji,
- sub = value.substr(0, length),
- subIndex = search.indexOf(sub)
-
- if (subIndex != -1) {
- let score = subIndex + 1
- if (sub == id) score = 0
-
- aIndex.results.push(emojisList[id])
- aIndex.pool[id] = emoji
-
- scores[id] = score
- }
- }
-
- aIndex.results.sort((a, b) => {
- var aScore = scores[a.id],
- bScore = scores[b.id]
-
- return aScore - bScore
- })
- }
-
- aPool = aIndex.pool
- }
-
- return aIndex.results
- })
- .filter((a) => a)
-
- if (allResults.length > 1) {
- results = intersect.apply(null, allResults)
- } else if (allResults.length) {
- results = allResults[0]
- } else {
- results = []
- }
- }
-
- if (results) {
- if (emojisToShowFilter) {
- results = results.filter((result) => emojisToShowFilter(pool[result.id]))
- }
-
- if (results && results.length > maxResults) {
- results = results.slice(0, maxResults)
- }
- }
-
- return results
-}
-
-export default { search, emojis: emojisList, emoticons: emoticonsList }
diff --git a/src/utils/emoji-index/emoji-index.js b/src/utils/emoji-index/emoji-index.js
new file mode 100644
index 0000000..0a8d3b1
--- /dev/null
+++ b/src/utils/emoji-index/emoji-index.js
@@ -0,0 +1,11 @@
+import data from '../../../data/all.json'
+import NimbleEmojiIndex from './nimble-emoji-index'
+
+const emojiIndex = new NimbleEmojiIndex(data)
+const { emojis, emoticons } = emojiIndex
+
+function search() {
+ return emojiIndex.search(...arguments)
+}
+
+export default { search, emojis, emoticons }
diff --git a/src/utils/emoji-index/nimble-emoji-index.js b/src/utils/emoji-index/nimble-emoji-index.js
new file mode 100644
index 0000000..adb269b
--- /dev/null
+++ b/src/utils/emoji-index/nimble-emoji-index.js
@@ -0,0 +1,196 @@
+import { getData, getSanitizedData, intersect } from '..'
+import { uncompress } from '../data'
+
+export default class NimbleEmojiIndex {
+ constructor(data) {
+ if (data.compressed) {
+ uncompress(data)
+ }
+
+ this.data = data || {}
+ this.originalPool = {}
+ this.index = {}
+ this.emojis = {}
+ this.emoticons = {}
+ this.customEmojisList = []
+
+ this.buildIndex()
+ }
+
+ buildIndex() {
+ for (let emoji in this.data.emojis) {
+ let emojiData = this.data.emojis[emoji],
+ { short_names, emoticons } = emojiData,
+ id = short_names[0]
+
+ if (emoticons) {
+ emoticons.forEach((emoticon) => {
+ if (this.emoticons[emoticon]) {
+ return
+ }
+
+ this.emoticons[emoticon] = id
+ })
+ }
+
+ this.emojis[id] = getSanitizedData(id, null, null, this.data)
+ this.originalPool[id] = emojiData
+ }
+ }
+
+ clearCustomEmojis(pool) {
+ this.customEmojisList.forEach((emoji) => {
+ let emojiId = emoji.id || emoji.short_names[0]
+
+ delete pool[emojiId]
+ delete emojisList[emojiId]
+ })
+ }
+
+ addCustomToPool(custom, pool) {
+ if (this.customEmojisList.length) this.clearCustomEmojis(pool)
+
+ custom.forEach((emoji) => {
+ let emojiId = emoji.id || emoji.short_names[0]
+
+ if (emojiId && !pool[emojiId]) {
+ pool[emojiId] = getData(emoji, null, null, this.data)
+ this.emojis[emojiId] = getSanitizedData(emoji, null, null, this.data)
+ }
+ })
+
+ this.customEmojisList = custom
+ this.index = {}
+ }
+
+ search(
+ value,
+ { emojisToShowFilter, maxResults, include, exclude, custom = [] } = {},
+ ) {
+ if (this.customEmojisList != custom)
+ this.addCustomToPool(custom, this.originalPool)
+
+ maxResults || (maxResults = 75)
+ include || (include = [])
+ exclude || (exclude = [])
+
+ var results = null,
+ pool = this.originalPool
+
+ if (value.length) {
+ if (value == '-' || value == '-1') {
+ return [this.emojis['-1']]
+ }
+
+ var values = value.toLowerCase().split(/[\s|,|\-|_]+/),
+ allResults = []
+
+ if (values.length > 2) {
+ values = [values[0], values[1]]
+ }
+
+ if (include.length || exclude.length) {
+ pool = {}
+
+ this.data.categories.forEach((category) => {
+ let isIncluded =
+ include && include.length ? include.indexOf(category.id) > -1 : true
+ let isExcluded =
+ exclude && exclude.length
+ ? exclude.indexOf(category.id) > -1
+ : false
+ if (!isIncluded || isExcluded) {
+ return
+ }
+
+ category.emojis.forEach(
+ (emojiId) => (pool[emojiId] = this.data.emojis[emojiId]),
+ )
+ })
+
+ if (custom.length) {
+ let customIsIncluded =
+ include && include.length ? include.indexOf('custom') > -1 : true
+ let customIsExcluded =
+ exclude && exclude.length ? exclude.indexOf('custom') > -1 : false
+ if (customIsIncluded && !customIsExcluded) {
+ this.addCustomToPool(custom, pool)
+ }
+ }
+ }
+
+ allResults = values
+ .map((value) => {
+ var aPool = pool,
+ aIndex = this.index,
+ length = 0
+
+ for (let charIndex = 0; charIndex < value.length; charIndex++) {
+ const char = value[charIndex]
+ length++
+
+ aIndex[char] || (aIndex[char] = {})
+ aIndex = aIndex[char]
+
+ if (!aIndex.results) {
+ let scores = {}
+
+ aIndex.results = []
+ aIndex.pool = {}
+
+ for (let id in aPool) {
+ let emoji = aPool[id],
+ { search } = emoji,
+ sub = value.substr(0, length),
+ subIndex = search.indexOf(sub)
+
+ if (subIndex != -1) {
+ let score = subIndex + 1
+ if (sub == id) score = 0
+
+ aIndex.results.push(this.emojis[id])
+ aIndex.pool[id] = emoji
+
+ scores[id] = score
+ }
+ }
+
+ aIndex.results.sort((a, b) => {
+ var aScore = scores[a.id],
+ bScore = scores[b.id]
+
+ return aScore - bScore
+ })
+ }
+
+ aPool = aIndex.pool
+ }
+
+ return aIndex.results
+ })
+ .filter((a) => a)
+
+ if (allResults.length > 1) {
+ results = intersect.apply(null, allResults)
+ } else if (allResults.length) {
+ results = allResults[0]
+ } else {
+ results = []
+ }
+ }
+
+ if (results) {
+ if (emojisToShowFilter) {
+ results = results.filter((result) =>
+ emojisToShowFilter(pool[result.id]),
+ )
+ }
+
+ if (results && results.length > maxResults) {
+ results = results.slice(0, maxResults)
+ }
+ }
+
+ return results
+ }
+}
diff --git a/src/utils/index.js b/src/utils/index.js
index 43bd6f2..241b18b 100644
--- a/src/utils/index.js
+++ b/src/utils/index.js
@@ -1,5 +1,4 @@
-import buildSearch from './build-search'
-import data from '../data'
+import { buildSearch } from './data'
import stringFromCodePoint from '../polyfills/stringFromCodePoint'
const _JSON = JSON
@@ -58,7 +57,7 @@ function getSanitizedData() {
return sanitize(getData(...arguments))
}
-function getData(emoji, skin, set) {
+function getData(emoji, skin, set, data) {
var emojiData = {}
if (typeof emoji == 'string') {
@@ -68,12 +67,12 @@ function getData(emoji, skin, set) {
emoji = matches[1]
if (matches[2]) {
- skin = parseInt(matches[2])
+ skin = parseInt(matches[2], 10)
}
}
- if (data.short_names.hasOwnProperty(emoji)) {
- emoji = data.short_names[emoji]
+ if (data.aliases.hasOwnProperty(emoji)) {
+ emoji = data.aliases[emoji]
}
if (data.emojis.hasOwnProperty(emoji)) {
@@ -82,8 +81,8 @@ function getData(emoji, skin, set) {
return null
}
} else if (emoji.id) {
- if (data.short_names.hasOwnProperty(emoji.id)) {
- emoji.id = data.short_names[emoji.id]
+ if (data.aliases.hasOwnProperty(emoji.id)) {
+ emoji.id = data.aliases[emoji.id]
}
if (data.emojis.hasOwnProperty(emoji.id)) {
@@ -114,7 +113,10 @@ function getData(emoji, skin, set) {
delete emojiData.variations
}
- if (variationData[`has_img_${set}`]) {
+ if (
+ variationData[`has_img_${set}`] == undefined ||
+ variationData[`has_img_${set}`]
+ ) {
emojiData.skin_tone = skin
for (let k in variationData) {
diff --git a/src/utils/shared-props.js b/src/utils/shared-props.js
new file mode 100644
index 0000000..a4987f1
--- /dev/null
+++ b/src/utils/shared-props.js
@@ -0,0 +1,104 @@
+import PropTypes from 'prop-types'
+
+const EmojiPropTypes = {
+ data: PropTypes.object.isRequired,
+ onOver: PropTypes.func,
+ onLeave: PropTypes.func,
+ onClick: PropTypes.func,
+ fallback: PropTypes.func,
+ backgroundImageFn: PropTypes.func,
+ native: PropTypes.bool,
+ forceSize: PropTypes.bool,
+ tooltip: PropTypes.bool,
+ skin: PropTypes.oneOf([1, 2, 3, 4, 5, 6]),
+ sheetSize: PropTypes.oneOf([16, 20, 32, 64]),
+ set: PropTypes.oneOf([
+ 'apple',
+ 'google',
+ 'twitter',
+ 'emojione',
+ 'messenger',
+ 'facebook',
+ ]),
+ size: PropTypes.number.isRequired,
+ emoji: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
+}
+
+const EmojiDefaultProps = {
+ skin: 1,
+ set: 'apple',
+ sheetSize: 64,
+ native: false,
+ forceSize: false,
+ tooltip: false,
+ backgroundImageFn: (set, sheetSize) =>
+ `https://unpkg.com/emoji-datasource-${set}@${EMOJI_DATASOURCE_VERSION}/img/${set}/sheets-256/${sheetSize}.png`,
+ onOver: () => {},
+ onLeave: () => {},
+ onClick: () => {},
+}
+
+const PickerPropTypes = {
+ onClick: PropTypes.func,
+ onSkinChange: PropTypes.func,
+ perLine: PropTypes.number,
+ emojiSize: PropTypes.number,
+ i18n: PropTypes.object,
+ style: PropTypes.object,
+ title: PropTypes.string,
+ emoji: PropTypes.string,
+ color: PropTypes.string,
+ set: EmojiPropTypes.set,
+ skin: EmojiPropTypes.skin,
+ native: PropTypes.bool,
+ backgroundImageFn: EmojiPropTypes.backgroundImageFn,
+ sheetSize: EmojiPropTypes.sheetSize,
+ emojisToShowFilter: PropTypes.func,
+ showPreview: PropTypes.bool,
+ showSkinTones: PropTypes.bool,
+ emojiTooltip: EmojiPropTypes.tooltip,
+ include: PropTypes.arrayOf(PropTypes.string),
+ exclude: PropTypes.arrayOf(PropTypes.string),
+ recent: PropTypes.arrayOf(PropTypes.string),
+ autoFocus: PropTypes.bool,
+ custom: PropTypes.arrayOf(
+ PropTypes.shape({
+ name: PropTypes.string.isRequired,
+ short_names: PropTypes.arrayOf(PropTypes.string).isRequired,
+ emoticons: PropTypes.arrayOf(PropTypes.string),
+ keywords: PropTypes.arrayOf(PropTypes.string),
+ imageUrl: PropTypes.string.isRequired,
+ }),
+ ),
+}
+
+const PickerDefaultProps = {
+ onClick: () => {},
+ onSkinChange: () => {},
+ emojiSize: 24,
+ perLine: 9,
+ i18n: {},
+ style: {},
+ title: 'Emoji Martâ˘',
+ emoji: 'department_store',
+ color: '#ae65c5',
+ set: EmojiDefaultProps.set,
+ skin: null,
+ defaultSkin: EmojiDefaultProps.skin,
+ native: EmojiDefaultProps.native,
+ sheetSize: EmojiDefaultProps.sheetSize,
+ backgroundImageFn: EmojiDefaultProps.backgroundImageFn,
+ emojisToShowFilter: null,
+ showPreview: true,
+ showSkinTones: true,
+ emojiTooltip: EmojiDefaultProps.tooltip,
+ autoFocus: false,
+ custom: [],
+}
+
+export {
+ EmojiPropTypes,
+ EmojiDefaultProps,
+ PickerPropTypes,
+ PickerDefaultProps,
+}