Skip to content

Commit b6fd133

Browse files
committed
Add ability to add emoji's and convert text to emojis in ChatInput
1 parent 97ed92a commit b6fd133

File tree

5 files changed

+178
-3
lines changed

5 files changed

+178
-3
lines changed

streams-react/src/components/ChatContainer/ChatContainer.module.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@
3030
border: 1px solid black;
3131
cursor: pointer;
3232
transform: translate(-100%, -50%);
33+
transition: all 0.1s ease-out;
3334
}
34-
.chat__button:hover {
35+
.chat__button:hover, .chat__button:focus {
3536
color: white;
3637
background-color: rgba(0,0,0,0.4);
3738
}

streams-react/src/components/ChatInput/ChatInput.js

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
1+
import { useEffect, useState, useRef} from "react";
12
import FormInputs from "../FormInputs/FormInputs";
23
import useForm from "../../hooks/useForm";
4+
import "emoji-mart/css/emoji-mart.css";
5+
import {Picker, Emoji, emojiIndex} from "emoji-mart";
36
import styles from "./ChatInput.module.css";
47
import {messageInputs} from "../../data/messageInputs";
8+
import {emojiTrayMap} from "../../data/emojiTrayMap";
59

610
const ChatInput = () => {
7-
const {values, handleChange, clearInput} = useForm({message: ""});
11+
const {values, handleChange, handleAdd, clearInput} = useForm({message: ""});
12+
const [showEmojis, setShowEmojis] = useState(false);
13+
const [trayEmoji, setTrayEmoji] = useState("blush");
14+
const hasConvertedRef = useRef(false);
15+
const nodeRef = useRef(null);
816

917
const formInputs = messageInputs.map(input => {
1018
return {
@@ -13,12 +21,118 @@ const ChatInput = () => {
1321
}
1422
});
1523

24+
useEffect(() => {
25+
if(!hasConvertedRef.current) {
26+
convertInputToEmoji(values.message);
27+
}
28+
else {
29+
hasConvertedRef.current = false;
30+
}
31+
}, [values]);
32+
33+
useEffect(() => {
34+
if(showEmojis) {
35+
window.addEventListener("mousedown", handleClickOutside);
36+
}
37+
else {
38+
window.removeEventListener("mousedown", handleClickOutside);
39+
}
40+
41+
return () => {
42+
window.removeEventListener("mousedown", handleClickOutside);
43+
}
44+
}, [showEmojis]);
45+
46+
const handleClickOutside = (e) => {
47+
if(nodeRef.current.contains(e.target)) return;
48+
49+
setShowEmojis(false);
50+
};
51+
52+
const handleClick = () => {
53+
setShowEmojis(!showEmojis);
54+
};
55+
56+
const handleEmojiHover = () => {
57+
let newEmoji = emojiTrayMap[Math.floor(Math.random() * emojiTrayMap.length)];
58+
while(newEmoji === trayEmoji) {
59+
newEmoji = emojiTrayMap[Math.floor(Math.random() * emojiTrayMap.length)];
60+
}
61+
setTrayEmoji(newEmoji);
62+
};
63+
64+
const handleEmoji = (e) => {
65+
const pseudoTarget = {target : {value: e.native, name: "message"}};
66+
handleAdd(pseudoTarget);
67+
};
68+
69+
const convertInputToEmoji = (userText) => {
70+
if(!userText.length) return;
71+
const textArray = userText.split(" ");
72+
let newValues = [];
73+
74+
for(let value of textArray) {
75+
if(value[0] !== ":") {
76+
newValues.push(value);
77+
continue;
78+
}
79+
80+
let emojiArray = [];
81+
let isEmojiCode = value[value.length - 1] === ":" && value.length > 1;
82+
83+
if(isEmojiCode) value = value.replaceAll(":", "");
84+
85+
emojiArray = emojiIndex.search(value).filter(emoji => {
86+
if(!isEmojiCode && emoji.emoticons.length) {
87+
for(let emoticon of emoji.emoticons) {
88+
if(emoticon === value) return emoji.native;
89+
}
90+
}
91+
if(value === emoji.id) return emoji.native;
92+
return false
93+
});
94+
95+
if(emojiArray.length) newValues.push(emojiArray[0].native);
96+
else {
97+
newValues.push(value);
98+
}
99+
}
100+
101+
let newText = newValues.join(" ");
102+
if(newText !== userText) {
103+
const pseudoTarget = {target : {value: newText, name: "message"}};
104+
hasConvertedRef.current = true;
105+
handleChange(pseudoTarget);
106+
}
107+
};
108+
16109
return(
17110
<div className={styles.container}>
18111
<div className={styles.input__container}>
19112
<FormInputs inputs={formInputs} handleChange={handleChange} clearInput={clearInput} />
20113
</div>
21114
<div className={styles.controls__container}>
115+
<div ref={nodeRef} className={styles.emoji__container}>
116+
<button
117+
type="button"
118+
title={"Pick your emoji..."}
119+
className={styles.emoji__button}
120+
onClick={handleClick}
121+
onMouseOver={handleEmojiHover}
122+
>
123+
<Emoji emoji={trayEmoji} size={18} native={true} />
124+
</button>
125+
{showEmojis &&
126+
<div className={styles.emoji__mart}>
127+
<Picker
128+
title={"Pick your emoji..."}
129+
onSelect={handleEmoji}
130+
emoji={"wave"}
131+
native={true}
132+
/>
133+
</div>
134+
}
135+
</div>
22136
<button type="button" className={styles.send__button}><i className="fas fa-play"></i></button>
23137
</div>
24138
</div>

streams-react/src/components/ChatInput/ChatInput.module.css

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
.input__container {
99
flex: 0.75;
1010
margin-left: 15px;
11-
margin-right: 15px;
11+
margin-right: 5px;
1212
}
1313
.input__container label {
1414
position: absolute;
@@ -22,8 +22,45 @@
2222
border: 0;
2323
}
2424
.controls__container {
25+
display: flex;
26+
justify-content: space-around;
2527
flex: 0.25;
2628
}
29+
.emoji__container {
30+
position: relative;
31+
margin-right: 5px;
32+
}
33+
.emoji__button {
34+
cursor: pointer;
35+
transition: all 0.15s ease;
36+
}
37+
.emoji__button:hover, .emoji__button:focus {
38+
transform: scale(1.1);
39+
}
40+
.emoji__mart {
41+
position: absolute;
42+
top: 0;
43+
left: 0;
44+
transform: translate(-95%, -105%);
45+
}
2746
.send__button {
2847
cursor: pointer;
48+
font-size: 18px;
49+
}
50+
51+
/***
52+
EMOJI MART CUSTOM CSS
53+
***/
54+
55+
:global(.emoji-mart) {
56+
width: 300px !important;
57+
}
58+
:global(.emoji-mart-title-label) {
59+
font-size: 16px !important;
60+
}
61+
:global(.emoji-mart-emoji) {
62+
cursor: pointer !important;
63+
}
64+
:global(.emoji-mart-emoji) > span {
65+
cursor: pointer !important;
2966
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const emojiTrayMap = [
2+
"blush",
3+
"smile",
4+
"joy",
5+
"rolling_on_the_floor_laughing",
6+
"heart_eyes",
7+
"sweat_smile",
8+
"kissing_heart",
9+
"smiling_face_with_3_hearts",
10+
"smiley",
11+
"relaxed",
12+
"hugging_face"
13+
];
14+
15+
export {emojiTrayMap};

streams-react/src/hooks/useForm.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ export default function useForm(init = {}) {
1515
[name]: value
1616
});
1717
};
18+
const handleAdd = e => {
19+
let {value, name} = e.target;
20+
setValues({
21+
...values,
22+
[name]: values[name] + value
23+
});
24+
};
1825
const resetForm = () => {
1926
setValues(init);
2027
};
@@ -31,6 +38,7 @@ export default function useForm(init = {}) {
3138
values,
3239
setValues,
3340
handleChange,
41+
handleAdd,
3442
resetForm,
3543
clearForm,
3644
clearInput

0 commit comments

Comments
 (0)