import React from 'react'; import {findDOMNode} from 'react-dom'; import {Actions} from 'nylas-exports'; import {RetinaImg, ScrollRegion} from 'nylas-component-kit'; import EmojiStore from './emoji-store'; import EmojiActions from './emoji-actions'; import emoji from 'node-emoji'; import categorizedEmojiList from './categorized-emoji'; import missingEmojiList from './missing-emoji'; class EmojiButtonPopover extends React.Component { static displayName = 'EmojiButtonPopover'; constructor() { super(); const {categoryNames, categorizedEmoji, categoryPositions} = this.getStateFromStore(); this.state = { emojiName: "Emoji Picker", categoryNames: categoryNames, categorizedEmoji: categorizedEmoji, categoryPositions: categoryPositions, searchValue: "", activeTab: Object.keys(categorizedEmoji)[0], }; } componentDidMount() { this._mounted = true; this.renderCanvas(); } componentWillUnmount() { this._mounted = false; } onMouseDown = (event) => { const emojiName = this.calcEmojiByPosition(this.calcPosition(event)); if (!emojiName) return null; EmojiActions.selectEmoji({emojiName: emojiName, replaceSelection: false}); Actions.closePopover(); } onScroll = () => { const emojiContainer = document.querySelector(".emoji-finder-container"); const tabContainer = document.querySelector(".emoji-tabs"); tabContainer.className = emojiContainer.scrollTop ? "emoji-tabs shadow" : "emoji-tabs"; if (emojiContainer.scrollTop === 0) { this.setState({activeTab: Object.keys(this.state.categorizedEmoji)[0]}); } else { for (const category in this.state.categoryPositions) { if (this.state.categoryPositions.hasOwnProperty(category)) { if (emojiContainer.scrollTop >= this.state.categoryPositions[category].top && emojiContainer.scrollTop <= this.state.categoryPositions[category].bottom) { if (category === 'More People') { this.setState({activeTab: 'People'}); } else { this.setState({activeTab: category}); } } } } } } onHover = (event) => { const emojiName = this.calcEmojiByPosition(this.calcPosition(event)); if (emojiName) { this.setState({emojiName: emojiName}); } else { this.setState({emojiName: "Emoji Picker"}); } } onMouseOut = () => { this.setState({emojiName: "Emoji Picker"}); } onChange = (event) => { const searchValue = event.target.value; if (searchValue.length > 0) { const searchMatches = this.findSearchMatches(searchValue); this.setState({ categorizedEmoji: { 'Search Results': searchMatches, }, categoryPositions: { 'Search Results': { top: 25, bottom: 25 + Math.ceil(searchMatches.length / 8) * 24, }, }, searchValue: searchValue, activeTab: null, }, this.renderCanvas); } else { this.setState(this.getStateFromStore, () => { this.setState({ searchValue: searchValue, activeTab: Object.keys(this.state.categorizedEmoji)[0], }, this.renderCanvas); }); } } getStateFromStore = () => { let categorizedEmoji = categorizedEmojiList; const categoryPositions = {}; let categoryNames = [ 'People', 'More People', 'Nature', 'Food and Drink', 'Activity', 'Travel and Places', 'Objects', 'Symbols', 'Flags', ]; const frequentlyUsedEmoji = EmojiStore.frequentlyUsedEmoji(); if (frequentlyUsedEmoji.length > 0) { categorizedEmoji = {'Frequently Used': frequentlyUsedEmoji}; for (const category in categorizedEmojiList) { if (categorizedEmojiList.hasOwnProperty(category)) { categorizedEmoji[category] = categorizedEmojiList[category]; } } categoryNames = ["Frequently Used"].concat(categoryNames); } // Calculates where each category should be (variable because Frequently // Used may or may not be present) for (const name of categoryNames) { categoryPositions[name] = {top: 0, bottom: 0}; } let verticalPos = 25; for (const category in categoryPositions) { if (categoryPositions.hasOwnProperty(category)) { const height = Math.ceil(categorizedEmoji[category].length / 8) * 24; categoryPositions[category].top = verticalPos; verticalPos += height; categoryPositions[category].bottom = verticalPos; if (category !== 'People') { verticalPos += 24; } } } return { categoryNames: categoryNames, categorizedEmoji: categorizedEmoji, categoryPositions: categoryPositions, }; } scrollToCategory(category) { const container = document.querySelector(".emoji-finder-container"); if (this.state.searchValue.length > 0) { this.setState({searchValue: ""}); this.setState(this.getStateFromStore, () => { this.renderCanvas(); container.scrollTop = this.state.categoryPositions[category].top + 16; }); } else { container.scrollTop = this.state.categoryPositions[category].top + 16; } this.setState({activeTab: category}) } findSearchMatches(searchValue) { // TODO: Find matches for aliases, too. const searchMatches = []; for (const category of Object.keys(categorizedEmojiList)) { categorizedEmojiList[category].forEach((emojiName) => { if (emojiName.indexOf(searchValue) !== -1) { searchMatches.push(emojiName); } }); } return searchMatches; } calcPosition(event) { const rect = event.target.getBoundingClientRect(); const position = { x: event.pageX - rect.left / 2, y: event.pageY - rect.top / 2, }; return position; } calcEmojiByPosition = (position) => { for (const category in this.state.categoryPositions) { if (this.state.categoryPositions.hasOwnProperty(category)) { const LEFT_BOUNDARY = 8; const RIGHT_BOUNDARY = 204; const EMOJI_WIDTH = 24.5; const EMOJI_HEIGHT = 24; const EMOJI_PER_ROW = 8; if (position.x >= LEFT_BOUNDARY && position.x <= RIGHT_BOUNDARY && position.y >= this.state.categoryPositions[category].top && position.y <= this.state.categoryPositions[category].bottom) { const x = Math.round((position.x + 5) / EMOJI_WIDTH); const y = Math.round((position.y - this.state.categoryPositions[category].top + 10) / EMOJI_HEIGHT); const index = x + (y - 1) * EMOJI_PER_ROW - 1; return this.state.categorizedEmoji[category][index]; } } } return null; } renderTabs() { const tabs = []; this.state.categoryNames.forEach((category) => { if (category !== 'More People') { let className = `emoji-tab ${(category.replace(/ /g, '-')).toLowerCase()}` if (category === this.state.activeTab) { className += " active"; } tabs.push(