Files
pos-dashboard-v2/src/views/apps/chat/SendMsgForm.tsx
T
2025-08-05 12:35:40 +07:00

242 lines
7.2 KiB
TypeScript

// React Imports
import { useRef, useState, useEffect } from 'react'
import type { FormEvent, KeyboardEvent, RefObject, MouseEvent } from 'react'
// MUI Imports
import TextField from '@mui/material/TextField'
import Button from '@mui/material/Button'
import IconButton from '@mui/material/IconButton'
import Popper from '@mui/material/Popper'
import Fade from '@mui/material/Fade'
import Paper from '@mui/material/Paper'
import ClickAwayListener from '@mui/material/ClickAwayListener'
import Menu from '@mui/material/Menu'
import MenuItem from '@mui/material/MenuItem'
// Third-party Imports
import Picker from '@emoji-mart/react'
import data from '@emoji-mart/data'
// Type Imports
import type { ContactType } from '@/types/apps/chatTypes'
import type { AppDispatch } from '@/redux-store'
// Slice Imports
import { sendMsg } from '@/redux-store/slices/chat'
// Component Imports
import CustomIconButton from '@core/components/mui/IconButton'
type Props = {
dispatch: AppDispatch
activeUser: ContactType
isBelowSmScreen: boolean
messageInputRef: RefObject<HTMLDivElement>
}
// Emoji Picker Component for selecting emojis
const EmojiPicker = ({
onChange,
isBelowSmScreen,
openEmojiPicker,
setOpenEmojiPicker,
anchorRef
}: {
onChange: (value: string) => void
isBelowSmScreen: boolean
openEmojiPicker: boolean
setOpenEmojiPicker: (value: boolean | ((prevVar: boolean) => boolean)) => void
anchorRef: RefObject<HTMLButtonElement>
}) => {
return (
<>
<Popper
open={openEmojiPicker}
transition
disablePortal
placement='top-start'
className='z-[12]'
anchorEl={anchorRef.current}
>
{({ TransitionProps, placement }) => (
<Fade {...TransitionProps} style={{ transformOrigin: placement === 'top-start' ? 'right top' : 'left top' }}>
<Paper>
<ClickAwayListener onClickAway={() => setOpenEmojiPicker(false)}>
<span>
<Picker
emojiSize={18}
theme='light'
data={data}
maxFrequentRows={1}
onEmojiSelect={(emoji: any) => {
onChange(emoji.native)
setOpenEmojiPicker(false)
}}
{...(isBelowSmScreen && { perLine: 8 })}
/>
</span>
</ClickAwayListener>
</Paper>
</Fade>
)}
</Popper>
</>
)
}
const SendMsgForm = ({ dispatch, activeUser, isBelowSmScreen, messageInputRef }: Props) => {
// States
const [msg, setMsg] = useState('')
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
const [openEmojiPicker, setOpenEmojiPicker] = useState(false)
// Refs
const anchorRef = useRef<HTMLButtonElement>(null)
// Vars
const open = Boolean(anchorEl)
const handleToggle = () => {
setOpenEmojiPicker(prevOpen => !prevOpen)
}
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
setAnchorEl(prev => (prev ? null : event.currentTarget))
}
const handleClose = () => {
setAnchorEl(null)
}
const handleSendMsg = (event: FormEvent | KeyboardEvent, msg: string) => {
event.preventDefault()
if (msg.trim() !== '') {
dispatch(sendMsg({ msg }))
setMsg('')
}
}
const handleInputEndAdornment = () => {
return (
<div className='flex items-center gap-1'>
{isBelowSmScreen ? (
<>
<IconButton
id='option-menu'
aria-haspopup='true'
{...(open && { 'aria-expanded': true, 'aria-controls': 'share-menu' })}
onClick={handleClick}
ref={anchorRef}
>
<i className='tabler-dots-vertical text-textPrimary' />
</IconButton>
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
<MenuItem
onClick={() => {
handleToggle()
handleClose()
}}
>
<i className='tabler-mood-smile' />
</MenuItem>
<MenuItem onClick={handleClose}>
<i className='tabler-microphone' />
</MenuItem>
<MenuItem onClick={handleClose} className='p-0'>
<label htmlFor='upload-img' className='plb-2 pli-4'>
<i className='tabler-paperclip' />
<input hidden type='file' id='upload-img' />
</label>
</MenuItem>
</Menu>
<EmojiPicker
anchorRef={anchorRef}
openEmojiPicker={openEmojiPicker}
setOpenEmojiPicker={setOpenEmojiPicker}
isBelowSmScreen={isBelowSmScreen}
onChange={value => {
setMsg(msg + value)
if (messageInputRef.current) {
messageInputRef.current.focus()
}
}}
/>
</>
) : (
<>
<IconButton ref={anchorRef} onClick={handleToggle}>
<i className='tabler-mood-smile cursor-pointer text-textPrimary' />
</IconButton>
<EmojiPicker
anchorRef={anchorRef}
openEmojiPicker={openEmojiPicker}
setOpenEmojiPicker={setOpenEmojiPicker}
isBelowSmScreen={isBelowSmScreen}
onChange={value => {
setMsg(msg + value)
if (messageInputRef.current) {
messageInputRef.current.focus()
}
}}
/>
<IconButton>
<i className='tabler-microphone text-textPrimary' />
</IconButton>
<IconButton component='label' htmlFor='upload-img'>
<i className='tabler-paperclip text-textPrimary' />
<input hidden type='file' id='upload-img' />
</IconButton>
</>
)}
{isBelowSmScreen ? (
<CustomIconButton variant='contained' color='primary' type='submit'>
<i className='tabler-send' />
</CustomIconButton>
) : (
<Button variant='contained' color='primary' type='submit' endIcon={<i className='tabler-send' />}>
Send
</Button>
)}
</div>
)
}
useEffect(() => {
setMsg('')
}, [activeUser.id])
return (
<form autoComplete='off' onSubmit={event => handleSendMsg(event, msg)}>
<TextField
fullWidth
multiline
maxRows={4}
placeholder='Type a message'
value={msg}
className='p-6'
onChange={e => setMsg(e.target.value)}
sx={{
'& fieldset': { border: '0' },
'& .MuiOutlinedInput-root': {
background: 'var(--mui-palette-background-paper)',
boxShadow: 'var(--mui-customShadows-xs) !important'
}
}}
onKeyDown={(e: KeyboardEvent<HTMLDivElement>) => {
if (e.key === 'Enter' && !e.shiftKey) {
handleSendMsg(e, msg)
}
}}
size='small'
inputRef={messageInputRef}
slotProps={{ input: { endAdornment: handleInputEndAdornment() } }}
/>
</form>
)
}
export default SendMsgForm