import { UIRouterContextComponent } from '@uirouter/react-hybrid'; import React from 'react'; import ReactDOM from 'react-dom'; import type { IModalProps } from './Modal'; import { Modal } from './Modal'; /** The Modal content Component will be passed these two props */ export interface IModalComponentProps { // Close modal with a result value (i.e., OK button) closeModal?(result?: C): void; // Dismiss/reject modal (i.e., Cancel button) dismissModal?(result?: D): void; } interface ModalCloseResult { status: 'CLOSED'; closeResult: T; } interface ModalDismissResult { status: 'DISMISSED'; dismissResult?: T; } export type IModalResult = ModalCloseResult | ModalDismissResult; /** * An imperative API for showing a react component as a modal. * * example: * const MyComponent = ({ closeModal, dismissModal }) => { *

Modal Contents!

* * * * } * * ... * * showModal(MyComponent).then(result => { * this.setState({ result }); * }); * * @param ModalComponent the component to be rendered inside a modal * @param componentProps to pass to the ModalComponent * @param modalProps props to pass to the modal itself * @returns {Promise} */ export const showModal = ( ModalComponent: React.ComponentType

>, componentProps?: P, modalProps?: Omit, ): Promise> => new Promise>((resolve) => { let mountNode = document.createElement('div'); let show = false; function onAfterClose() { if (!mountNode) { return; } ReactDOM.unmountComponentAtNode(mountNode); mountNode = null; } const handleResultWith = (resultHandler: (result: C | D) => IModalResult) => (result: C | D) => { if (!mountNode) { return; } resolve(resultHandler(result)); // Switch `show` to false to trigger exit transition and call onExitComplete show = false; render(); }; const handleClose = handleResultWith((result: C) => ({ status: 'CLOSED', closeResult: result })); const handleDismiss = handleResultWith((result: D) => ({ status: 'DISMISSED', dismissResult: result })); const handleRequestClose = () => handleDismiss(null); function render() { ReactDOM.render( , mountNode, ); } // If the first render has show=true, the enter transition for the modal // will short-circuit because all the transition classes are added // immediately in a single paint. // Instead let's render once with show=false, let the browser paint, // then render a second time with show=true. render(); setTimeout(() => { if (!mountNode) { return; } show = true; render(); }); });