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.
198 lines (196 loc) • 8.95 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"));
var _react = _interopRequireWildcard(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _reactIntl = require("react-intl");
var _classnames = _interopRequireDefault(require("classnames"));
var _bind = _interopRequireDefault(require("classnames/bind"));
var _terraThemeContext = _interopRequireDefault(require("terra-theme-context"));
var _terraVisuallyHiddenText = _interopRequireDefault(require("terra-visually-hidden-text"));
var _reactFocusOn = require("react-focus-on");
var _ModalOverlay = _interopRequireDefault(require("./_ModalOverlay"));
var _inertHelpers = require("./inertHelpers");
var _ModalContentModule = _interopRequireDefault(require("./ModalContent.module.scss"));
var _excluded = ["ariaLabel", "ariaLabelledBy", "ariaDescribedBy", "children", "classNameModal", "classNameOverlay", "closeOnOutsideClick", "onRequestClose", "role", "isFullscreen", "isScrollable", "rootSelector", "zIndex", "setModalFocusElementRef", "isCalledFromNotificationDialog", "shouldTrapFocus"];
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 cx = _bind.default.bind(_ModalContentModule.default);
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 a mouseclick is triggered outside the modal.
*/
closeOnOutsideClick: _propTypes.default.bool,
/**
* Callback function indicating a close condition was met, should be combined with isOpen for state management.
*/
onRequestClose: _propTypes.default.func.isRequired,
/**
* If set to true, the modal will be fullscreen on all breakpoint sizes.
*/
isFullscreen: _propTypes.default.bool,
/**
* If set to true, the modal dialog with have overflow-y set to scroll.
*/
isScrollable: _propTypes.default.bool,
/**
* 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.
*/
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,
closeOnOutsideClick: true,
isFullscreen: false,
isScrollable: false,
role: 'dialog',
rootSelector: '#root',
zIndex: '6000',
isCalledFromNotificationDialog: false
};
var ModalContent = /*#__PURE__*/(0, _react.forwardRef)(function (props, ref) {
var ariaLabel = props.ariaLabel,
ariaLabelledBy = props.ariaLabelledBy,
ariaDescribedBy = props.ariaDescribedBy,
children = props.children,
classNameModal = props.classNameModal,
classNameOverlay = props.classNameOverlay,
closeOnOutsideClick = props.closeOnOutsideClick,
onRequestClose = props.onRequestClose,
role = props.role,
isFullscreen = props.isFullscreen,
isScrollable = props.isScrollable,
rootSelector = props.rootSelector,
zIndex = props.zIndex,
setModalFocusElementRef = props.setModalFocusElementRef,
isCalledFromNotificationDialog = props.isCalledFromNotificationDialog,
shouldTrapFocus = props.shouldTrapFocus,
customProps = (0, _objectWithoutProperties2.default)(props, _excluded);
(0, _react.useEffect)(function () {
// Store element that was last focused prior to modal opening
var modalTrigger = document.activeElement;
(0, _inertHelpers.showModalDomUpdates)(ref.current, rootSelector);
return function () {
(0, _inertHelpers.hideModalDomUpdates)(modalTrigger, rootSelector);
};
}, [ref, rootSelector]);
var zIndexLayer = '6000';
if (zIndexes.indexOf(zIndex) >= 0) {
zIndexLayer = zIndex;
}
var theme = _react.default.useContext(_terraThemeContext.default);
var modalClassName = (0, _classnames.default)(cx('abstract-modal', {
'is-fullscreen': isFullscreen
}, "layer-".concat(zIndexLayer), theme.className), classNameModal);
var modalContainerClassName = (0, _classnames.default)(cx('abstract-modal-container'));
// Delete the closePortal prop that comes from react-portal.
delete customProps.closePortal;
delete customProps.fallbackFocus;
var beginLabelId = ariaLabel === 'Modal' ? 'Terra.AbstractModal.BeginModalDialog' : 'Terra.AbstractModal.BeginModalDialogTitle';
var endLabelId = ariaLabel === 'Modal' ? 'Terra.AbstractModal.EndModalDialog' : 'Terra.AbstractModal.EndModalDialogTitle';
var modalContent = /*#__PURE__*/_react.default.createElement("div", (0, _extends2.default)({}, customProps, {
"aria-label": ariaLabel,
"aria-labelledby": ariaLabelledBy,
"aria-describedby": ariaDescribedBy,
className: modalClassName,
role: role,
ref: ref
}), /*#__PURE__*/_react.default.createElement("div", {
className: modalContainerClassName,
ref: setModalFocusElementRef,
"data-terra-abstract-modal-begin": true,
tabIndex: "-1"
}, !isCalledFromNotificationDialog && /*#__PURE__*/_react.default.createElement(_reactIntl.FormattedMessage, {
id: beginLabelId,
values: {
title: ariaLabel
}
}, function (text) {
// In the latest version of react-intl this param is an array, when previous versions it was a string.
var useText = text;
if (Array.isArray(text)) {
useText = text.join('');
}
return /*#__PURE__*/_react.default.createElement(_terraVisuallyHiddenText.default, {
text: useText
});
}), children, !isCalledFromNotificationDialog && /*#__PURE__*/_react.default.createElement(_reactIntl.FormattedMessage, {
id: endLabelId,
values: {
title: ariaLabel
}
}, function (text) {
// In the latest version of react-intl this param is an array, when previous versions it was a string.
var useText = text;
if (Array.isArray(text)) {
useText = text.join('');
}
return /*#__PURE__*/_react.default.createElement(_terraVisuallyHiddenText.default, {
text: useText
});
})));
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_ModalOverlay.default, {
onClick: closeOnOutsideClick ? onRequestClose : null,
className: classNameOverlay,
zIndex: zIndexLayer
}), shouldTrapFocus ? /*#__PURE__*/_react.default.createElement(_reactFocusOn.FocusOn, {
onClickOutside: closeOnOutsideClick ? onRequestClose : null
}, modalContent) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, modalContent));
});
ModalContent.propTypes = propTypes;
ModalContent.defaultProps = defaultProps;
var _default = exports.default = ModalContent;