From 35ddcb27f073cf58b8d80f9b4cf05cd128772b01 Mon Sep 17 00:00:00 2001 From: Philipp Heckel Date: Tue, 5 Apr 2022 19:40:34 -0400 Subject: [PATCH] Good enough emoji picker --- web/src/components/EmojiPicker.js | 181 ++++++++++++++++-------------- 1 file changed, 95 insertions(+), 86 deletions(-) diff --git a/web/src/components/EmojiPicker.js b/web/src/components/EmojiPicker.js index 85d05d49..965d8ac8 100644 --- a/web/src/components/EmojiPicker.js +++ b/web/src/components/EmojiPicker.js @@ -1,15 +1,20 @@ import * as React from 'react'; import {useRef, useState} from 'react'; -import Popover from '@mui/material/Popover'; import Typography from '@mui/material/Typography'; import {rawEmojis} from '../app/emojis'; import Box from "@mui/material/Box"; import TextField from "@mui/material/TextField"; -import {InputAdornment} from "@mui/material"; +import {ClickAwayListener, Fade, InputAdornment, styled} from "@mui/material"; import IconButton from "@mui/material/IconButton"; import {Close} from "@mui/icons-material"; +import Popper from "@mui/material/Popper"; +import {splitNoEmpty} from "../app/utils"; + +// Create emoji list by category and create a search base (string with all search words) +// +// This also filters emojis that are not supported by Desktop Chrome. +// This is a hack, but on Ubuntu 18.04, with Chrome 99, only Emoji <= 11 are supported. -// Create emoji list by category; filter emojis that are not supported by Desktop Chrome const emojisByCategory = {}; const isDesktopChrome = /Chrome/.test(navigator.userAgent) && !/Mobile/.test(navigator.userAgent); const maxSupportedVersionForDesktopChrome = 11; @@ -21,7 +26,9 @@ rawEmojis.forEach(emoji => { const unicodeVersion = parseFloat(emoji.unicode_version); const supportedEmoji = unicodeVersion <= maxSupportedVersionForDesktopChrome || !isDesktopChrome; if (supportedEmoji) { - emojisByCategory[emoji.category].push(emoji); + const searchBase = `${emoji.description.toLowerCase()} ${emoji.aliases.join(" ")} ${emoji.tags.join(" ")}`; + const emojiWithSearchBase = { ...emoji, searchBase: searchBase }; + emojisByCategory[emoji.category].push(emojiWithSearchBase); } } catch (e) { // Nothing. Ignore. @@ -32,79 +39,77 @@ const EmojiPicker = (props) => { const open = Boolean(props.anchorEl); const [search, setSearch] = useState(""); const searchRef = useRef(null); + const searchFields = splitNoEmpty(search.toLowerCase(), " "); - /* - FIXME Search is inefficient, somehow make it faster - - useEffect(() => { - const matching = rawEmojis.filter(e => { - const searchLower = search.toLowerCase(); - return e.description.toLowerCase().indexOf(searchLower) !== -1 - || matchInArray(e.aliases, searchLower) - || matchInArray(e.tags, searchLower); - }); - console.log("matching", matching.length); - }, [search]); - */ const handleSearchClear = () => { setSearch(""); searchRef.current?.focus(); }; return ( - <> - - - setSearch(ev.target.value)} - type="text" - variant="standard" - fullWidth - sx={{ marginTop: 0, paddingRight: 2 }} - InputProps={{ - endAdornment: - - - - }} - /> - - {Object.keys(emojisByCategory).map(category => - + {({ TransitionProps }) => ( + + + + setSearch(ev.target.value)} + type="text" + variant="standard" + fullWidth + sx={{ marginTop: 0, marginBottom: "12px", paddingRight: 2 }} + InputProps={{ + endAdornment: + + + + }} /> - )} - - - - + + {Object.keys(emojisByCategory).map(category => + + )} + + + + + )} + ); }; const Category = (props) => { - const showTitle = !props.search; + const showTitle = props.search.length === 0; return ( <> {showTitle && - + {props.title} } @@ -122,39 +127,43 @@ const Category = (props) => { const Emoji = (props) => { const emoji = props.emoji; - const search = props.search; - const matches = search === "" - || emoji.description.toLowerCase().indexOf(search) !== -1 - || matchInArray(emoji.aliases, search) - || matchInArray(emoji.tags, search); - if (!matches) { - return null; - } + const matches = emojiMatches(emoji, props.search); return ( -
{props.emoji.emoji} -
+ ); }; -const matchInArray = (arr, search) => { - if (!arr || !search) { - return false; +const EmojiDiv = styled("div")({ + fontSize: "30px", + width: "30px", + height: "30px", + marginTop: "8px", + marginBottom: "8px", + marginRight: "8px", + lineHeight: "30px", + cursor: "pointer", + opacity: 0.85, + "&:hover": { + opacity: 1 } - return arr.filter(s => s.indexOf(search) !== -1).length > 0; +}); + +const emojiMatches = (emoji, words) => { + if (words.length === 0) { + return true; + } + for (const word of words) { + if (emoji.searchBase.indexOf(word) === -1) { + return false; + } + } + return true; } export default EmojiPicker;