445 lines
16 KiB
JavaScript
445 lines
16 KiB
JavaScript
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
|
|
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
|
|
|
|
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
|
|
|
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
|
|
|
var _react = require("react");
|
|
|
|
var _propTypes = require("prop-types");
|
|
|
|
var _propTypes2 = _interopRequireDefault(_propTypes);
|
|
|
|
var _focusManager = require("../helpers/focusManager");
|
|
|
|
var focusManager = _interopRequireWildcard(_focusManager);
|
|
|
|
var _scopeTab = require("../helpers/scopeTab");
|
|
|
|
var _scopeTab2 = _interopRequireDefault(_scopeTab);
|
|
|
|
var _ariaAppHider = require("../helpers/ariaAppHider");
|
|
|
|
var ariaAppHider = _interopRequireWildcard(_ariaAppHider);
|
|
|
|
var _classList = require("../helpers/classList");
|
|
|
|
var classList = _interopRequireWildcard(_classList);
|
|
|
|
var _safeHTMLElement = require("../helpers/safeHTMLElement");
|
|
|
|
var _safeHTMLElement2 = _interopRequireDefault(_safeHTMLElement);
|
|
|
|
var _portalOpenInstances = require("../helpers/portalOpenInstances");
|
|
|
|
var _portalOpenInstances2 = _interopRequireDefault(_portalOpenInstances);
|
|
|
|
require("../helpers/bodyTrap");
|
|
|
|
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
|
|
|
|
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
|
|
|
// so that our CSS is statically analyzable
|
|
var CLASS_NAMES = {
|
|
overlay: "ReactModal__Overlay",
|
|
content: "ReactModal__Content"
|
|
};
|
|
|
|
/**
|
|
* We need to support the deprecated `KeyboardEvent.keyCode` in addition to
|
|
* `KeyboardEvent.code` for apps that still support IE11. Can be removed when
|
|
* `react-modal` only supports React >18 (which dropped IE support).
|
|
*/
|
|
var isTabKey = function isTabKey(event) {
|
|
return event.code === "Tab" || event.keyCode === 9;
|
|
};
|
|
var isEscKey = function isEscKey(event) {
|
|
return event.code === "Escape" || event.keyCode === 27;
|
|
};
|
|
|
|
var ariaHiddenInstances = 0;
|
|
|
|
var ModalPortal = function (_Component) {
|
|
_inherits(ModalPortal, _Component);
|
|
|
|
function ModalPortal(props) {
|
|
_classCallCheck(this, ModalPortal);
|
|
|
|
var _this = _possibleConstructorReturn(this, (ModalPortal.__proto__ || Object.getPrototypeOf(ModalPortal)).call(this, props));
|
|
|
|
_this.setOverlayRef = function (overlay) {
|
|
_this.overlay = overlay;
|
|
_this.props.overlayRef && _this.props.overlayRef(overlay);
|
|
};
|
|
|
|
_this.setContentRef = function (content) {
|
|
_this.content = content;
|
|
_this.props.contentRef && _this.props.contentRef(content);
|
|
};
|
|
|
|
_this.afterClose = function () {
|
|
var _this$props = _this.props,
|
|
appElement = _this$props.appElement,
|
|
ariaHideApp = _this$props.ariaHideApp,
|
|
htmlOpenClassName = _this$props.htmlOpenClassName,
|
|
bodyOpenClassName = _this$props.bodyOpenClassName,
|
|
parentSelector = _this$props.parentSelector;
|
|
|
|
|
|
var parentDocument = parentSelector && parentSelector().ownerDocument || document;
|
|
|
|
// Remove classes.
|
|
bodyOpenClassName && classList.remove(parentDocument.body, bodyOpenClassName);
|
|
|
|
htmlOpenClassName && classList.remove(parentDocument.getElementsByTagName("html")[0], htmlOpenClassName);
|
|
|
|
// Reset aria-hidden attribute if all modals have been removed
|
|
if (ariaHideApp && ariaHiddenInstances > 0) {
|
|
ariaHiddenInstances -= 1;
|
|
|
|
if (ariaHiddenInstances === 0) {
|
|
ariaAppHider.show(appElement);
|
|
}
|
|
}
|
|
|
|
if (_this.props.shouldFocusAfterRender) {
|
|
if (_this.props.shouldReturnFocusAfterClose) {
|
|
focusManager.returnFocus(_this.props.preventScroll);
|
|
focusManager.teardownScopedFocus();
|
|
} else {
|
|
focusManager.popWithoutFocus();
|
|
}
|
|
}
|
|
|
|
if (_this.props.onAfterClose) {
|
|
_this.props.onAfterClose();
|
|
}
|
|
|
|
_portalOpenInstances2.default.deregister(_this);
|
|
};
|
|
|
|
_this.open = function () {
|
|
_this.beforeOpen();
|
|
if (_this.state.afterOpen && _this.state.beforeClose) {
|
|
clearTimeout(_this.closeTimer);
|
|
_this.setState({ beforeClose: false });
|
|
} else {
|
|
if (_this.props.shouldFocusAfterRender) {
|
|
focusManager.setupScopedFocus(_this.node);
|
|
focusManager.markForFocusLater();
|
|
}
|
|
|
|
_this.setState({ isOpen: true }, function () {
|
|
_this.openAnimationFrame = requestAnimationFrame(function () {
|
|
_this.setState({ afterOpen: true });
|
|
|
|
if (_this.props.isOpen && _this.props.onAfterOpen) {
|
|
_this.props.onAfterOpen({
|
|
overlayEl: _this.overlay,
|
|
contentEl: _this.content
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
};
|
|
|
|
_this.close = function () {
|
|
if (_this.props.closeTimeoutMS > 0) {
|
|
_this.closeWithTimeout();
|
|
} else {
|
|
_this.closeWithoutTimeout();
|
|
}
|
|
};
|
|
|
|
_this.focusContent = function () {
|
|
return _this.content && !_this.contentHasFocus() && _this.content.focus({ preventScroll: true });
|
|
};
|
|
|
|
_this.closeWithTimeout = function () {
|
|
var closesAt = Date.now() + _this.props.closeTimeoutMS;
|
|
_this.setState({ beforeClose: true, closesAt: closesAt }, function () {
|
|
_this.closeTimer = setTimeout(_this.closeWithoutTimeout, _this.state.closesAt - Date.now());
|
|
});
|
|
};
|
|
|
|
_this.closeWithoutTimeout = function () {
|
|
_this.setState({
|
|
beforeClose: false,
|
|
isOpen: false,
|
|
afterOpen: false,
|
|
closesAt: null
|
|
}, _this.afterClose);
|
|
};
|
|
|
|
_this.handleKeyDown = function (event) {
|
|
if (isTabKey(event)) {
|
|
(0, _scopeTab2.default)(_this.content, event);
|
|
}
|
|
|
|
if (_this.props.shouldCloseOnEsc && isEscKey(event)) {
|
|
event.stopPropagation();
|
|
_this.requestClose(event);
|
|
}
|
|
};
|
|
|
|
_this.handleOverlayOnClick = function (event) {
|
|
if (_this.shouldClose === null) {
|
|
_this.shouldClose = true;
|
|
}
|
|
|
|
if (_this.shouldClose && _this.props.shouldCloseOnOverlayClick) {
|
|
if (_this.ownerHandlesClose()) {
|
|
_this.requestClose(event);
|
|
} else {
|
|
_this.focusContent();
|
|
}
|
|
}
|
|
_this.shouldClose = null;
|
|
};
|
|
|
|
_this.handleContentOnMouseUp = function () {
|
|
_this.shouldClose = false;
|
|
};
|
|
|
|
_this.handleOverlayOnMouseDown = function (event) {
|
|
if (!_this.props.shouldCloseOnOverlayClick && event.target == _this.overlay) {
|
|
event.preventDefault();
|
|
}
|
|
};
|
|
|
|
_this.handleContentOnClick = function () {
|
|
_this.shouldClose = false;
|
|
};
|
|
|
|
_this.handleContentOnMouseDown = function () {
|
|
_this.shouldClose = false;
|
|
};
|
|
|
|
_this.requestClose = function (event) {
|
|
return _this.ownerHandlesClose() && _this.props.onRequestClose(event);
|
|
};
|
|
|
|
_this.ownerHandlesClose = function () {
|
|
return _this.props.onRequestClose;
|
|
};
|
|
|
|
_this.shouldBeClosed = function () {
|
|
return !_this.state.isOpen && !_this.state.beforeClose;
|
|
};
|
|
|
|
_this.contentHasFocus = function () {
|
|
return document.activeElement === _this.content || _this.content.contains(document.activeElement);
|
|
};
|
|
|
|
_this.buildClassName = function (which, additional) {
|
|
var classNames = (typeof additional === "undefined" ? "undefined" : _typeof(additional)) === "object" ? additional : {
|
|
base: CLASS_NAMES[which],
|
|
afterOpen: CLASS_NAMES[which] + "--after-open",
|
|
beforeClose: CLASS_NAMES[which] + "--before-close"
|
|
};
|
|
var className = classNames.base;
|
|
if (_this.state.afterOpen) {
|
|
className = className + " " + classNames.afterOpen;
|
|
}
|
|
if (_this.state.beforeClose) {
|
|
className = className + " " + classNames.beforeClose;
|
|
}
|
|
return typeof additional === "string" && additional ? className + " " + additional : className;
|
|
};
|
|
|
|
_this.attributesFromObject = function (prefix, items) {
|
|
return Object.keys(items).reduce(function (acc, name) {
|
|
acc[prefix + "-" + name] = items[name];
|
|
return acc;
|
|
}, {});
|
|
};
|
|
|
|
_this.state = {
|
|
afterOpen: false,
|
|
beforeClose: false
|
|
};
|
|
|
|
_this.shouldClose = null;
|
|
_this.moveFromContentToOverlay = null;
|
|
return _this;
|
|
}
|
|
|
|
_createClass(ModalPortal, [{
|
|
key: "componentDidMount",
|
|
value: function componentDidMount() {
|
|
if (this.props.isOpen) {
|
|
this.open();
|
|
}
|
|
}
|
|
}, {
|
|
key: "componentDidUpdate",
|
|
value: function componentDidUpdate(prevProps, prevState) {
|
|
if (process.env.NODE_ENV !== "production") {
|
|
if (prevProps.bodyOpenClassName !== this.props.bodyOpenClassName) {
|
|
// eslint-disable-next-line no-console
|
|
console.warn('React-Modal: "bodyOpenClassName" prop has been modified. ' + "This may cause unexpected behavior when multiple modals are open.");
|
|
}
|
|
if (prevProps.htmlOpenClassName !== this.props.htmlOpenClassName) {
|
|
// eslint-disable-next-line no-console
|
|
console.warn('React-Modal: "htmlOpenClassName" prop has been modified. ' + "This may cause unexpected behavior when multiple modals are open.");
|
|
}
|
|
}
|
|
|
|
if (this.props.isOpen && !prevProps.isOpen) {
|
|
this.open();
|
|
} else if (!this.props.isOpen && prevProps.isOpen) {
|
|
this.close();
|
|
}
|
|
|
|
// Focus only needs to be set once when the modal is being opened
|
|
if (this.props.shouldFocusAfterRender && this.state.isOpen && !prevState.isOpen) {
|
|
this.focusContent();
|
|
}
|
|
}
|
|
}, {
|
|
key: "componentWillUnmount",
|
|
value: function componentWillUnmount() {
|
|
if (this.state.isOpen) {
|
|
this.afterClose();
|
|
}
|
|
clearTimeout(this.closeTimer);
|
|
cancelAnimationFrame(this.openAnimationFrame);
|
|
}
|
|
}, {
|
|
key: "beforeOpen",
|
|
value: function beforeOpen() {
|
|
var _props = this.props,
|
|
appElement = _props.appElement,
|
|
ariaHideApp = _props.ariaHideApp,
|
|
htmlOpenClassName = _props.htmlOpenClassName,
|
|
bodyOpenClassName = _props.bodyOpenClassName,
|
|
parentSelector = _props.parentSelector;
|
|
|
|
|
|
var parentDocument = parentSelector && parentSelector().ownerDocument || document;
|
|
|
|
// Add classes.
|
|
bodyOpenClassName && classList.add(parentDocument.body, bodyOpenClassName);
|
|
|
|
htmlOpenClassName && classList.add(parentDocument.getElementsByTagName("html")[0], htmlOpenClassName);
|
|
|
|
if (ariaHideApp) {
|
|
ariaHiddenInstances += 1;
|
|
ariaAppHider.hide(appElement);
|
|
}
|
|
|
|
_portalOpenInstances2.default.register(this);
|
|
}
|
|
|
|
// Don't steal focus from inner elements
|
|
|
|
}, {
|
|
key: "render",
|
|
value: function render() {
|
|
var _props2 = this.props,
|
|
id = _props2.id,
|
|
className = _props2.className,
|
|
overlayClassName = _props2.overlayClassName,
|
|
defaultStyles = _props2.defaultStyles,
|
|
children = _props2.children;
|
|
|
|
var contentStyles = className ? {} : defaultStyles.content;
|
|
var overlayStyles = overlayClassName ? {} : defaultStyles.overlay;
|
|
|
|
if (this.shouldBeClosed()) {
|
|
return null;
|
|
}
|
|
|
|
var overlayProps = {
|
|
ref: this.setOverlayRef,
|
|
className: this.buildClassName("overlay", overlayClassName),
|
|
style: _extends({}, overlayStyles, this.props.style.overlay),
|
|
onClick: this.handleOverlayOnClick,
|
|
onMouseDown: this.handleOverlayOnMouseDown
|
|
};
|
|
|
|
var contentProps = _extends({
|
|
id: id,
|
|
ref: this.setContentRef,
|
|
style: _extends({}, contentStyles, this.props.style.content),
|
|
className: this.buildClassName("content", className),
|
|
tabIndex: "-1",
|
|
onKeyDown: this.handleKeyDown,
|
|
onMouseDown: this.handleContentOnMouseDown,
|
|
onMouseUp: this.handleContentOnMouseUp,
|
|
onClick: this.handleContentOnClick,
|
|
role: this.props.role,
|
|
"aria-label": this.props.contentLabel
|
|
}, this.attributesFromObject("aria", _extends({ modal: true }, this.props.aria)), this.attributesFromObject("data", this.props.data || {}), {
|
|
"data-testid": this.props.testId
|
|
});
|
|
|
|
var contentElement = this.props.contentElement(contentProps, children);
|
|
return this.props.overlayElement(overlayProps, contentElement);
|
|
}
|
|
}]);
|
|
|
|
return ModalPortal;
|
|
}(_react.Component);
|
|
|
|
ModalPortal.defaultProps = {
|
|
style: {
|
|
overlay: {},
|
|
content: {}
|
|
},
|
|
defaultStyles: {}
|
|
};
|
|
ModalPortal.propTypes = {
|
|
isOpen: _propTypes2.default.bool.isRequired,
|
|
defaultStyles: _propTypes2.default.shape({
|
|
content: _propTypes2.default.object,
|
|
overlay: _propTypes2.default.object
|
|
}),
|
|
style: _propTypes2.default.shape({
|
|
content: _propTypes2.default.object,
|
|
overlay: _propTypes2.default.object
|
|
}),
|
|
className: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.object]),
|
|
overlayClassName: _propTypes2.default.oneOfType([_propTypes2.default.string, _propTypes2.default.object]),
|
|
parentSelector: _propTypes2.default.func,
|
|
bodyOpenClassName: _propTypes2.default.string,
|
|
htmlOpenClassName: _propTypes2.default.string,
|
|
ariaHideApp: _propTypes2.default.bool,
|
|
appElement: _propTypes2.default.oneOfType([_propTypes2.default.instanceOf(_safeHTMLElement2.default), _propTypes2.default.instanceOf(_safeHTMLElement.SafeHTMLCollection), _propTypes2.default.instanceOf(_safeHTMLElement.SafeNodeList), _propTypes2.default.arrayOf(_propTypes2.default.instanceOf(_safeHTMLElement2.default))]),
|
|
onAfterOpen: _propTypes2.default.func,
|
|
onAfterClose: _propTypes2.default.func,
|
|
onRequestClose: _propTypes2.default.func,
|
|
closeTimeoutMS: _propTypes2.default.number,
|
|
shouldFocusAfterRender: _propTypes2.default.bool,
|
|
shouldCloseOnOverlayClick: _propTypes2.default.bool,
|
|
shouldReturnFocusAfterClose: _propTypes2.default.bool,
|
|
preventScroll: _propTypes2.default.bool,
|
|
role: _propTypes2.default.string,
|
|
contentLabel: _propTypes2.default.string,
|
|
aria: _propTypes2.default.object,
|
|
data: _propTypes2.default.object,
|
|
children: _propTypes2.default.node,
|
|
shouldCloseOnEsc: _propTypes2.default.bool,
|
|
overlayRef: _propTypes2.default.func,
|
|
contentRef: _propTypes2.default.func,
|
|
id: _propTypes2.default.string,
|
|
overlayElement: _propTypes2.default.func,
|
|
contentElement: _propTypes2.default.func,
|
|
testId: _propTypes2.default.string
|
|
};
|
|
exports.default = ModalPortal;
|
|
module.exports = exports["default"]; |