@atlaskit/modal-dialog
Version:
A modal dialog displays content that requires user interaction, in a layer above the page.
159 lines (156 loc) • 7.03 kB
JavaScript
/* modal-wrapper.tsx generated by @compiled/babel-plugin v0.39.1 */
import _extends from "@babel/runtime/helpers/extends";
import "./modal-wrapper.compiled.css";
import * as React from 'react';
import { ax, ix } from "@compiled/react/runtime";
import { useCallback } from '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 { Layering } from '@atlaskit/layering';
import { useNotifyOpenLayerObserver } from '@atlaskit/layering/experimental/open-layer-observer';
import { Motion } from '@atlaskit/motion';
import FadeIn from '@atlaskit/motion/fade-in';
import { fg } from '@atlaskit/platform-feature-flags';
import Portal from '@atlaskit/portal';
import { layers } from '@atlaskit/theme/constants';
import useModalStack from '../hooks/use-modal-stack';
import usePreventProgrammaticScroll from '../hooks/use-prevent-programmatic-scroll';
import ModalDialog from './modal-dialog';
const fillScreenStyles = null;
const allowlistElements = (element, callback) => {
// Allow focus to reach elements outside the modal:
// if AUI dialog is allowListed and visible
if (!!document.querySelector('.aui-blanket:not([hidden])')) {
return false;
}
// allows to pass a callback function to allow elements be ignored by focus lock
if (typeof callback === 'function') {
return callback(element);
}
return true;
};
/**
* __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 InternalModalWrapper = props => {
const {
autoFocus = true,
focusLockAllowlist,
shouldCloseOnEscapePress = true,
shouldCloseOnOverlayClick = true,
shouldScrollInViewport = false,
shouldReturnFocus = true,
stackIndex: stackIndexOverride,
onClose: providedOnClose,
onStackChange = noop,
isBlanketHidden,
children,
height,
width,
onCloseComplete,
onOpenComplete,
label,
testId,
isFullScreen
} = 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
// When flag is true and a ref is not supplied, autofocus is true. See https://product-fabric.atlassian.net/browse/DSP-24307
// When we remove boolean `autoFocus`, we won't have to worry about this
const autoFocusLock = fg('platform_dst_autofocus-never-false-2') ? typeof autoFocus === 'boolean' : typeof autoFocus === 'boolean' ? autoFocus : false;
const onCloseHandler = usePlatformLeafEventHandler({
fn: providedOnClose || noop,
action: 'closed',
componentName: 'modalDialog',
packageName: "@atlaskit/modal-dialog",
packageVersion: "14.14.2"
});
const onBlanketClicked = useCallback(e => {
if (shouldCloseOnOverlayClick) {
onCloseHandler(e);
}
}, [shouldCloseOnOverlayClick, onCloseHandler]);
// This ensures to prevent additional re-renders while nothing is passed to focusLockAllowlist explicitly.
const allowListCallback = useCallback(element => allowlistElements(element, focusLockAllowlist), [focusLockAllowlist]);
usePreventProgrammaticScroll();
useNotifyOpenLayerObserver({
type: 'modal',
// Modal dialog is conditionally rendered when visible, so when this runs it is always open.
isOpen: true,
// Passing a no-op for now, as there isn't a real use case for closing the modal dialog programmatically
// by the OpenLayerObserver. The only current use case is closing layers when resizing the nav layout,
// which cannot happen while a modal dialog is open.
onClose: noop
});
const modalDialogWithBlanket = /*#__PURE__*/React.createElement(Blanket, {
isTinted: !isBlanketHidden,
onBlanketClicked: onBlanketClicked,
testId: testId && `${testId}--blanket`
}, /*#__PURE__*/React.createElement(ModalDialog, {
testId: testId,
label: label,
autoFocus: autoFocus,
stackIndex: stackIndex,
onClose: onCloseHandler,
shouldCloseOnEscapePress: shouldCloseOnEscapePress && isForeground,
shouldScrollInViewport: shouldScrollInViewport,
height: height,
width: width,
onCloseComplete: onCloseComplete,
onOpenComplete: onOpenComplete,
hasProvidedOnClose: Boolean(providedOnClose),
isFullScreen: isFullScreen
}, children));
let returnFocus = true;
let onDeactivation = noop;
if ('boolean' === typeof shouldReturnFocus) {
returnFocus = shouldReturnFocus;
} else {
onDeactivation = () => {
window.setTimeout(() => {
var _shouldReturnFocus$cu;
(_shouldReturnFocus$cu = shouldReturnFocus.current) === null || _shouldReturnFocus$cu === void 0 ? void 0 : _shouldReturnFocus$cu.focus();
}, 0);
};
}
return /*#__PURE__*/React.createElement(Layering, {
isDisabled: false
}, /*#__PURE__*/React.createElement(Portal, {
zIndex: layers.modal()
}, fg('platform-dst-motion-uplift') ? /*#__PURE__*/React.createElement(Motion, {
enteringAnimation: "var(--ds-content-enter-medium, 200ms cubic-bezier(0.4, 0, 0, 1) FadeIn)",
exitingAnimation: "var(--ds-content-exit-long, 200ms cubic-bezier(0.4, 0, 0, 1) FadeOut)"
}, /*#__PURE__*/React.createElement("div", {
"aria-hidden": !isForeground,
className: ax(["_1bsbauwl _4t3i1kxc _kqsw1n9t _152tze3t _1e02ze3t _18m91wug _8am5i4x0"])
}, /*#__PURE__*/React.createElement(FocusLock, {
autoFocus: autoFocusLock,
returnFocus: returnFocus,
onDeactivation: onDeactivation,
whiteList: allowListCallback
}, /*#__PURE__*/React.createElement(ScrollLock, null), shouldScrollInViewport ? /*#__PURE__*/React.createElement(TouchScrollable, null, modalDialogWithBlanket) : modalDialogWithBlanket))) : /*#__PURE__*/React.createElement(FadeIn, null, fadeInProps => /*#__PURE__*/React.createElement("div", _extends({}, fadeInProps, {
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop
className: ax(["_1bsbauwl _4t3i1kxc _kqsw1n9t _152tze3t _1e02ze3t _18m91wug _8am5i4x0", fadeInProps.className]),
"aria-hidden": !isForeground
}), /*#__PURE__*/React.createElement(FocusLock, {
autoFocus: autoFocusLock,
returnFocus: returnFocus,
onDeactivation: onDeactivation,
whiteList: allowListCallback
}, /*#__PURE__*/React.createElement(ScrollLock, null), shouldScrollInViewport ? /*#__PURE__*/React.createElement(TouchScrollable, null, modalDialogWithBlanket) : modalDialogWithBlanket)))));
};
export default InternalModalWrapper;