diff --git a/extension/package.json b/extension/package.json index 0cfc940..e344ead 100644 --- a/extension/package.json +++ b/extension/package.json @@ -10,6 +10,8 @@ "preview": "vite preview" }, "dependencies": { + "@emoji-mart/data": "^1.1.2", + "@emoji-mart/react": "^1.1.1", "@headlessui/react": "1.7.13", "@tanstack/react-query": "^4.20.4", "react": "^18.2.0", diff --git a/extension/src/components/Room.tsx b/extension/src/components/Room.tsx index 66e860f..9be672a 100644 --- a/extension/src/components/Room.tsx +++ b/extension/src/components/Room.tsx @@ -12,6 +12,7 @@ import { SERVER_URL } from "../config"; import { useIsMutating } from "@tanstack/react-query"; import PlayersButton from "./buttons/PlayersButton"; import Timer from "./Timer"; +import EmojiPicker from "./buttons/EmojiPickerButton"; interface RoomMessagesLocalStorage { roomId: string; @@ -55,6 +56,8 @@ export default function Room({ let [hasClickedCopyIcon, setHasClickedCopyIcon] = useState(false); let socketRef = useRef(null); let previousSubmissionUrl = useRef(null); + const [showEmojiPicker, setShowEmojiPicker] = useState(false); + const pickerWrapperRef = useRef(null); function handleSubmitMessage(event: React.SyntheticEvent) { event.preventDefault(); @@ -92,6 +95,23 @@ export default function Room({ }, 2000); } + function onEmojiSelect(emoji: Record) { + const input = inputRef.current; + + if (!input) return; + + const startIndex = input.selectionStart || input.value.length; + const endIndex = input.selectionEnd || input.value.length; + + const newValue = input.value.slice(0, startIndex) + + emoji.native + + input.value.slice(endIndex); + + input.value = newValue; + input.focus(); + input.setSelectionRange(startIndex + emoji.native.length, startIndex + emoji.native.length); + }; + useEffect(() => { let socket: Socket = io(SERVER_URL, { transports: ["websocket", "polling"], @@ -245,6 +265,25 @@ export default function Room({ autoScrollToLatestMessage(); }, [messages]); + useEffect(() => { + const handleClickOutsideEvent = (event: Record) => { + if ( + pickerWrapperRef.current && + !pickerWrapperRef.current.contains(event.target) && + showEmojiPicker + ) { + setShowEmojiPicker(false); + } + }; + + document.addEventListener('click', handleClickOutsideEvent); + + return () => { + document.removeEventListener('click', handleClickOutsideEvent); + }; + + }, [showEmojiPicker]); + return (
@@ -317,7 +356,7 @@ export default function Room({ type="text" name="chatbox" id="chatbox" - className="w-full bg-lc-fg-light outline-none dark:bg-lc-fg" + className="w-full bg-lc-fg-light outline-none dark:bg-lc-fg" placeholder="Type a message..." spellCheck="false" autoComplete="off" @@ -325,6 +364,9 @@ export default function Room({ autoCorrect="off" /> +
+ +
void; + onSelectEmoji: (e: Record) => any; +} + +function EmojiPicker({ showEmojiPicker, setShowEmojiPicker, onSelectEmoji }: IEmojiPickerParams) { + const buttonRef = useRef(null); + + function handleEmojiSelect(emoji: Record) { + setShowEmojiPicker(false); + onSelectEmoji(emoji); + }; + + function handleToggleEmojiPicker() { + setShowEmojiPicker(!showEmojiPicker); + } + + const calculatePickerPosition = () => { + if (!buttonRef.current) { + return + } + + const buttonRect = buttonRef.current.getBoundingClientRect(); + const bottom = buttonRect.height * 2; + const right = buttonRect.width; + return { bottom, right }; + }; + + return ( + <> +
+ +
+ +
+ {showEmojiPicker && + + } +
+ + ); +}; + +export default EmojiPicker; \ No newline at end of file diff --git a/extension/src/icons/EmojiIcon.tsx b/extension/src/icons/EmojiIcon.tsx new file mode 100644 index 0000000..98f22b8 --- /dev/null +++ b/extension/src/icons/EmojiIcon.tsx @@ -0,0 +1,15 @@ +export default function SettingsIcon() { + return ( + + + + ); +} diff --git a/yarn.lock b/yarn.lock index 81dd192..c3b72b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -379,6 +379,16 @@ resolved "https://registry.yarnpkg.com/@emmetio/scanner/-/scanner-1.0.4.tgz#e9cdc67194fd91f8b7eb141014be4f2d086c15f1" integrity sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA== +"@emoji-mart/data@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@emoji-mart/data/-/data-1.1.2.tgz#777c976f8f143df47cbb23a7077c9ca9fe5fc513" + integrity sha512-1HP8BxD2azjqWJvxIaWAMyTySeZY0Osr83ukYjltPVkNXeJvTz7yDrPLBtnrD5uqJ3tg4CcLuuBW09wahqL/fg== + +"@emoji-mart/react@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@emoji-mart/react/-/react-1.1.1.tgz#ddad52f93a25baf31c5383c3e7e4c6e05554312a" + integrity sha512-NMlFNeWgv1//uPsvLxvGQoIerPuVdXwK/EUek8OOkJ6wVOWPUizRBJU0hDqWZCOROVpfBgCemaC3m6jDOXi03g== + "@esbuild/android-arm@0.15.18": version "0.15.18" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.18.tgz#266d40b8fdcf87962df8af05b76219bc786b4f80"