Merge branch 'master' into custom-emojis

nolan/hinaloe-test
Etienne Lemay 2017-05-27 11:26:59 -04:00
commit 1b77cb8244
15 changed files with 776 additions and 692 deletions

View File

@ -26,7 +26,7 @@ import { Picker } from 'emoji-mart'
| ---- | :------: | ------- | ----------- |
| **autoFocus** | | `false` | Auto focus the search input when mounted |
| **color** | | `#ae65c5` | The top bar anchors select and hover color |
| **emoji** | | `department_store` | The emoji shown when no emojis are hovered |
| **emoji** | | `department_store` | The emoji shown when no emojis are hovered, set to an empty string to show nothing |
| **include** | | `[]` | Only load included categories. Accepts [I18n categories keys](#i18n). Order will be respected, except for the `recent` category which will always be the first. |
| **exclude** | | `[]` | Don't load excluded categories. Accepts [I18n categories keys](#i18n). |
| **custom** | | `[]` | [Custom emojis](#custom-emojis) |

View File

@ -18,11 +18,16 @@
padding: 6px;
}
.emoji-mart-bar {
border: 0 solid #d9d9d9;
}
.emoji-mart-bar:first-child {
border-bottom-width: 1px;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.emoji-mart-bar:last-child {
border-top-width: 1px;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
@ -74,16 +79,17 @@
overflow-y: scroll;
height: 270px;
padding: 0 6px 6px 6px;
border: solid #d9d9d9;
border-width: 1px 0;
}
.emoji-mart-search {
margin-top: 6px;
padding: 0 6px;
}
.emoji-mart-search input {
font-size: 16px;
display: block;
width: 100%;
padding: .2em .6em;
margin-top: 6px;
border-radius: 25px;
border: 1px solid #d9d9d9;
outline: 0;

View File

@ -18,6 +18,18 @@ const CUSTOM_EMOJIS = [
}
]
const CATEGORIES = [
'recent',
'people',
'nature',
'foods',
'activity',
'places',
'objects',
'symbols',
'flags',
]
class Example extends React.Component {
constructor(props) {
super(props)
@ -25,25 +37,49 @@ class Example extends React.Component {
emojiSize: 24,
perLine: 9,
skin: 1,
native: false,
native: true,
set: 'apple',
hidden: false,
currentEmoji: 'thumbsup',
autoFocus: false,
include: [],
exclude: [],
}
}
handleInput(e) {
var { currentTarget } = e,
{ value, type, checked } = currentTarget,
{ type } = currentTarget,
key = currentTarget.getAttribute('data-key'),
mount = currentTarget.getAttribute('data-mount'),
state = {}
if (type == 'checkbox') {
state[key] = checked
var { checked } = currentTarget,
value = currentTarget.getAttribute('data-value')
if (value) {
if (checked) {
state[key] = this.state[key].concat(value)
} else {
state[key] = this.state[key].filter((item) => { return item != value })
}
} else {
state[key] = checked
}
} else {
var { value } = currentTarget
state[key] = parseInt(value)
}
this.setState(state)
if (mount) {
this.setState({ hidden: true }, () => {
state.hidden = false
this.setState(state)
})
} else {
this.setState(state)
}
}
render() {
@ -61,19 +97,25 @@ class Example extends React.Component {
</div>
<div className="row">
{['apple', 'google', 'twitter', 'emojione'].map((set) => {
var props = { disabled: set == this.state.set }
{['native', 'apple', 'google', 'twitter', 'emojione'].map((set) => {
var props = { disabled: !this.state.native && set == this.state.set }
if (set == 'native' && this.state.native) {
props.disabled = true
}
return <button
key={set}
value={set}
onClick={() => this.setState({ set: set })}
onClick={() => {
if (set == 'native') {
this.setState({ native: true })
} else {
this.setState({ set: set, native: false })
}
}}
{...props}>
<Emoji
set={set}
size={24}
emoji='grinning'
/>
{set}
</button>
})}
</div>
@ -94,14 +136,37 @@ class Example extends React.Component {
<Operator>import</Operator> &#123;Picker&#125; <Operator>from</Operator> <String>'emoji-mart'</String>
<br />
<br /><Operator>&lt;</Operator><Variable>Picker</Variable>
<br /> emojiSize<Operator>=</Operator>&#123;<Variable>{this.state.emojiSize}</Variable>&#125; <input type='range' data-key='emojiSize' onChange={this.handleInput.bind(this)} min='16' max='64' value={this.state.emojiSize} />
<br /> emojiSize<Operator>=</Operator>&#123;<Variable>{this.state.emojiSize}</Variable>&#125; <input type='range' data-key='emojiSize' data-mount={true} onChange={this.handleInput.bind(this)} min='16' max='64' value={this.state.emojiSize} />
<br /> perLine<Operator>=</Operator>&#123;<Variable>{this.state.perLine}</Variable>&#125; {this.state.perLine < 10 ? ' ' : ' '} <input type='range' data-key='perLine' onChange={this.handleInput.bind(this)} min='7' max='16' value={this.state.perLine} />
<br /> skin<Operator>=</Operator>&#123;<Variable>{this.state.skin}</Variable>&#125; <input type='range' data-key='skin' onChange={this.handleInput.bind(this)} min='1' max='6' value={this.state.skin} />
<br /> native<Operator>=</Operator>&#123;<Variable>{this.state.native ? 'true' : 'false'}</Variable>&#125;{this.state.native ? ' ' : ''} <input type='checkbox' data-key='native' onChange={this.handleInput.bind(this)} value={this.state.native} />
<br /> set<Operator>=</Operator><String>'{this.state.set}'</String>
<br /> custom<Operator>=</Operator>&#123;<Variable>{'[]'}</Variable>&#125;
<br /> onClick<Operator>=</Operator>&#123;(<Variable>emoji</Variable>) => console.log(<Variable>emoji</Variable>)&#125;
<br /><Operator>/&gt;</Operator>
<br /> autoFocus<Operator>=</Operator>&#123;<Variable>{this.state.autoFocus ? 'true' : 'false'}</Variable>&#125;{this.state.autoFocus ? ' ' : ''} <input type='checkbox' data-key='autoFocus' data-mount={true} onChange={this.handleInput.bind(this)} checked={this.state.autoFocus} />
<div style={{ opacity: this.state.exclude.length ? 0.6 : 1 }}> include<Operator>=</Operator>&#91;
{[0, 2, 4, 6, 8].map((i) => {
let category1 = CATEGORIES[i],
category2 = CATEGORIES[i + 1]
return <div key={i}>
<label style={{ width: '200px', display: 'inline-block' }}> <input type='checkbox' data-key='include' data-value={category1} data-mount={true} onChange={this.handleInput.bind(this)} disabled={this.state.exclude.length} /> <String>'{category1}'</String></label>
{category2 && <label><input type='checkbox' data-key='include' data-value={category2} data-mount={true} onChange={this.handleInput.bind(this)} disabled={this.state.exclude.length} /> <String>'{category2}'</String></label>}
</div>
})}
  &#93;
</div>
<div style={{ opacity: this.state.include.length ? 0.6 : 1 }}> exclude<Operator>=</Operator>&#91;
{[0, 2, 4, 6, 8].map((i) => {
let category1 = CATEGORIES[i],
category2 = CATEGORIES[i + 1]
return <div key={i}>
<label style={{ width: '200px', display: 'inline-block' }}> <input type='checkbox' data-key='exclude' data-value={category1} data-mount={true} onChange={this.handleInput.bind(this)} disabled={this.state.include.length} /> <String>'{category1}'</String></label>
{category2 && <label><input type='checkbox' data-key='exclude' data-value={category2} data-mount={true} onChange={this.handleInput.bind(this)} disabled={this.state.include.length} /> <String>'{category2}'</String></label>}
</div>
})}
  &#93;
</div>
<Operator>/&gt;</Operator>
</pre>
{!this.state.hidden &&
@ -112,7 +177,13 @@ class Example extends React.Component {
native={this.state.native}
set={this.state.set}
custom={CUSTOM_EMOJIS}
onClick={(emoji) => console.log(emoji)}
autoFocus={this.state.autoFocus}
include={this.state.include}
exclude={this.state.exclude}
onClick={(emoji) => {
this.setState({ currentEmoji: emoji.id })
console.log(emoji)
}}
/>
}
@ -133,11 +204,11 @@ class Example extends React.Component {
</pre>
<span style={{ display: 'inline-block', marginTop: 60 }}>
<Emoji
emoji='thumbsup'
size={64}
set={this.state.set}
/>
{Emoji({
emoji: this.state.currentEmoji,
size: 64,
set: this.state.set,
})}
</span>
</div>
@ -156,11 +227,11 @@ class Example extends React.Component {
</pre>
<span style={{ display: 'inline-block', marginTop: 40 }}>
<Emoji
emoji=':thumbsup:'
size={64}
set={this.state.set}
/>
{Emoji({
emoji: `:${this.state.currentEmoji}:`,
size: 64,
set: this.state.set,
})}
</span>
</div>
@ -179,11 +250,11 @@ class Example extends React.Component {
</pre>
<span style={{ display: 'inline-block', marginTop: 40 }}>
<Emoji
emoji=':thumbsup::skin-tone-3:'
size={64}
set={this.state.set}
/>
{Emoji({
emoji: `:${this.state.currentEmoji}::skin-tone-3:`,
size: 64,
set: this.state.set,
})}
</span>
</div>
@ -203,11 +274,11 @@ class Example extends React.Component {
</pre>
<span style={{ display: 'inline-block', marginTop: 60 }}>
<Emoji
emoji=':thumbsup::skin-tone-3:'
size={64}
native={true}
/>
{Emoji({
emoji: `:${this.state.currentEmoji}::skin-tone-3:`,
size: 64,
native: true,
})}
</span>
</div>
</div>

View File

@ -29,8 +29,8 @@
"babel-plugin-transform-object-rest-spread": "6.8.0",
"babel-preset-es2015": "6.6.0",
"babel-preset-react": "6.5.0",
"emoji-datasource": "2.4.4",
"emojilib": "2.0.2",
"emoji-datasource": "^3.0.0",
"emojilib": "^2.2.1",
"inflection": "1.10.0",
"jasmine-core": "^2.5.2",
"karma": "^1.4.0",
@ -40,9 +40,8 @@
"karma-webpack": "^2.0.2",
"mkdirp": "0.5.1",
"prop-types": "^15.5.8",
"react": "15.2.0",
"react-addons-test-utils": "15.2.0",
"react-dom": "15.2.0",
"react": "^15.5.4",
"react-dom": "^15.5.4",
"rimraf": "2.5.2",
"svg-inline-loader": "0.4.1",
"svg-inline-react": "1.0.2",

View File

@ -125,24 +125,23 @@ export default class Category extends React.Component {
</div>
{emojis && emojis.map((emoji) =>
<Emoji
key={emoji.id || emoji}
emoji={emoji}
{...emojiProps}
/>
Emoji({
emoji: emoji,
...emojiProps
})
)}
{emojis && !emojis.length &&
<div>
<div>
<Emoji
{...emojiProps}
size={38}
emoji='sleuth_or_spy'
onOver={null}
onLeave={null}
onClick={null}
/>
{Emoji({
...emojiProps,
size: 38,
emoji: 'sleuth_or_spy',
onOver: null,
onLeave: null,
onClick: null,
})}
</div>
<div className='emoji-mart-no-results-label'>

View File

@ -4,112 +4,100 @@ import data from '../../data'
import { getData, getSanitizedData, unifiedToNative } from '../utils'
const SHEET_COLUMNS = 41
const SHEET_COLUMNS = 49
export default class Emoji extends React.Component {
constructor(props) {
super(props)
const _getPosition = (props) => {
var { sheet_x, sheet_y } = _getData(props),
multiply = 100 / (SHEET_COLUMNS - 1)
this.hasSkinVariations = !!this.getData().skin_variations
}
return `${multiply * sheet_x}% ${multiply * sheet_y}%`
}
shouldComponentUpdate(nextProps) {
return (
this.hasSkinVariations && nextProps.skin != this.props.skin ||
nextProps.size != this.props.size ||
nextProps.native != this.props.native ||
nextProps.set != this.props.set ||
nextProps.emoji != this.props.emoji
)
}
const _getData = (props) => {
var { emoji, skin, set } = props
return getData(emoji, skin, set)
}
getPosition() {
var { sheet_x, sheet_y } = this.getData(),
multiply = 100 / (SHEET_COLUMNS - 1)
const _getSanitizedData = (props) => {
var { emoji, skin, set } = props
return getSanitizedData(emoji, skin, set)
}
return `${multiply * sheet_x}% ${multiply * sheet_y}%`
}
const _handleClick = (e, props) => {
if (!props.onClick) { return }
var { onClick } = props,
emoji = _getSanitizedData(props)
getData() {
var { emoji, skin, set } = this.props
return getData(emoji, skin, set)
}
onClick(emoji, e)
}
getSanitizedData() {
var { emoji, skin, set } = this.props
return getSanitizedData(emoji, skin, set)
}
const _handleOver = (e, props) => {
if (!props.onOver) { return }
var { onOver } = props,
emoji = _getSanitizedData(props)
handleClick(e) {
if (!this.props.onClick) { return }
var { onClick } = this.props,
emoji = this.getSanitizedData()
onOver(emoji, e)
}
onClick(emoji, e)
}
const _handleLeave = (e, props) => {
if (!props.onLeave) { return }
var { onLeave } = props,
emoji = _getSanitizedData(props)
handleOver(e) {
if (!this.props.onOver) { return }
var { onOver } = this.props,
emoji = this.getSanitizedData()
onLeave(emoji, e)
}
onOver(emoji, e)
}
handleLeave(e) {
if (!this.props.onLeave) { return }
var { onLeave } = this.props,
emoji = this.getSanitizedData()
onLeave(emoji, e)
}
render() {
var { set, size, sheetSize, native, forceSize, onOver, onLeave, backgroundImageFn } = this.props,
{ unified, custom, imageUrl } = this.getData(),
style = {},
children = this.props.children
if (!unified && !custom) {
return null
const Emoji = (props) => {
for (let k in Emoji.defaultProps) {
if (props[k] == undefined && Emoji.defaultProps[k] != undefined) {
props[k] = Emoji.defaultProps[k]
}
if (native && unified) {
style = { fontSize: size }
children = unifiedToNative(unified)
if (forceSize) {
style.display = 'inline-block'
style.width = size
style.height = size
}
} else if (custom) {
style = {
width: size,
height: size,
display: 'inline-block',
backgroundImage: `url(${imageUrl})`,
backgroundSize: '100%'
}
} else {
style = {
width: size,
height: size,
display: 'inline-block',
backgroundImage: `url(${backgroundImageFn(set, sheetSize)})`,
backgroundSize: `${100 * SHEET_COLUMNS}%`,
backgroundPosition: this.getPosition(),
}
}
return <span
onClick={this.handleClick.bind(this)}
onMouseEnter={this.handleOver.bind(this)}
onMouseLeave={this.handleLeave.bind(this)}
className='emoji-mart-emoji'>
<span style={style}>{children}</span>
</span>
}
var { unified, custom, imageUrl } = _getData(props),
style = {},
children = props.children
if (!unified && !custom) {
return null
}
if (props.native && unified) {
style = { fontSize: props.size }
children = unifiedToNative(unified)
if (props.forceSize) {
style.display = 'inline-block'
style.width = props.size
style.height = props.size
}
} else if (custom) {
style = {
width: props.size,
height: props.size,
display: 'inline-block',
backgroundImage: `url(${imageUrl})`,
backgroundSize: '100%',
}
} else {
style = {
width: props.size,
height: props.size,
display: 'inline-block',
backgroundImage: `url(${props.backgroundImageFn(props.set, props.sheetSize)})`,
backgroundSize: `${100 * SHEET_COLUMNS}%`,
backgroundPosition: _getPosition(props),
}
}
return <span
key={props.emoji.id || props.emoji}
onClick={(e) => _handleClick(e, props)}
onMouseEnter={(e) => _handleOver(e, props)}
onMouseLeave={(e) => _handleLeave(e, props)}
className='emoji-mart-emoji'>
<span style={style}>{children}</span>
</span>
}
Emoji.propTypes = {
@ -135,8 +123,10 @@ Emoji.defaultProps = {
sheetSize: 64,
native: false,
forceSize: false,
backgroundImageFn: ((set, sheetSize) => `https://unpkg.com/emoji-datasource@${EMOJI_DATASOURCE_VERSION}/sheet_${set}_${sheetSize}.png`),
backgroundImageFn: ((set, sheetSize) => `https://unpkg.com/emoji-datasource-${set}@${EMOJI_DATASOURCE_VERSION}/img/${set}/sheets/${sheetSize}.png`),
onOver: (() => {}),
onLeave: (() => {}),
onClick: (() => {}),
}
export default Emoji

View File

@ -58,8 +58,8 @@ export default class Picker extends React.Component {
}
for (let category of data.categories) {
let isIncluded = props.include == undefined ? true : props.include.indexOf(category.name.toLowerCase()) > -1
let isExcluded = props.exclude == undefined ? false : props.exclude.indexOf(category.name.toLowerCase()) > -1
let isIncluded = props.include && props.include.length ? props.include.indexOf(category.name.toLowerCase()) > -1 : true
let isExcluded = props.exclude && props.exclude.length ? props.exclude.indexOf(category.name.toLowerCase()) > -1 : false
if (!isIncluded || isExcluded) { continue }
if (props.emojisToShowFilter) {
@ -86,8 +86,8 @@ export default class Picker extends React.Component {
}
}
let includeRecent = props.include == undefined ? true : props.include.indexOf('recent') > -1
let excludeRecent = props.exclude == undefined ? false : props.exclude.indexOf('recent') > -1
let includeRecent = props.include && props.include.length ? props.include.indexOf('recent') > -1 : true
let excludeRecent = props.exclude && props.exclude.length ? props.exclude.indexOf('recent') > -1 : false
if (includeRecent && !excludeRecent) {
this.categories.unshift(RECENT_CATEGORY)
}
@ -261,6 +261,8 @@ export default class Picker extends React.Component {
}
this.forceUpdate()
this.refs.scroll.scrollTop = 0
this.handleScroll()
}
handleAnchorClick(category, i) {
@ -326,18 +328,18 @@ export default class Picker extends React.Component {
/>
</div>
<div ref="scroll" className='emoji-mart-scroll' onScroll={this.handleScroll.bind(this)}>
<Search
ref='search'
onSearch={this.handleSearch.bind(this)}
i18n={this.i18n}
emojisToShowFilter={emojisToShowFilter}
include={include}
exclude={exclude}
custom={CUSTOM_CATEGORY.emojis}
autoFocus={autoFocus}
/>
<Search
ref='search'
onSearch={this.handleSearch.bind(this)}
i18n={this.i18n}
emojisToShowFilter={emojisToShowFilter}
include={include}
exclude={exclude}
custom={CUSTOM_CATEGORY.emojis}
autoFocus={autoFocus}
/>
<div ref="scroll" className='emoji-mart-scroll' onScroll={this.handleScroll.bind(this)}>
{this.getCategories().map((category, i) => {
return <Category
ref={`category-${i}`}

View File

@ -1,5 +1,5 @@
import React from 'react';
import TestUtils from 'react-addons-test-utils';
import TestUtils from 'react-dom/test-utils';
import Picker from './picker';
const {

View File

@ -29,11 +29,11 @@ export default class Preview extends React.Component {
return <div className='emoji-mart-preview'>
<div className='emoji-mart-preview-emoji'>
<Emoji
key={emoji.id}
emoji={emoji}
{...emojiProps}
/>
{Emoji({
key: emoji.id,
emoji: emoji,
...emojiProps,
})}
</div>
<div className='emoji-mart-preview-data'>
@ -53,10 +53,10 @@ export default class Preview extends React.Component {
} else {
return <div className='emoji-mart-preview'>
<div className='emoji-mart-preview-emoji'>
<Emoji
emoji={idleEmoji}
{...emojiProps}
/>
{idleEmoji && idleEmoji.length && Emoji({
emoji: idleEmoji,
...emojiProps,
})}
</div>
<div className='emoji-mart-preview-data'>

View File

@ -23,14 +23,15 @@ export default class Search extends React.Component {
render() {
var { i18n, autoFocus } = this.props
return <input
ref='input'
type='text'
onChange={this.handleChange.bind(this)}
placeholder={i18n.search}
className='emoji-mart-search'
autoFocus={autoFocus}
/>
return <div className='emoji-mart-search'>
<input
ref='input'
type='text'
onChange={this.handleChange.bind(this)}
placeholder={i18n.search}
autoFocus={autoFocus}
/>
</div>
}
}

View File

@ -16,8 +16,10 @@ export default class Skins extends React.Component {
if (!this.state.opened) {
this.setState({ opened: true })
} else {
onChange(skin)
this.setState({ opened: false })
if (skin != this.props.skin) {
onChange(skin)
}
}
}

View File

@ -6,6 +6,8 @@ import { getData, getSanitizedData, intersect } from '.'
var index = {}
var emojisList = {}
var emoticonsList = {}
var previousInclude = null
var previousExclude = null
for (let emoji in data.emojis) {
let emojiData = data.emojis[emoji],
@ -40,6 +42,10 @@ function search(value, { emojisToShowFilter, maxResults, include, exclude, custo
pool = data.emojis
if (value.length) {
if (value == '-' || value == '-1') {
return [emojisList['-1']]
}
var values = value.toLowerCase().split(/[\s|,|\-|_]+/),
allResults = []
@ -50,15 +56,23 @@ function search(value, { emojisToShowFilter, maxResults, include, exclude, custo
if ((include && include.length) || (exclude && exclude.length)) {
pool = {}
if (previousInclude != include.sort().join(',') || previousExclude != exclude.sort().join(',')) {
previousInclude = include.sort().join(',')
previousExclude = exclude.sort().join(',')
index = {}
}
for (let category of data.categories) {
let isIncluded = include == undefined ? true : include.indexOf(category.name.toLowerCase()) > -1
let isExcluded = exclude == undefined ? false : exclude.indexOf(category.name.toLowerCase()) > -1
let isIncluded = include && include.length ? include.indexOf(category.name.toLowerCase()) > -1 : true
let isExcluded = exclude && exclude.length ? exclude.indexOf(category.name.toLowerCase()) > -1 : false
if (!isIncluded || isExcluded) { continue }
for (let emojiId of category.emojis) {
pool[emojiId] = data.emojis[emojiId]
}
}
} else if (previousInclude || previousExclude) {
index = {}
}
allResults = values.map((value) => {
@ -66,10 +80,6 @@ function search(value, { emojisToShowFilter, maxResults, include, exclude, custo
aIndex = index,
length = 0
if (value == '-' || value == '-1') {
return [emojisList['-1']]
}
for (let char of value.split('')) {
length++

View File

@ -20,11 +20,12 @@ const DEFAULTS = [
]
let frequently = store.get('frequently')
let defaults = {}
function add(emoji) {
var { id } = emoji
frequently || (frequently = {})
frequently || (frequently = defaults)
frequently[id] || (frequently[id] = 0)
frequently[id] += 1
@ -34,10 +35,11 @@ function add(emoji) {
function get(perLine) {
if (!frequently) {
frequently = {}
defaults = {}
Array(perLine).fill('').forEach((_, i) => {
frequently[DEFAULTS[i]] = perLine - i
return Array(perLine).fill('').map((_, i) => {
defaults[DEFAULTS[i]] = perLine - i
return DEFAULTS[i]
})
}

View File

@ -99,8 +99,7 @@ function getData(emoji, skin, set) {
emojiData = JSON.parse(JSON.stringify(emojiData))
var skinKey = SKINS[skin - 1],
variationKey = `${emojiData.unified}-${skinKey}`,
variationData = emojiData.skin_variations[variationKey]
variationData = emojiData.skin_variations[skinKey]
if (!variationData.variations && emojiData.variations) {
delete emojiData.variations

989
yarn.lock

File diff suppressed because it is too large Load Diff