Files
clinicpro/assets/admin/components/ui/Modal.tsx
T
hamed 02c34bac8e fix(modal): render Modal with React Portal to center it on the screen
fix(calendar): add type="button" to all buttons in PersianCalendar to prevent form submission
2026-07-02 11:48:34 +03:30

53 lines
1.4 KiB
TypeScript

import React, { useEffect } from 'react';
import { createPortal } from 'react-dom';
import { XMarkIcon } from '@heroicons/react/24/outline';
type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
const sizeMap: Record<ModalSize, string> = {
sm: '420px',
md: '540px',
lg: '720px',
xl: '960px',
};
interface Props {
open: boolean;
title: string;
size?: ModalSize;
onClose: () => void;
children: React.ReactNode;
footer?: React.ReactNode;
}
export default function Modal({ open, title, size = 'md', onClose, children, footer }: Props) {
useEffect(() => {
if (!open) return;
const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };
document.addEventListener('keydown', onKey);
return () => document.removeEventListener('keydown', onKey);
}, [open, onClose]);
if (!open) return null;
return createPortal(
<div className="overlay" onClick={onClose}>
<div
className="modal"
style={{ maxWidth: sizeMap[size] }}
onClick={(e) => e.stopPropagation()}
>
<div className="modal-head">
<h2>{title}</h2>
<button type="button" className="mini-btn" onClick={onClose}>
<XMarkIcon style={{ width: 18, height: 18 }} />
</button>
</div>
<div className="modal-body">{children}</div>
{footer && <div className="modal-foot">{footer}</div>}
</div>
</div>,
document.body
);
}