This document outlines the steps to create EmojiReaction
component styled withTailwind CSS and using some npm dependency libraries.
Node.js
and npm
installed on your machine.Tailwind CSS
installed in your project.CVA(class-variance-authority)
is a utility for managing CSS class names based on various conditions.clsx
is a tiny utility for constructing className strings conditionally.1// tailwind.config.js
2module.exports = {
3 ...
4 theme: {
5 ...
6 extend: {
7 ...
8 keyframes: {
9 "fly-emoji": {
10 "0%": {
11 transform: "translateY(0) scale(1)",
12 opacity: "0.7",
13 },
14 "100%": {
15 transform: "translateY(-150px) scale(2)",
16 opacity: "0",
17 },
18 },
19 },
20 animation: {
21 "fly-emoji": "fly-emoji 1s forwards",
22 },
23 },
24 },
25 plugins: [require("tailwindcss-animate")]
26};
27
1// emoji-reaction.component.tsx
2import clsx from "clsx";
3import { useEffect, useRef, useState } from "react";
4
5interface CurrentEmoji {
6 emoji: string;
7 id: number;
8}
9
10interface EmojiReactionProps {
11 emojis: string[];
12 disabled?: boolean;
13 className?: string;
14}
15
16const EmojiReaction: React.FC<EmojiReactionProps> = ({
17 emojis,
18 disabled,
19 className,
20}) => {
21 const [currentEmoji, setCurrentEmoji] = useState<CurrentEmoji | null>(null);
22
23 const clearEmojiTimeout = useRef<ReturnType<typeof setTimeout>>(null);
24
25 useEffect(() => {
26 const timeout = clearEmojiTimeout.current;
27
28 return () => {
29 if (timeout) clearTimeout(timeout);
30 };
31 }, []);
32
33 const handleEmojiClick = (emoji: string) => {
34 if (clearEmojiTimeout.current) clearTimeout(clearEmojiTimeout.current);
35
36 setCurrentEmoji({ emoji, id: Date.now() });
37
38 clearEmojiTimeout.current = setTimeout(() => {
39 setCurrentEmoji(null);
40 }, 3000);
41 };
42
43 return (
44 <div
45 className={clsx(
46 "mx-auto rounded-full border border-gray-300 bg-white dark:border-gray-700 dark:bg-gray-500",
47 className,
48 )}
49 >
50 <div className="grid items-center justify-start">
51 <div className="p-2">
52 <div className="grid grid-flow-col items-center justify-start">
53 {emojis.map((emoji) => (
54 <div key={emoji} className="relative w-fit">
55 <button
56 className={clsx(
57 "transition-bg-color duration-600 relative inline-flex items-center justify-center rounded-full bg-transparent p-1 align-middle text-2xl leading-6 ease-in-out active:duration-0",
58 {
59 "hover:bg-gray-200 active:bg-gray-400": !disabled,
60 },
61 )}
62 disabled={disabled}
63 onClick={() => handleEmojiClick(emoji)}
64 >
65 {emoji}
66 {currentEmoji && currentEmoji.emoji === emoji && (
67 <span
68 key={currentEmoji.id}
69 className="animate-fly-emoji duration-3000 absolute -top-10 left-0 right-0 mx-auto"
70 >
71 {currentEmoji.emoji}
72 </span>
73 )}
74 </button>
75 </div>
76 ))}
77 </div>
78 </div>
79 </div>
80 </div>
81 );
82};
83
84export default EmojiReaction;
85