UNPKG

@atlaskit/modal-dialog

Version:

A modal dialog displays content that requires user interaction, in a layer above the page.

113 lines (110 loc) 4.01 kB
import _extends from "@babel/runtime/helpers/extends"; /** @jsx jsx */ import { useCallback } from 'react'; import { css, jsx } from '@emotion/react'; import FocusLock from 'react-focus-lock'; import ScrollLock, { TouchScrollable } from 'react-scrolllock'; import { usePlatformLeafEventHandler } from '@atlaskit/analytics-next'; import Blanket from '@atlaskit/blanket'; import noop from '@atlaskit/ds-lib/noop'; import useCloseOnEscapePress from '@atlaskit/ds-lib/use-close-on-escape-press'; import FadeIn from '@atlaskit/motion/fade-in'; import Portal from '@atlaskit/portal'; import { layers } from '@atlaskit/theme/constants'; import ModalDialog from './internal/components/modal-dialog'; import useModalStack from './internal/hooks/use-modal-stack'; import usePreventProgrammaticScroll from './internal/hooks/use-prevent-programmatic-scroll'; const fillScreenStyles = css({ width: '100vw', height: '100vh', position: 'fixed', // eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage-spacing top: 0, // eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage-spacing left: 0, overflowY: 'auto', WebkitOverflowScrolling: 'touch' }); const whiteListElements = element => { // allows focus to reach elements outside the modal if they contain the data-atlas-extension attribute return !element.hasAttribute('data-atlas-extension'); }; /** * __Modal wrapper__ * * A modal wrapper displays content that requires user interaction, in a layer above the page. * This component is primary container for other modal components. * * - [Examples](https://atlassian.design/components/modal-dialog/examples) * - [Code](https://atlassian.design/components/modal-dialog/code) * - [Usage](https://atlassian.design/components/modal-dialog/usage) */ const ModalWrapper = props => { const { autoFocus = true, shouldCloseOnEscapePress = true, shouldCloseOnOverlayClick = true, shouldScrollInViewport = false, stackIndex: stackIndexOverride, onClose = noop, onStackChange = noop, isBlanketHidden, children, height, width, onCloseComplete, onOpenComplete, testId } = props; const calculatedStackIndex = useModalStack({ onStackChange }); const stackIndex = stackIndexOverride || calculatedStackIndex; const isForeground = stackIndex === 0; // When a user supplies a ref to focus we skip auto focus via react-focus-lock const autoFocusLock = typeof autoFocus === 'boolean' ? autoFocus : false; const onCloseHandler = usePlatformLeafEventHandler({ fn: onClose, action: 'closed', componentName: 'modalDialog', packageName: "@atlaskit/modal-dialog", packageVersion: "12.4.12" }); const onBlanketClicked = useCallback(e => { if (shouldCloseOnOverlayClick) { onCloseHandler(e); } }, [shouldCloseOnOverlayClick, onCloseHandler]); usePreventProgrammaticScroll(); useCloseOnEscapePress({ onClose: onCloseHandler, isDisabled: !shouldCloseOnEscapePress || !isForeground }); const modalDialogWithBlanket = jsx(Blanket, { isTinted: !isBlanketHidden, onBlanketClicked: onBlanketClicked, testId: testId && `${testId}--blanket` }, jsx(ModalDialog, { testId: testId, autoFocus: autoFocus, stackIndex: stackIndex, onClose: onCloseHandler, shouldScrollInViewport: shouldScrollInViewport, height: height, width: width, onCloseComplete: onCloseComplete, onOpenComplete: onOpenComplete }, children)); return jsx(Portal, { zIndex: layers.modal() }, jsx(FadeIn, null, fadeInProps => jsx("div", _extends({}, fadeInProps, { css: fillScreenStyles, "aria-hidden": !isForeground }), jsx(FocusLock, { autoFocus: autoFocusLock, disabled: !isForeground, returnFocus: true, whiteList: whiteListElements }, jsx(ScrollLock, null), shouldScrollInViewport ? jsx(TouchScrollable, null, modalDialogWithBlanket) : modalDialogWithBlanket)))); }; export default ModalWrapper;