feat: add emoji picker to chat room message input
- Integrated emoji-picker-element library for emoji selection - Added emoji toggle button in message input container - Implemented emoji picker show/hide functionality - Created CSS styles for emoji picker positioning - Added event handling for emoji selection and outside click - Updated package.json to include emoji-picker-element dependency - Modified Prisma schema to use TEXT type for message content - Updated Prisma migration scripts to use dotenv for environment configuration
This commit is contained in:
		
							parent
							
								
									1e3b188d90
								
							
						
					
					
						commit
						3d41e2cc42
					
				
					 7 changed files with 111 additions and 11 deletions
				
			
		|  | @ -449,3 +449,32 @@ button:disabled { | |||
|     height: 50vh; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /* Emoji Picker Styles */ | ||||
| .message-input-container { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   flex: 1; | ||||
| } | ||||
| 
 | ||||
| .emoji-toggle-button { | ||||
|   background: none; | ||||
|   border: none; | ||||
|   font-size: 1.5rem; | ||||
|   cursor: pointer; | ||||
|   margin-left: 0.5rem; | ||||
|   padding: 0.25rem; | ||||
|   line-height: 1; | ||||
| } | ||||
| 
 | ||||
| .emoji-picker-container { | ||||
|   position: absolute; | ||||
|   bottom: 100%; | ||||
|   right: 0; | ||||
|   z-index: 10; | ||||
|   margin-bottom: 0.5rem; | ||||
| } | ||||
| 
 | ||||
| .message-form { | ||||
|   position: relative; | ||||
| } | ||||
|  |  | |||
|  | @ -1,7 +1,10 @@ | |||
| import { createSignal, createEffect, For, Show } from 'solid-js'; | ||||
| import { createSignal, createEffect, For, Show, onMount } from 'solid-js'; | ||||
| import { gql } from '@urql/core'; | ||||
| import { createQuery, createMutation, createSubscription } from '@urql/solid'; | ||||
| import { Message } from '../types'; | ||||
| import 'emoji-picker-element'; | ||||
| import { EmojiClickEvent } from 'emoji-picker-element/shared'; | ||||
| import { Picker } from 'emoji-picker-element'; | ||||
| 
 | ||||
| const ROOM_QUERY = gql` | ||||
|   query GetRoom($id: ID!) { | ||||
|  | @ -80,7 +83,13 @@ export function ChatRoom(props: ChatRoomProps) { | |||
|   const [messages, setMessages] = createSignal<Message[]>([]); | ||||
|   const [confirmLeave, setConfirmLeave] = createSignal(false); | ||||
|   const [leaveError, setLeaveError] = createSignal<string | null>(null); | ||||
|   const [showEmojiPicker, setShowEmojiPicker] = createSignal(false); | ||||
|   const [emojiPickerRef, setPickerRef] = createSignal<Picker | undefined>( | ||||
|     undefined | ||||
|   ); | ||||
|   let messagesContainer: HTMLDivElement | undefined; | ||||
|   let messageInput: HTMLInputElement | undefined; | ||||
|   let emojiPickerContainer: HTMLDivElement | undefined; | ||||
| 
 | ||||
|   const scrollToBottom = () => { | ||||
|     if (messagesContainer) { | ||||
|  | @ -198,6 +207,41 @@ export function ChatRoom(props: ChatRoomProps) { | |||
|     return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); | ||||
|   }; | ||||
| 
 | ||||
|   const toggleEmojiPicker = () => { | ||||
|     setShowEmojiPicker(!showEmojiPicker()); | ||||
|   }; | ||||
| 
 | ||||
|   createEffect(() => { | ||||
|     const handleEmojiSelect = (event: EmojiClickEvent) => { | ||||
|       setMessage((prev) => prev + event.detail.unicode); | ||||
|       messageInput?.focus(); | ||||
|     }; | ||||
|     const ref = emojiPickerRef(); | ||||
|     if (ref) { | ||||
|       ref.addEventListener('emoji-click', handleEmojiSelect); | ||||
|       return () => { | ||||
|         ref.removeEventListener('emoji-click', handleEmojiSelect); | ||||
|       }; | ||||
|     } | ||||
|   }); | ||||
| 
 | ||||
|   // Close emoji picker when clicking outside
 | ||||
|   onMount(() => { | ||||
|     const handleClickOutside = (event: MouseEvent) => { | ||||
|       if ( | ||||
|         emojiPickerContainer && | ||||
|         !emojiPickerContainer.contains(event.target as Node) | ||||
|       ) { | ||||
|         setShowEmojiPicker(false); | ||||
|       } | ||||
|     }; | ||||
| 
 | ||||
|     document.addEventListener('mousedown', handleClickOutside); | ||||
|     return () => { | ||||
|       document.removeEventListener('mousedown', handleClickOutside); | ||||
|     }; | ||||
|   }); | ||||
| 
 | ||||
|   return ( | ||||
|     <div class='chat-room'> | ||||
|       <Show when={roomQuery.data?.room} fallback={<div>Loading room...</div>}> | ||||
|  | @ -246,13 +290,30 @@ export function ChatRoom(props: ChatRoomProps) { | |||
|       </div> | ||||
| 
 | ||||
|       <form class='message-form' onSubmit={handleSendMessage}> | ||||
|         <input | ||||
|           type='text' | ||||
|           value={message()} | ||||
|           onInput={(e) => setMessage(e.currentTarget.value)} | ||||
|           placeholder='Type a message...' | ||||
|         /> | ||||
|         <div class='message-input-container'> | ||||
|           <input | ||||
|             ref={messageInput} | ||||
|             type='text' | ||||
|             value={message()} | ||||
|             onInput={(e) => setMessage(e.currentTarget.value)} | ||||
|             placeholder='Type a message...' | ||||
|           /> | ||||
|           <button | ||||
|             type='button' | ||||
|             class='emoji-toggle-button' | ||||
|             onClick={toggleEmojiPicker} | ||||
|           > | ||||
|             😊 | ||||
|           </button> | ||||
|         </div> | ||||
|         <button type='submit'>Send</button> | ||||
| 
 | ||||
|         {showEmojiPicker() && ( | ||||
|           <div ref={emojiPickerContainer} class='emoji-picker-container'> | ||||
|             {/* @ts-ignore */} | ||||
|             <emoji-picker ref={(el: Picker) => setPickerRef(el)}></emoji-picker> | ||||
|           </div> | ||||
|         )} | ||||
|       </form> | ||||
|     </div> | ||||
|   ); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue