|
|
@@ -1,8 +1,13 @@
|
|
|
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 IconButton from "@mui/material/IconButton";
|
|
|
+import {Close} from "@mui/icons-material";
|
|
|
|
|
|
const emojisByCategory = {};
|
|
|
rawEmojis.forEach(emoji => {
|
|
|
@@ -14,22 +19,69 @@ rawEmojis.forEach(emoji => {
|
|
|
|
|
|
const EmojiPicker = (props) => {
|
|
|
const open = Boolean(props.anchorEl);
|
|
|
+ const [search, setSearch] = useState("");
|
|
|
+ const searchRef = useRef(null);
|
|
|
+
|
|
|
+ /*
|
|
|
+ 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 (
|
|
|
<>
|
|
|
<Popover
|
|
|
open={open}
|
|
|
- anchorEl={props.anchorEl}
|
|
|
+ elevation={3}
|
|
|
onClose={props.onClose}
|
|
|
+ anchorEl={props.anchorEl}
|
|
|
anchorOrigin={{
|
|
|
vertical: 'bottom',
|
|
|
horizontal: 'left',
|
|
|
}}
|
|
|
>
|
|
|
<Box sx={{ padding: 2, paddingRight: 0, width: "370px", maxHeight: "300px" }}>
|
|
|
- {Object.keys(emojisByCategory).map(category =>
|
|
|
- <Category title={category} emojis={emojisByCategory[category]} onPick={props.onEmojiPick}/>
|
|
|
- )}
|
|
|
+ <TextField
|
|
|
+ inputRef={searchRef}
|
|
|
+ margin="dense"
|
|
|
+ size="small"
|
|
|
+ placeholder="Search emoji"
|
|
|
+ value={search}
|
|
|
+ onChange={ev => setSearch(ev.target.value)}
|
|
|
+ type="text"
|
|
|
+ variant="standard"
|
|
|
+ fullWidth
|
|
|
+ sx={{ marginTop: 0, paddingRight: 2 }}
|
|
|
+ InputProps={{
|
|
|
+ endAdornment:
|
|
|
+ <InputAdornment position="end" sx={{ display: (search) ? '' : 'none' }}>
|
|
|
+ <IconButton size="small" onClick={handleSearchClear} edge="end"><Close/></IconButton>
|
|
|
+ </InputAdornment>
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ <Box sx={{ display: "flex", flexWrap: "wrap", paddingRight: 0, marginTop: 1 }}>
|
|
|
+ {Object.keys(emojisByCategory).map(category =>
|
|
|
+ <Category
|
|
|
+ key={category}
|
|
|
+ title={category}
|
|
|
+ emojis={emojisByCategory[category]}
|
|
|
+ search={search.toLowerCase()}
|
|
|
+ onPick={props.onEmojiPick}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </Box>
|
|
|
</Box>
|
|
|
</Popover>
|
|
|
</>
|
|
|
@@ -37,18 +89,36 @@ const EmojiPicker = (props) => {
|
|
|
};
|
|
|
|
|
|
const Category = (props) => {
|
|
|
+ const showTitle = !props.search;
|
|
|
return (
|
|
|
<>
|
|
|
- <Typography variant="body2">{props.title}</Typography>
|
|
|
- <Box sx={{ display: "flex", flexWrap: "wrap", paddingRight: 0, marginBottom: 1 }}>
|
|
|
- {props.emojis.map(emoji => <Emoji emoji={emoji} onClick={() => props.onPick(emoji.aliases[0])}/>)}
|
|
|
- </Box>
|
|
|
+ {showTitle &&
|
|
|
+ <Typography variant="body1" sx={{ width: "100%", marginTop: 1, marginBottom: 1 }}>
|
|
|
+ {props.title}
|
|
|
+ </Typography>
|
|
|
+ }
|
|
|
+ {props.emojis.map(emoji =>
|
|
|
+ <Emoji
|
|
|
+ key={emoji.aliases[0]}
|
|
|
+ emoji={emoji}
|
|
|
+ search={props.search}
|
|
|
+ onClick={() => props.onPick(emoji.aliases[0])}
|
|
|
+ />
|
|
|
+ )}
|
|
|
</>
|
|
|
);
|
|
|
};
|
|
|
|
|
|
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;
|
|
|
+ }
|
|
|
return (
|
|
|
<div
|
|
|
onClick={props.onClick}
|
|
|
@@ -69,4 +139,11 @@ const Emoji = (props) => {
|
|
|
);
|
|
|
};
|
|
|
|
|
|
+const matchInArray = (arr, search) => {
|
|
|
+ if (!arr || !search) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return arr.filter(s => s.indexOf(search) !== -1).length > 0;
|
|
|
+}
|
|
|
+
|
|
|
export default EmojiPicker;
|