// src/components/ui/modal.tsx import { cn } from '@/lib/utils'; import * as DialogPrimitive from '@radix-ui/react-dialog'; import { Loader, X } from 'lucide-react'; import { FC, ReactNode, useCallback, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { createPortalModal } from './modal-manage'; export interface ModalProps { open: boolean; onOpenChange?: (open: boolean) => void; title?: ReactNode; titleClassName?: string; children: ReactNode; footer?: ReactNode; footerClassName?: string; showfooter?: boolean; className?: string; size?: 'small' | 'default' | 'large'; closable?: boolean; closeIcon?: ReactNode; maskClosable?: boolean; destroyOnClose?: boolean; full?: boolean; confirmLoading?: boolean; cancelText?: ReactNode | string; okText?: ReactNode | string; onOk?: () => void; onCancel?: () => void; } export interface ModalType extends FC { show: typeof modalIns.show; hide: typeof modalIns.hide; destroy: typeof modalIns.destroy; } const Modal: ModalType = ({ open, onOpenChange, title, titleClassName, children, footer, footerClassName, showfooter = true, className = '', size = 'default', closable = true, closeIcon = , maskClosable = true, destroyOnClose = false, full = false, onOk, onCancel, confirmLoading, cancelText, okText, }) => { const sizeClasses = { small: 'max-w-md', default: 'max-w-2xl', large: 'max-w-4xl', }; const { t } = useTranslation(); // Handle ESC key close useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape' && maskClosable) { onOpenChange?.(false); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [maskClosable, onOpenChange]); const handleCancel = useCallback(() => { onOpenChange?.(false); onCancel?.(); }, [onCancel, onOpenChange]); const handleOk = useCallback(() => { onOpenChange?.(true); onOk?.(); }, [onOk, onOpenChange]); const handleChange = (open: boolean) => { onOpenChange?.(open); console.log('open', open, onOpenChange); if (open) { onOk?.(); } if (!open) { onCancel?.(); } }; const footEl = useMemo(() => { if (showfooter === false) { return <>; } let footerTemp; if (footer) { footerTemp = footer; } else { footerTemp = (
); } return (
{footerTemp}
); }, [ footer, cancelText, t, confirmLoading, okText, handleCancel, handleOk, showfooter, footerClassName, ]); return ( maskClosable && onOpenChange?.(false)} > e.stopPropagation()} > {/* title */} {(title || closable) && (
{title && ( {title} )} {closable && ( )}
)} {/* content */}
{destroyOnClose && !open ? null : children}
{/* footer */} {footEl}
); }; let modalIns = createPortalModal(); Modal.show = modalIns ? modalIns.show : () => { modalIns = createPortalModal(); return modalIns.show; }; Modal.hide = modalIns.hide; Modal.destroy = modalIns.destroy; export { Modal };