diff --git a/web/modals/modal-overlay.css b/web/modals/modal-overlay.css new file mode 100644 index 000000000..89037f396 --- /dev/null +++ b/web/modals/modal-overlay.css @@ -0,0 +1,13 @@ +div.modalOverlay { + position: fixed; + left: 0; + top: 0; + height: 100%; + z-index: 4; + width: 100%; + background-color: rgba(0, 0, 0, 0.9); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} diff --git a/web/modals/modal-overlay.react.js b/web/modals/modal-overlay.react.js new file mode 100644 index 000000000..dbccb2fc9 --- /dev/null +++ b/web/modals/modal-overlay.react.js @@ -0,0 +1,63 @@ +// @flow + +import * as React from 'react'; + +import css from './modal-overlay.css'; + +type ModalOverlayProps = { + +onClose: () => void, + +children?: React.Node, +}; + +function ModalOverlay(props: ModalOverlayProps): React.Node { + const { children, onClose } = props; + + const overlayRef = React.useRef(); + const firstClickRef = React.useRef(null); + + const onBackgroundMouseDown = React.useCallback(event => { + firstClickRef.current = event.target; + }, []); + + const onBackgroundMouseUp = React.useCallback( + event => { + if ( + event.target === overlayRef.current && + firstClickRef.current === overlayRef.current + ) { + onClose(); + } + }, + [onClose], + ); + + const onKeyDown = React.useCallback( + event => { + if (event.keyCode === 27) { + onClose(); + } + }, + [onClose], + ); + + React.useEffect(() => { + if (overlayRef.current) { + overlayRef.current.focus(); + } + }, []); + + return ( +
+ {children} +
+ ); +} + +export default ModalOverlay; diff --git a/web/modals/modal.css b/web/modals/modal.css index 9cad1b387..2d64163bf 100644 --- a/web/modals/modal.css +++ b/web/modals/modal.css @@ -1,57 +1,43 @@ -div.modalOverlay { - position: fixed; - left: 0; - top: 0; - height: 100%; - z-index: 4; - width: 100%; - background-color: rgba(0, 0, 0, 0.9); - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; -} - div.modalContainer { display: flex; background-color: var(--modal-bg); border-radius: 8px; flex-direction: column; margin: 20px; overflow: hidden; } div.modalContainerSmall { width: 330px; } div.modalContainerLarge { width: 500px; } span.modalClose { display: flex; color: var(--modal-close-color); } span.modalClose:hover { cursor: pointer; color: var(--modal-close-color-hover); } div.modalHeader { display: flex; justify-content: space-between; align-items: center; padding: 32px 32px 0 32px; } h2.title { font-size: 20px; font-weight: 500; line-height: 32px; color: var(--fg); display: flex; align-items: center; column-gap: 8px; } diff --git a/web/modals/modal.react.js b/web/modals/modal.react.js index 169bd0470..567bef0a5 100644 --- a/web/modals/modal.react.js +++ b/web/modals/modal.react.js @@ -1,117 +1,78 @@ // @flow import classNames from 'classnames'; import * as React from 'react'; import SWMansionIcon, { type Icon } from '../SWMansionIcon.react'; +import ModalOverlay from './modal-overlay.react'; import css from './modal.css'; export type ModalSize = 'small' | 'large' | 'fit-content'; export type ModalOverridableProps = { +name: string, +icon?: Icon, +onClose: () => void, +withCloseButton?: boolean, +size?: ModalSize, }; type ModalProps = { ...ModalOverridableProps, +children?: React.Node, }; function Modal(props: ModalProps): React.Node { const { size = 'small', children, onClose, name, icon, withCloseButton = true, } = props; - const overlayRef = React.useRef(); - const firstClickRef = React.useRef(null); - - const onBackgroundMouseDown = React.useCallback(event => { - firstClickRef.current = event.target; - }, []); - - const onBackgroundMouseUp = React.useCallback( - event => { - if ( - event.target === overlayRef.current && - firstClickRef.current === overlayRef.current - ) { - onClose(); - } - }, - [onClose], - ); - - const onKeyDown = React.useCallback( - event => { - if (event.keyCode === 27) { - onClose(); - } - }, - [onClose], - ); - - React.useEffect(() => { - if (overlayRef.current) { - overlayRef.current.focus(); - } - }, []); const modalContainerClasses = React.useMemo( () => classNames(css.modalContainer, { [css.modalContainerLarge]: size === 'large', [css.modalContainerSmall]: size === 'small', }), [size], ); const cornerCloseButton = React.useMemo(() => { if (!withCloseButton) { return null; } return ( ); }, [onClose, withCloseButton]); const headerIcon = React.useMemo(() => { if (!icon) { return null; } return ; }, [icon]); return ( -
+

{headerIcon} {name}

{cornerCloseButton}
{children}
-
+ ); } export default Modal;