import { useMediaQuery, MutuallyExclusiveMediaQueryBreakpoints } from '@/Hooks/useMediaQuery' import { isIOS } from '@/Utils' import { DialogContent } from '@reach/dialog' import { classNames } from '@standardnotes/snjs' import { ReactNode, useMemo, useRef, useState } from 'react' import Button from '../Button/Button' import Icon from '../Icon/Icon' import Popover from '../Popover/Popover' import MobileModalAction from './MobileModalAction' import MobileModalHeader from './MobileModalHeader' import ModalAndroidBackHandler from './ModalAndroidBackHandler' export type ModalAction = { label: NonNullable type: 'primary' | 'secondary' | 'destructive' | 'cancel' onClick: () => void mobileSlot?: 'left' | 'right' hidden?: boolean disabled?: boolean } type Props = { title: string close: () => void actions?: ModalAction[] className?: { content?: string description?: string } customHeader?: ReactNode disableCustomHeader?: boolean customFooter?: ReactNode children: ReactNode } const Modal = ({ title, close, actions = [], className = {}, customHeader, disableCustomHeader = false, customFooter, children, }: Props) => { const sortedActions = useMemo( () => actions .sort((a, b) => { if (a.type === 'cancel') { return -1 } if (b.type === 'cancel') { return 1 } if (a.type === 'destructive') { return -1 } if (b.type === 'destructive') { return 1 } if (a.type === 'secondary') { return -1 } if (b.type === 'secondary') { return 1 } return 0 }) .filter((action) => !action.hidden), [actions], ) const primaryActions = sortedActions.filter((action) => action.type === 'primary') if (primaryActions.length > 1) { throw new Error('Modal can only have 1 primary action') } const cancelActions = sortedActions.filter((action) => action.type === 'cancel') if (cancelActions.length > 1) { throw new Error('Modal can only have 1 cancel action') } const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm) const leftSlotAction = sortedActions.find((action) => action.mobileSlot === 'left') const rightSlotAction = sortedActions.find((action) => action.mobileSlot === 'right') const firstPrimaryActionIndex = sortedActions.findIndex((action) => action.type === 'primary') const extraActions = sortedActions.filter((action) => action.type !== 'primary' && action.type !== 'cancel') const [showAdvanced, setShowAdvanced] = useState(false) const advancedOptionRef = useRef(null) return ( <> {customHeader && !disableCustomHeader ? ( customHeader ) : (
{leftSlotAction ? ( {leftSlotAction.label} ) : (
)}
{extraActions.length > 0 && ( <> setShowAdvanced((show) => !show)} slot="left" ref={advancedOptionRef} >
setShowAdvanced((show) => !show)} align="start" portal={false} className="!fixed divide-y divide-border border border-border" > {extraActions .filter((action) => action.type !== 'cancel') .map((action, index) => ( ))} )} {title}
{rightSlotAction ? ( {rightSlotAction.label} ) : null}
)}
{children}
{customFooter ? customFooter : sortedActions.length > 0 && (
{sortedActions.map((action, index) => ( ))}
)} ) } export default Modal