2016-07-06 19:45:34 +00:00
|
|
|
import '../vendor/raf-polyfill'
|
2016-06-02 15:26:48 +00:00
|
|
|
|
2016-05-31 14:36:52 +00:00
|
|
|
import React from 'react'
|
2016-07-08 17:56:29 +00:00
|
|
|
import ReactDOM from 'react-dom'
|
2016-05-31 14:36:52 +00:00
|
|
|
import data from '../../data'
|
2016-06-02 17:21:31 +00:00
|
|
|
|
2016-07-07 18:22:02 +00:00
|
|
|
import {store, frequently} from '../utils'
|
2016-06-02 17:21:31 +00:00
|
|
|
import {Anchors, Category, Preview, Search} from '.'
|
2016-06-02 15:26:48 +00:00
|
|
|
|
2016-07-08 17:56:29 +00:00
|
|
|
const RECENT_CATEGORY = { name: 'Recent', emojis: null }
|
|
|
|
const SEARCH_CATEGORY = { name: 'Search', emojis: null, anchor: RECENT_CATEGORY }
|
|
|
|
|
|
|
|
const CATEGORIES = [
|
|
|
|
SEARCH_CATEGORY,
|
|
|
|
RECENT_CATEGORY,
|
2016-06-02 15:26:48 +00:00
|
|
|
].concat(data.categories)
|
2016-05-31 14:36:52 +00:00
|
|
|
|
|
|
|
export default class Picker extends React.Component {
|
2016-05-31 18:39:30 +00:00
|
|
|
constructor(props) {
|
|
|
|
super(props)
|
|
|
|
this.testStickyPosition()
|
|
|
|
|
|
|
|
this.state = {
|
2016-07-06 17:35:09 +00:00
|
|
|
skin: store.get('skin') || props.skin,
|
2016-05-31 18:39:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-09 00:22:06 +00:00
|
|
|
componentWillReceiveProps(props) {
|
2016-07-06 17:35:09 +00:00
|
|
|
if (props.skin && !store.get('skin')) {
|
|
|
|
this.setState({ skin: props.skin })
|
|
|
|
}
|
2016-06-09 00:22:06 +00:00
|
|
|
}
|
|
|
|
|
2016-05-31 18:39:30 +00:00
|
|
|
componentDidUpdate() {
|
2016-07-08 17:56:29 +00:00
|
|
|
this.updateCategoriesSize()
|
2016-05-31 18:39:30 +00:00
|
|
|
this.handleScroll()
|
|
|
|
}
|
|
|
|
|
|
|
|
testStickyPosition() {
|
2016-05-31 16:35:08 +00:00
|
|
|
var stickyTestElement = document.createElement('div')
|
|
|
|
for (let prefix of ['', '-webkit-', '-ms-', '-moz-', '-o-']) {
|
|
|
|
stickyTestElement.style.position = `${prefix}sticky`
|
|
|
|
}
|
|
|
|
|
|
|
|
this.hasStickyPosition = !!stickyTestElement.style.position.length
|
|
|
|
}
|
|
|
|
|
2016-05-31 14:36:52 +00:00
|
|
|
handleEmojiOver(emoji) {
|
|
|
|
var { preview } = this.refs
|
|
|
|
preview.setState({ emoji: emoji })
|
2016-05-31 20:53:06 +00:00
|
|
|
clearTimeout(this.leaveTimeout)
|
|
|
|
}
|
|
|
|
|
|
|
|
handleEmojiLeave(emoji) {
|
|
|
|
this.leaveTimeout = setTimeout(() => {
|
|
|
|
var { preview } = this.refs
|
|
|
|
preview.setState({ emoji: null })
|
|
|
|
}, 16)
|
2016-05-31 14:36:52 +00:00
|
|
|
}
|
|
|
|
|
2016-07-07 18:22:02 +00:00
|
|
|
handleEmojiClick(emoji) {
|
|
|
|
this.props.onClick(emoji)
|
|
|
|
frequently.add(emoji)
|
|
|
|
|
2016-07-08 17:56:29 +00:00
|
|
|
var component = this.refs['category-1']
|
|
|
|
if (component) {
|
2016-07-07 18:22:02 +00:00
|
|
|
let maxMargin = component.maxMargin
|
|
|
|
component.forceUpdate()
|
|
|
|
|
|
|
|
window.requestAnimationFrame(() => {
|
2016-07-08 17:56:29 +00:00
|
|
|
component.memoizeSize()
|
2016-07-07 18:22:02 +00:00
|
|
|
if (maxMargin == component.maxMargin) return
|
|
|
|
|
2016-07-08 17:56:29 +00:00
|
|
|
this.updateCategoriesSize()
|
2016-07-07 18:22:02 +00:00
|
|
|
this.handleScrollPaint()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-31 16:35:08 +00:00
|
|
|
handleScroll() {
|
2016-06-02 15:26:48 +00:00
|
|
|
if (!this.waitingForPaint) {
|
|
|
|
this.waitingForPaint = true
|
|
|
|
window.requestAnimationFrame(this.handleScrollPaint.bind(this))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
handleScrollPaint() {
|
|
|
|
this.waitingForPaint = false
|
|
|
|
|
2016-05-31 16:35:08 +00:00
|
|
|
var target = this.refs.scroll,
|
2016-06-02 15:26:48 +00:00
|
|
|
scrollTop = target.scrollTop,
|
2016-07-08 17:56:29 +00:00
|
|
|
scrollingDown = scrollTop > (this.scrollTop || 0),
|
|
|
|
activeCategory = null
|
2016-05-31 16:35:08 +00:00
|
|
|
|
2016-07-08 17:56:29 +00:00
|
|
|
for (let i = 0, l = CATEGORIES.length; i < l; i++) {
|
|
|
|
let ii = scrollingDown ? (CATEGORIES.length - 1 - i) : i,
|
|
|
|
category = CATEGORIES[ii],
|
2016-06-02 18:58:19 +00:00
|
|
|
component = this.refs[`category-${ii}`]
|
2016-06-02 15:26:48 +00:00
|
|
|
|
|
|
|
if (component) {
|
|
|
|
let active = component.handleScroll(scrollTop)
|
|
|
|
if (active && !activeCategory) {
|
2016-07-08 17:56:29 +00:00
|
|
|
if (category.anchor) category = category.anchor
|
2016-06-02 15:26:48 +00:00
|
|
|
activeCategory = category
|
2016-05-31 16:35:08 +00:00
|
|
|
}
|
2016-06-02 15:26:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (activeCategory) {
|
|
|
|
let { anchors } = this.refs,
|
|
|
|
{ name: categoryName } = activeCategory
|
|
|
|
|
|
|
|
if (anchors.state.selected != categoryName) {
|
|
|
|
anchors.setState({ selected: categoryName })
|
|
|
|
}
|
2016-05-31 16:35:08 +00:00
|
|
|
}
|
2016-06-02 15:26:48 +00:00
|
|
|
|
|
|
|
this.scrollTop = scrollTop
|
2016-05-31 16:35:08 +00:00
|
|
|
}
|
|
|
|
|
2016-05-31 18:39:30 +00:00
|
|
|
handleSearch(emojis) {
|
2016-07-08 17:56:29 +00:00
|
|
|
SEARCH_CATEGORY.emojis = emojis
|
|
|
|
|
|
|
|
for (let i = 0, l = CATEGORIES.length; i < l; i++) {
|
|
|
|
let component = this.refs[`category-${i}`]
|
|
|
|
|
|
|
|
if (component && component.props.name != 'Search') {
|
|
|
|
let DOMNode = ReactDOM.findDOMNode(component),
|
|
|
|
display = emojis ? 'none' : null
|
|
|
|
|
|
|
|
if (DOMNode) DOMNode.style.display = display
|
|
|
|
}
|
2016-05-31 18:39:30 +00:00
|
|
|
}
|
2016-07-08 17:56:29 +00:00
|
|
|
|
|
|
|
this.forceUpdate()
|
2016-05-31 18:39:30 +00:00
|
|
|
}
|
|
|
|
|
2016-06-02 18:58:19 +00:00
|
|
|
handleAnchorClick(category, i) {
|
|
|
|
var component = this.refs[`category-${i}`],
|
2016-07-08 20:28:43 +00:00
|
|
|
{ scroll, anchors } = this.refs,
|
|
|
|
scrollToComponent = null
|
2016-06-02 18:58:19 +00:00
|
|
|
|
2016-07-08 20:28:43 +00:00
|
|
|
scrollToComponent = () => {
|
|
|
|
if (component) {
|
|
|
|
let { top } = component
|
|
|
|
|
|
|
|
if (category.name == 'Recent') {
|
|
|
|
top = 0
|
|
|
|
} else {
|
|
|
|
top += 1
|
|
|
|
}
|
2016-06-02 18:58:19 +00:00
|
|
|
|
2016-07-08 20:28:43 +00:00
|
|
|
scroll.scrollTop = top
|
2016-06-02 18:58:19 +00:00
|
|
|
}
|
2016-07-08 20:28:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (SEARCH_CATEGORY.emojis) {
|
|
|
|
this.handleSearch(null)
|
|
|
|
this.refs.search.clear()
|
2016-06-02 18:58:19 +00:00
|
|
|
|
2016-07-08 20:28:43 +00:00
|
|
|
window.requestAnimationFrame(scrollToComponent)
|
|
|
|
} else {
|
|
|
|
scrollToComponent()
|
2016-06-02 18:58:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-09 00:22:06 +00:00
|
|
|
handleSkinChange(skin) {
|
2016-07-06 17:35:09 +00:00
|
|
|
var newState = { skin: skin }
|
|
|
|
|
|
|
|
this.setState(newState)
|
|
|
|
store.update(newState)
|
2016-06-09 00:22:06 +00:00
|
|
|
}
|
|
|
|
|
2016-07-08 17:56:29 +00:00
|
|
|
updateCategoriesSize() {
|
|
|
|
for (let i = 0, l = CATEGORIES.length; i < l; i++) {
|
|
|
|
let component = this.refs[`category-${i}`]
|
|
|
|
if (component) component.memoizeSize()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-31 14:36:52 +00:00
|
|
|
render() {
|
2016-06-09 00:22:06 +00:00
|
|
|
var { perLine, emojiSize, sheetURL } = this.props,
|
|
|
|
{ skin } = this.state,
|
2016-05-31 18:48:15 +00:00
|
|
|
width = (perLine * (emojiSize + 12)) + 12 + 2
|
2016-05-31 14:36:52 +00:00
|
|
|
|
2016-05-31 18:48:15 +00:00
|
|
|
return <div style={{width: width}} className='emoji-picker'>
|
2016-05-31 14:36:52 +00:00
|
|
|
<div className='emoji-picker-bar'>
|
2016-06-02 15:26:48 +00:00
|
|
|
<Anchors
|
|
|
|
ref='anchors'
|
2016-07-08 17:56:29 +00:00
|
|
|
categories={CATEGORIES}
|
2016-06-02 18:58:19 +00:00
|
|
|
onAnchorClick={this.handleAnchorClick.bind(this)}
|
2016-06-02 15:26:48 +00:00
|
|
|
/>
|
2016-05-31 14:36:52 +00:00
|
|
|
</div>
|
|
|
|
|
2016-05-31 16:35:08 +00:00
|
|
|
<div ref="scroll" className='emoji-picker-scroll' onScroll={this.handleScroll.bind(this)}>
|
2016-05-31 18:39:30 +00:00
|
|
|
<Search
|
2016-07-08 20:28:43 +00:00
|
|
|
ref='search'
|
2016-05-31 18:39:30 +00:00
|
|
|
onSearch={this.handleSearch.bind(this)}
|
2016-05-31 14:36:52 +00:00
|
|
|
/>
|
|
|
|
|
2016-07-08 17:56:29 +00:00
|
|
|
{CATEGORIES.map((category, i) => {
|
2016-05-31 14:36:52 +00:00
|
|
|
return <Category
|
2016-05-31 16:35:08 +00:00
|
|
|
ref={`category-${i}`}
|
2016-05-31 14:36:52 +00:00
|
|
|
key={category.name}
|
|
|
|
name={category.name}
|
|
|
|
emojis={category.emojis}
|
2016-07-07 18:22:02 +00:00
|
|
|
perLine={perLine}
|
2016-05-31 16:35:08 +00:00
|
|
|
hasStickyPosition={this.hasStickyPosition}
|
2016-05-31 14:36:52 +00:00
|
|
|
emojiProps={{
|
2016-05-31 23:36:50 +00:00
|
|
|
skin: skin,
|
2016-05-31 14:36:52 +00:00
|
|
|
size: emojiSize,
|
|
|
|
sheetURL: sheetURL,
|
|
|
|
onOver: this.handleEmojiOver.bind(this),
|
2016-05-31 20:53:06 +00:00
|
|
|
onLeave: this.handleEmojiLeave.bind(this),
|
2016-07-07 18:22:02 +00:00
|
|
|
onClick: this.handleEmojiClick.bind(this),
|
2016-05-31 14:36:52 +00:00
|
|
|
}}
|
|
|
|
/>
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
|
2016-06-01 19:25:45 +00:00
|
|
|
<div className='emoji-picker-bar'>
|
2016-05-31 14:36:52 +00:00
|
|
|
<Preview
|
|
|
|
ref='preview'
|
|
|
|
emojiProps={{
|
|
|
|
size: 38,
|
|
|
|
sheetURL: sheetURL,
|
|
|
|
}}
|
2016-06-09 00:22:06 +00:00
|
|
|
skinsProps={{
|
|
|
|
skin: skin,
|
|
|
|
onChange: this.handleSkinChange.bind(this)
|
|
|
|
}}
|
2016-05-31 14:36:52 +00:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Picker.propTypes = {
|
|
|
|
onClick: React.PropTypes.func,
|
2016-05-31 23:36:50 +00:00
|
|
|
skin: React.PropTypes.number,
|
2016-05-31 14:36:52 +00:00
|
|
|
perLine: React.PropTypes.number,
|
|
|
|
emojiSize: React.PropTypes.number,
|
|
|
|
sheetURL: React.PropTypes.string.isRequired,
|
|
|
|
}
|
|
|
|
|
|
|
|
Picker.defaultProps = {
|
|
|
|
onClick: (() => {}),
|
2016-05-31 21:23:51 +00:00
|
|
|
emojiSize: 24,
|
2016-05-31 14:36:52 +00:00
|
|
|
perLine: 9,
|
2016-05-31 23:36:50 +00:00
|
|
|
skin: 1,
|
2016-05-31 14:36:52 +00:00
|
|
|
}
|