264 lines
8.6 KiB
TypeScript
264 lines
8.6 KiB
TypeScript
// React Imports
|
|
import { useState } from 'react'
|
|
|
|
// MUI Imports
|
|
import Drawer from '@mui/material/Drawer'
|
|
import Button from '@mui/material/Button'
|
|
import Typography from '@mui/material/Typography'
|
|
import IconButton from '@mui/material/IconButton'
|
|
import InputBase from '@mui/material/InputBase'
|
|
|
|
// Third-party Imports
|
|
import classnames from 'classnames'
|
|
import { useEditor, EditorContent } from '@tiptap/react'
|
|
import { StarterKit } from '@tiptap/starter-kit'
|
|
import { Underline } from '@tiptap/extension-underline'
|
|
import { Placeholder } from '@tiptap/extension-placeholder'
|
|
import { TextAlign } from '@tiptap/extension-text-align'
|
|
import type { Editor } from '@tiptap/core'
|
|
|
|
// Component Imports
|
|
import CustomIconButton from '@core/components/mui/IconButton'
|
|
|
|
// Hook Imports
|
|
import { useSettings } from '@core/hooks/useSettings'
|
|
|
|
// Style Imports
|
|
import '@/libs/styles/tiptapEditor.css'
|
|
|
|
type Props = {
|
|
openCompose: boolean
|
|
setOpenCompose: (value: boolean) => void
|
|
isBelowSmScreen: boolean
|
|
isBelowMdScreen: boolean
|
|
}
|
|
|
|
const EditorToolbar = ({ editor }: { editor: Editor | null }) => {
|
|
if (!editor) {
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<div className='flex flex-wrap gap-x-3 gap-y-1 plb-2 pli-4 border-bs'>
|
|
<CustomIconButton
|
|
{...(editor.isActive('bold') && { color: 'primary' })}
|
|
variant='tonal'
|
|
size='small'
|
|
onClick={() => editor.chain().focus().toggleBold().run()}
|
|
>
|
|
<i className={classnames('tabler-bold', { 'text-textSecondary': !editor.isActive('bold') })} />
|
|
</CustomIconButton>
|
|
<CustomIconButton
|
|
{...(editor.isActive('underline') && { color: 'primary' })}
|
|
variant='tonal'
|
|
size='small'
|
|
onClick={() => editor.chain().focus().toggleUnderline().run()}
|
|
>
|
|
<i className={classnames('tabler-underline', { 'text-textSecondary': !editor.isActive('underline') })} />
|
|
</CustomIconButton>
|
|
<CustomIconButton
|
|
{...(editor.isActive('italic') && { color: 'primary' })}
|
|
variant='tonal'
|
|
size='small'
|
|
onClick={() => editor.chain().focus().toggleItalic().run()}
|
|
>
|
|
<i className={classnames('tabler-italic', { 'text-textSecondary': !editor.isActive('italic') })} />
|
|
</CustomIconButton>
|
|
<CustomIconButton
|
|
{...(editor.isActive('strike') && { color: 'primary' })}
|
|
variant='tonal'
|
|
size='small'
|
|
onClick={() => editor.chain().focus().toggleStrike().run()}
|
|
>
|
|
<i className={classnames('tabler-strikethrough', { 'text-textSecondary': !editor.isActive('strike') })} />
|
|
</CustomIconButton>
|
|
<CustomIconButton
|
|
{...(editor.isActive({ textAlign: 'left' }) && { color: 'primary' })}
|
|
variant='tonal'
|
|
size='small'
|
|
onClick={() => editor.chain().focus().setTextAlign('left').run()}
|
|
>
|
|
<i
|
|
className={classnames('tabler-align-left', { 'text-textSecondary': !editor.isActive({ textAlign: 'left' }) })}
|
|
/>
|
|
</CustomIconButton>
|
|
<CustomIconButton
|
|
{...(editor.isActive({ textAlign: 'center' }) && { color: 'primary' })}
|
|
variant='tonal'
|
|
size='small'
|
|
onClick={() => editor.chain().focus().setTextAlign('center').run()}
|
|
>
|
|
<i
|
|
className={classnames('tabler-align-center', {
|
|
'text-textSecondary': !editor.isActive({ textAlign: 'center' })
|
|
})}
|
|
/>
|
|
</CustomIconButton>
|
|
<CustomIconButton
|
|
{...(editor.isActive({ textAlign: 'right' }) && { color: 'primary' })}
|
|
variant='tonal'
|
|
size='small'
|
|
onClick={() => editor.chain().focus().setTextAlign('right').run()}
|
|
>
|
|
<i
|
|
className={classnames('tabler-align-right', {
|
|
'text-textSecondary': !editor.isActive({ textAlign: 'right' })
|
|
})}
|
|
/>
|
|
</CustomIconButton>
|
|
<CustomIconButton
|
|
{...(editor.isActive({ textAlign: 'justify' }) && { color: 'primary' })}
|
|
variant='tonal'
|
|
size='small'
|
|
onClick={() => editor.chain().focus().setTextAlign('justify').run()}
|
|
>
|
|
<i
|
|
className={classnames('tabler-align-justified', {
|
|
'text-textSecondary': !editor.isActive({ textAlign: 'justify' })
|
|
})}
|
|
/>
|
|
</CustomIconButton>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const ComposeMail = (props: Props) => {
|
|
// Props
|
|
const { openCompose, setOpenCompose, isBelowSmScreen, isBelowMdScreen } = props
|
|
|
|
// States
|
|
const [visibility, setVisibility] = useState({ cc: false, bcc: false })
|
|
|
|
// Hooks
|
|
const { settings } = useSettings()
|
|
|
|
const toggleVisibility = (value: 'cc' | 'bcc') => {
|
|
setVisibility(prev => ({ ...prev, [value]: !prev[value] }))
|
|
}
|
|
|
|
const editor = useEditor({
|
|
extensions: [
|
|
StarterKit,
|
|
Placeholder.configure({
|
|
placeholder: 'Message'
|
|
}),
|
|
TextAlign.configure({
|
|
types: ['heading', 'paragraph']
|
|
}),
|
|
Underline
|
|
],
|
|
immediatelyRender: false
|
|
})
|
|
|
|
return (
|
|
<Drawer
|
|
anchor='bottom'
|
|
variant='persistent'
|
|
hideBackdrop
|
|
open={openCompose}
|
|
onClose={() => setOpenCompose(false)}
|
|
PaperProps={{
|
|
sx: {
|
|
width: isBelowMdScreen ? 'calc(100% - 2 * 1.5rem)' : '100%',
|
|
maxWidth: 600,
|
|
position: 'absolute',
|
|
height: 'auto',
|
|
insetInlineStart: 'auto',
|
|
insetInlineEnd: '1.5rem',
|
|
insetBlockEnd: '1.5rem',
|
|
borderRadius: 'var(--mui-shape-borderRadius)',
|
|
borderTop: 0,
|
|
boxShadow: settings.skin === 'bordered' ? 'none' : 'var(--mui-customShadows-xl)',
|
|
border: settings.skin === 'bordered' ? '1px solid var(--mui-palette-divider)' : undefined,
|
|
zIndex: 12
|
|
}
|
|
}}
|
|
>
|
|
<div className='flex items-center justify-between plb-3.5 pli-6 bg-actionHover'>
|
|
<Typography variant='h5' color='text.secondary'>
|
|
Compose Mail
|
|
</Typography>
|
|
<div className='flex gap-2'>
|
|
<IconButton size='small' onClick={() => setOpenCompose(false)}>
|
|
<i className='tabler-minus text-textSecondary' />
|
|
</IconButton>
|
|
<IconButton size='small' onClick={() => setOpenCompose(false)}>
|
|
<i className='tabler-x text-textSecondary' />
|
|
</IconButton>
|
|
</div>
|
|
</div>
|
|
<div className='flex items-center gap-2 pli-6 plb-1'>
|
|
<Typography className='font-medium' color='text.disabled'>
|
|
To:
|
|
</Typography>
|
|
<InputBase fullWidth />
|
|
<div className='text-textSecondary'>
|
|
<span className='cursor-pointer' onClick={() => toggleVisibility('cc')}>
|
|
Cc
|
|
</span>
|
|
<span className='mli-1'>|</span>
|
|
<span className='cursor-pointer' onClick={() => toggleVisibility('bcc')}>
|
|
Bcc
|
|
</span>
|
|
</div>
|
|
</div>
|
|
{visibility.cc && (
|
|
<InputBase
|
|
className='plb-1 pli-6 border-bs'
|
|
startAdornment={
|
|
<Typography className='font-medium mie-2' color='text.disabled'>
|
|
Cc:
|
|
</Typography>
|
|
}
|
|
/>
|
|
)}
|
|
{visibility.bcc && (
|
|
<InputBase
|
|
className='plb-1 pli-6 border-bs'
|
|
startAdornment={
|
|
<Typography className='font-medium mie-2' color='text.disabled'>
|
|
Bcc:
|
|
</Typography>
|
|
}
|
|
/>
|
|
)}
|
|
<InputBase
|
|
className='plb-1 pli-6 border-bs'
|
|
startAdornment={
|
|
<Typography className='font-medium mie-2' color='text.disabled'>
|
|
Subject:
|
|
</Typography>
|
|
}
|
|
/>
|
|
<EditorToolbar editor={editor} />
|
|
<EditorContent editor={editor} className='bs-[105px] overflow-y-auto flex border-bs' />
|
|
<div className='plb-4 pli-5 flex justify-between items-center gap-4'>
|
|
<div className='flex items-center gap-4 max-sm:gap-3'>
|
|
{isBelowSmScreen ? (
|
|
<CustomIconButton color='primary' variant='contained'>
|
|
<i className='tabler-send' />
|
|
</CustomIconButton>
|
|
) : (
|
|
<Button variant='contained' endIcon={<i className='tabler-send' />} onClick={() => setOpenCompose(false)}>
|
|
Send
|
|
</Button>
|
|
)}
|
|
<IconButton size='small'>
|
|
<i className='tabler-paperclip text-textSecondary' />
|
|
</IconButton>
|
|
</div>
|
|
<div className='flex gap-2'>
|
|
<IconButton size='small'>
|
|
<i className='tabler-dots-vertical text-textSecondary' />
|
|
</IconButton>
|
|
<IconButton size='small' onClick={() => setOpenCompose(false)}>
|
|
<i className='tabler-trash text-textSecondary' />
|
|
</IconButton>
|
|
</div>
|
|
</div>
|
|
</Drawer>
|
|
)
|
|
}
|
|
|
|
export default ComposeMail
|