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(
target === currentTarget && onClose()) } >
{!hideCloseButton && onClose && ( )} {(header || headerContent) && (
{header && (
{!!header.imageUrl && (
)}
{!!header.supertitle && (

{header.supertitle}

)}

{header.title}

{!!header.subtitle && (
{header.subtitle.split('\n').map((line, index) => (

{line}

))}
)}
)} {headerContent}
)} {children && (
{children}
)} {footerContent && (
{footerContent}
)}
, document.body ) : null } export const ModalLoader = ( // Allow overriding visible. props: Pick & Partial> ) => ( )