terra-abstract-modal
Version:
The abstract modal is a structural component that provides the ability to display portal'd content in a layer above the app.
185 lines (184 loc) • 7.82 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
require("promise-polyfill/dist/polyfill.min");
var _react = _interopRequireWildcard(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _reactPortal = require("react-portal");
var _keycodeJs = require("keycode-js");
require("mutationobserver-shim");
require("./_contains-polyfill");
require("./_matches-polyfill");
var _ModalContent = _interopRequireDefault(require("./_ModalContent"));
var _excluded = ["ariaLabel", "ariaLabelledBy", "ariaDescribedBy", "children", "classNameModal", "classNameOverlay", "closeOnEsc", "closeOnOutsideClick", "isFullscreen", "isOpen", "role", "rootSelector", "onRequestClose", "zIndex", "isCalledFromNotificationDialog", "shouldTrapFocus"]; // React-focus-on uses native promises which isn't available in IE11
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
var zIndexes = ['6000', '7000', '8000', '9000'];
var propTypes = {
/**
* String that labels the modal for screen readers.
*/
ariaLabel: _propTypes.default.string,
/**
* String that labels the modal for screen readers.
*/
ariaLabelledBy: _propTypes.default.string,
/**
* String that labels the modal for screen readers.
*/
ariaDescribedBy: _propTypes.default.string,
/**
* Content inside the modal dialog.
*/
children: _propTypes.default.node.isRequired,
/**
* CSS classnames that are appended to the modal.
*/
classNameModal: _propTypes.default.string,
/**
* CSS classnames that are appended to the overlay.
*/
classNameOverlay: _propTypes.default.string,
/**
* If set to true, the modal will close when the esc key is pressed.
*/
closeOnEsc: _propTypes.default.bool,
/**
* If set to true, the modal will close when a mouse click is triggered outside the modal.
*/
closeOnOutsideClick: _propTypes.default.bool,
/**
* If set to true, the modal will be fullscreen on all breakpoint sizes.
*/
isFullscreen: _propTypes.default.bool,
/**
* If set to true, the modal will rendered as opened.
*/
isOpen: _propTypes.default.bool.isRequired,
/**
* Callback function indicating a close condition was met, should be combined with isOpen for state management.
*/
onRequestClose: _propTypes.default.func.isRequired,
/**
* Role attribute on the modal dialog.
*/
role: _propTypes.default.string,
/**
* Allows assigning of root element custom data attribute for easy selecting of document base component.
*/
rootSelector: _propTypes.default.string,
/**
* Z-Index layer to apply to the ModalContent and ModalOverlay. Valid values are the standard modal layer: '6000', and the max layer: '8000'.
*/
zIndex: _propTypes.default.oneOf(zIndexes),
/**
* @private
* Callback function to set the reference of the element that will receive focus when the Slide content is visible.
*/
setModalFocusElementRef: _propTypes.default.func,
/**
* @private
* If set to true, the AbstractModal is rendered inside a NotificationDialog.
*/
isCalledFromNotificationDialog: _propTypes.default.bool,
/**
* If set to true, then the focus lock will get enabled.
*/
shouldTrapFocus: _propTypes.default.bool
};
var defaultProps = {
classNameModal: null,
classNameOverlay: null,
closeOnEsc: true,
closeOnOutsideClick: true,
isFullscreen: false,
role: 'dialog',
rootSelector: '#root',
zIndex: '6000',
isCalledFromNotificationDialog: false,
shouldTrapFocus: false
};
var AbstractModal = function AbstractModal(props) {
var ariaLabel = props.ariaLabel,
ariaLabelledBy = props.ariaLabelledBy,
ariaDescribedBy = props.ariaDescribedBy,
children = props.children,
classNameModal = props.classNameModal,
classNameOverlay = props.classNameOverlay,
closeOnEsc = props.closeOnEsc,
closeOnOutsideClick = props.closeOnOutsideClick,
isFullscreen = props.isFullscreen,
isOpen = props.isOpen,
role = props.role,
rootSelector = props.rootSelector,
onRequestClose = props.onRequestClose,
zIndex = props.zIndex,
isCalledFromNotificationDialog = props.isCalledFromNotificationDialog,
shouldTrapFocus = props.shouldTrapFocus,
customProps = (0, _objectWithoutProperties2.default)(props, _excluded);
var modalElementRef = (0, _react.useRef)();
(0, _react.useLayoutEffect)(function () {
// eslint-disable-next-line no-prototype-builtins
if (!Element.prototype.hasOwnProperty('inert')) {
// IE10 throws an error if wicg-inert is imported too early, as wicg-inert tries to set an observer on document.body which may not exist on import
// eslint-disable-next-line global-require
require('wicg-inert/dist/inert');
}
}, []);
(0, _react.useEffect)(function () {
function handleDocumentKeydown(e) {
if (e.keyCode === _keycodeJs.KEY_ESCAPE && closeOnEsc && isOpen) {
var body = document.querySelector('body');
if (e.target === body) {
onRequestClose();
}
}
}
document.addEventListener('keydown', handleDocumentKeydown);
return function () {
document.removeEventListener('keydown', handleDocumentKeydown);
};
}, [closeOnEsc, isOpen, onRequestClose]);
var handleKeydown = (0, _react.useCallback)(function (e) {
if (e.keyCode === _keycodeJs.KEY_ESCAPE && closeOnEsc && isOpen) {
var currentModalNode = modalElementRef.current;
if (currentModalNode && (e.target === currentModalNode || currentModalNode.contains(e.target))) {
if (e.target === modalElementRef.current || modalElementRef.current.contains(e.target)) {
onRequestClose();
}
}
}
}, [closeOnEsc, isOpen, onRequestClose, modalElementRef]);
if (!isOpen) {
return null;
}
return /*#__PURE__*/_react.default.createElement(_reactPortal.Portal, {
isOpened: isOpen
}, /*#__PURE__*/_react.default.createElement(_ModalContent.default, (0, _extends2.default)({}, customProps, {
closeOnOutsideClick: closeOnOutsideClick,
ariaLabel: ariaLabel,
ariaLabelledBy: ariaLabelledBy,
ariaDescribedBy: ariaDescribedBy,
classNameModal: classNameModal,
classNameOverlay: classNameOverlay,
role: role,
isFullscreen: isFullscreen,
onRequestClose: onRequestClose,
rootSelector: rootSelector,
zIndex: zIndex,
"aria-modal": "true",
ref: modalElementRef,
onKeyDown: handleKeydown,
isCalledFromNotificationDialog: isCalledFromNotificationDialog,
shouldTrapFocus: shouldTrapFocus
}), children));
};
AbstractModal.propTypes = propTypes;
AbstractModal.defaultProps = defaultProps;
var _default = exports.default = AbstractModal;