import { Close } from '@mui/icons-material' import clsx from 'clsx' import { useEffect, useRef } from 'react' import { createPortal } from 'react-dom' import { ModalProps } from '@dao-dao/types/components/Modal' import { useMountedInBrowser } from '../../hooks' import { ErrorBoundary } from '../error/ErrorBoundary' import { IconButton } from '../icon_buttons' import { Loader } from '../logo/Loader' export * from '@dao-dao/types/components/Modal' // This component renders a modal above the page content with a dim backdrop. // // Ideally, it is not conditionally rendered, but instead is always rendered and // its `visible` prop is used to control its visibility. This is because the // fade in/out animation only occurs when the component is mounted and // hidden/unhidden. Some modals, like the stateful PfpkNftSelectionModal, are // conditionally rendered because they load a good amount of state which we // don't want to load until necessary. // // Common gotcha: If adding any keypress listeners to navigate or perform // actions in the modal, make sure to only add the listeners when the modal is // visible. See the code below which adds a keypress listener to close the modal // on escape, which only adds the listener when visible. export const Modal = ({ children, visible, onClose, backdropClassName, containerClassName, hideCloseButton, header, headerContent, footerContent, headerContainerClassName, contentContainerClassName, footerContainerClassName, titleClassName, smallCloseButton, closeButtonClassName, }: ModalProps) => { // Close modal on escape, only listening if visible. useEffect(() => { if (!onClose || !visible) { return } const handleKeyPress = (event: KeyboardEvent) => event.key === 'Escape' && onClose() // Attach event listener. document.addEventListener('keydown', handleKeyPress) // Clean up event listener. return () => document.removeEventListener('keydown', handleKeyPress) }, [onClose, visible]) const mountedInBrowser = useMountedInBrowser() const openedOnce = useRef(visible) if (visible && !openedOnce.current) { openedOnce.current = true } // Don't render until first time the modal is visible to conserve memory. if (!openedOnce.current) { return null } return mountedInBrowser ? createPortal(
{header.supertitle}
)}{header.title}
{!!header.subtitle && ({line}
))}