import type { CloseWrapper, DialogInfo, DialogOptions, ShowDialogFn } from '@views/contexts/DialogContext'; import { DialogContext, useDialog } from '@views/contexts/DialogContext'; import type { ReactNode } from 'react'; import React, { useCallback, useRef, useState } from 'react'; import Dialog from '../Dialog'; import Text from '../Text/Text'; type DialogElement = (show: boolean) => ReactNode; /** * Represents information for a prompt dialog */ export interface PromptInfo extends Omit { title: JSX.Element | string; description: JSX.Element | string; onClose?: () => void; buttons: NonNullable; } function unwrapCloseWrapper(obj: T | CloseWrapper, close: () => void): T { if (typeof obj === 'function') { return (obj as CloseWrapper)(close); } return obj; } /** * Hook to show prompt with default stylings. */ export function usePrompt(): (info: PromptInfo, options?: DialogOptions) => void { const showDialog = useDialog(); return (info, options) => { showDialog( { ...info, title: ( {info.title} ), description: ( {info.description} ), className: 'max-w-[415px] flex flex-col gap-2.5 p-6.25 border border-ut-offwhite/50', }, options ); }; } // Unique ID counter is safe to be global let nextId = 1; /** * Allows descendant to show dialogs via a function, handling animations and stacking. */ export default function DialogProvider(props: { children: ReactNode }): JSX.Element { const dialogQueue = useRef([]); const [openDialog, setOpenDialog] = useState(); const openRef = useRef(); openRef.current = openDialog; const [isOpen, setIsOpen] = useState(false); const showDialog = useCallback((info, options) => { const id = nextId++; const handleClose = () => { setIsOpen(false); }; const infoUnwrapped = unwrapCloseWrapper(info, handleClose); const buttons = unwrapCloseWrapper(infoUnwrapped.buttons, handleClose); const onLeave = () => { setOpenDialog(undefined); if (dialogQueue.current.length > 0) { const newOpen = dialogQueue.current.pop(); setOpenDialog(() => newOpen); setIsOpen(true); } infoUnwrapped.onClose?.(); }; const dialogElement = (show: boolean) => ( {}} afterLeave={onLeave} title=<>{infoUnwrapped.title} description=<>{infoUnwrapped.description} appear={!(options?.immediate ?? false)} show={show} className={infoUnwrapped.className} >
{buttons}
); if (openRef.current) { dialogQueue.current.push(openRef.current); } setOpenDialog(() => dialogElement); setIsOpen(true); }, []); return ( {props.children} {openDialog?.(isOpen)} ); }