add components for modal

This commit is contained in:
hamid zarghami
2026-03-14 16:15:09 +03:30
parent 60eadcc4af
commit f4f617f8d2
5 changed files with 225 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
import { type FC, Fragment, type ReactNode, useEffect } from 'react'
import { createPortal } from 'react-dom'
import HeaderModal from './HeaderModal'
interface Props {
open: boolean,
close: () => void,
children: ReactNode,
isHeader?: boolean,
title_header?: string,
width?: number
}
const DefaulModal: FC<Props> = (props: Props) => {
useEffect(() => {
if (props.open) {
document.body.style.overflow = 'hidden'
} else {
document.body.style.overflow = 'auto'
}
}, [props.open])
const modalContent = props.open ? (
<Fragment>
<div
onClick={props.close}
className='fixed size-full top-0 bottom-0 right-0 left-0 bg-black/40 inset-0 z-9998'
/>
<div
style={{ maxWidth: props.width }}
className='xl:justify-center xl:items-center items-end flex overflow-x-hidden overflow-y-auto fixed inset-0 z-9999 h-auto top-0 bottom-0 m-auto outline-none focus:outline-none xl:max-w-xl mx-auto pointer-events-none'
>
<div
className='relative xl:h-full h-[80%] bottom-0 left-0 flex xl:items-center sm:h-auto w-full xl:my-6 xl:p-2 pointer-events-auto'
onClick={(e) => e.stopPropagation()}
>
<div className='border-0 h-auto p-5 lg:min-w-full overflow-y-auto rounded-3xl rounded-b-none xl:rounded-b-3xl relative flex flex-col w-full bg-white outline-none focus:outline-none shadow-2xl'>
{
props.isHeader && props.title_header &&
<div className='pb-3 border-b border-border'>
<div className='h-[5px] w-[200px] mx-auto bg-[#D1D3D7] rounded-full mb-4 xl:hidden'></div>
<HeaderModal close={props.close} label={props.title_header} />
</div>
}
{props.children}
</div>
</div>
</div>
</Fragment>
) : null
return createPortal(modalContent, document.body)
}
export default DefaulModal
+25
View File
@@ -0,0 +1,25 @@
import { CloseCircle } from 'iconsax-react'
import { type FC } from 'react'
type Props = {
label: string,
close: () => void,
}
const HeaderModal: FC<Props> = (props: Props) => {
return (
<div className='flex justify-between items-center'>
<div className='text-sm'>{props.label}</div>
<div className='size-7 rounded-full bg-white bg-opacity-35 flex justify-center items-center'>
<CloseCircle
size={16}
color='black'
onClick={props.close}
/>
</div>
</div>
)
}
export default HeaderModal
+69
View File
@@ -0,0 +1,69 @@
import { type FC, useState } from 'react'
import DefaulModal from './DefaulModal'
import { useTranslation } from 'react-i18next'
import Button from './Button'
import Textarea from './Textarea'
type Props = {
isloading?: boolean,
close: () => void,
isOpen: boolean,
onConfrim: (text?: string) => void,
label?: string,
isHasDescription?: boolean
}
const ModalConfrim: FC<Props> = (props: Props) => {
const { t } = useTranslation('global')
const [description, setDescription] = useState<string>('')
return (
<DefaulModal
open={props.isOpen}
close={props.close}
title_header={t('confrim.subject')}
isHeader
>
<div className='mt-6'>
<div className='text-sm text-center'>
{
props.label ?
props.label
:
t('confrim.content')
}
{
props.isHasDescription &&
<div className='mt-4'>
<Textarea
placeholder={t('description')}
onChange={(e) => setDescription(e.target.value)}
value={description}
className='bg-transparent border border-gray-500 rounded-xl w-[300px] p-2'
/>
</div>
}
</div>
<div className='flex gap-4 justify-center mt-10'>
<Button
onClick={() => props.onConfrim(props.isHasDescription ? description : undefined)}
disabled={props.isloading}
>
بله
</Button>
<Button
className='bg-transparent text-black border border-primary'
onClick={props.close}
>
لغو
</Button>
</div>
</div>
</DefaulModal>
)
}
export default ModalConfrim
+39
View File
@@ -0,0 +1,39 @@
import { type FC, type InputHTMLAttributes } from 'react'
import Error from './Error'
import { clx } from '../helpers/utils'
type Props = {
label?: string,
error_text?: string
} & InputHTMLAttributes<HTMLTextAreaElement>
const Textarea: FC<Props> = (props: Props) => {
return (
<div className='w-full relative'>
{
props.label &&
<div className='text-sm' >
{props.label}
</div>
}
<textarea
{...props}
className={clx(
'border border-border leading-7 w-full rounded-xl px-4 py-3 min-h-[100px] mt-1 text-xs',
props.className
)}
>
</textarea>
{
props.error_text &&
<Error errorText={props.error_text} />
}
</div>
)
}
export default Textarea
+30
View File
@@ -0,0 +1,30 @@
import { Trash } from 'iconsax-react'
import { type FC, useState } from 'react'
import ModalConfrim from './ModalConfrim'
interface Props {
onDelete: () => void,
isloading?: boolean
}
const TrashWithConfrim: FC<Props> = ({ onDelete, isloading = false }) => {
const [isConfirm, setIsConfirm] = useState(false)
return (
<div>
<Trash
onClick={() => setIsConfirm(true)}
className='size-5'
color='#888'
/>
<ModalConfrim
isOpen={isConfirm}
close={() => setIsConfirm(false)}
onConfrim={() => onDelete()}
isloading={isloading}
/>
</div>
)
}
export default TrashWithConfrim