diff --git a/lib/components/modal-provider.react.js b/lib/components/modal-provider.react.js new file mode 100644 --- /dev/null +++ b/lib/components/modal-provider.react.js @@ -0,0 +1,68 @@ +// @flow + +import invariant from 'invariant'; +import * as React from 'react'; + +import { getUUID } from '../utils/uuid'; + +type Props = { + +children: React.Node, +}; +type ModalContextType = { + +modals: $ReadOnlyArray<[React.Node, string]>, + +pushModal: React.Node => void, + +popModal: () => void, + +clearModals: () => void, +}; + +const ModalContext: React.Context = React.createContext( + { + modals: [], + pushModal: () => {}, + popModal: () => {}, + clearModals: () => {}, + }, +); + +function ModalProvider(props: Props): React.Node { + const { children } = props; + + const [modals, setModals] = React.useState< + $ReadOnlyArray<[React.Node, string]>, + >([]); + + const popModal = React.useCallback( + () => setModals(oldModals => oldModals.slice(0, -1)), + [], + ); + + const pushModal = React.useCallback(newModal => { + const key = getUUID(); + setModals(oldModals => [...oldModals, [newModal, key]]); + }, []); + + const clearModals = React.useCallback(() => setModals([]), []); + + const value = React.useMemo( + () => ({ + modals, + pushModal, + popModal, + clearModals, + }), + [modals, pushModal, popModal, clearModals], + ); + + return ( + {children} + ); +} + +function useModalContext(): ModalContextType { + const context = React.useContext(ModalContext); + invariant(context, 'ModalContext not found'); + + return context; +} + +export { ModalProvider, useModalContext }; diff --git a/lib/components/modal.css b/lib/components/modal.css new file mode 100644 --- /dev/null +++ b/lib/components/modal.css @@ -0,0 +1,13 @@ +div.modalOverlay { + position: fixed; + left: 0; + top: 0; + height: 100%; + z-index: 4; + width: 100%; + background-color: #000000e6; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} diff --git a/lib/components/modal.react.js b/lib/components/modal.react.js new file mode 100644 --- /dev/null +++ b/lib/components/modal.react.js @@ -0,0 +1,54 @@ +// @flow + +import * as React from 'react'; + +import css from './modal.css'; + +type ModalProps = { + +onClose: () => void, + +children?: React.Node, +}; + +function Modal(props: ModalProps): React.Node { + const { children, onClose } = props; + + const overlayRef = React.useRef(); + + React.useLayoutEffect(() => { + if (overlayRef.current) { + overlayRef.current.focus(); + } + }, []); + + const onBackgroundClick = React.useCallback( + event => { + if (event.target === overlayRef.current) { + onClose(); + } + }, + [onClose], + ); + + const onKeyDown = React.useCallback( + event => { + if (event.keyCode === 27) { + onClose(); + } + }, + [onClose], + ); + + return ( +
+ {children} +
+ ); +} + +export default Modal;