initial commit
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
// 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
|
||||
Reference in New Issue
Block a user