@material-ui/core
Version:
React components that implement Google's Material Design.
507 lines (406 loc) • 16.1 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.styles = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
var _react = _interopRequireDefault(require("react"));
var _reactDom = _interopRequireDefault(require("react-dom"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _classnames = _interopRequireDefault(require("classnames"));
var _warning = _interopRequireDefault(require("warning"));
var _keycode = _interopRequireDefault(require("keycode"));
var _ownerDocument = _interopRequireDefault(require("../utils/ownerDocument"));
var _RootRef = _interopRequireDefault(require("../RootRef"));
var _Portal = _interopRequireDefault(require("../Portal"));
var _helpers = require("../utils/helpers");
var _withStyles = _interopRequireDefault(require("../styles/withStyles"));
var _ModalManager = _interopRequireDefault(require("./ModalManager"));
var _Backdrop = _interopRequireDefault(require("../Backdrop"));
function getContainer(container, defaultContainer) {
container = typeof container === 'function' ? container() : container;
return _reactDom.default.findDOMNode(container) || defaultContainer;
}
function getHasTransition(props) {
return props.children ? props.children.props.hasOwnProperty('in') : false;
}
var styles = function styles(theme) {
return {
/* Styles applied to the root element. */
root: {
position: 'fixed',
zIndex: theme.zIndex.modal,
right: 0,
bottom: 0,
top: 0,
left: 0
},
/* Styles applied to the root element if the `Modal` has exited. */
hidden: {
visibility: 'hidden'
}
};
};
/* istanbul ignore if */
exports.styles = styles;
if (process.env.NODE_ENV !== 'production' && !_react.default.createContext) {
throw new Error('Material-UI: react@16.3.0 or greater is required.');
}
/**
* This component shares many concepts with [react-overlays](https://react-bootstrap.github.io/react-overlays/#modals).
*/
var Modal =
/*#__PURE__*/
function (_React$Component) {
(0, _inherits2.default)(Modal, _React$Component);
function Modal(props) {
var _this;
(0, _classCallCheck2.default)(this, Modal);
_this = (0, _possibleConstructorReturn2.default)(this, (0, _getPrototypeOf2.default)(Modal).call(this));
_this.mounted = false;
_this.handleRendered = function () {
_this.autoFocus(); // Fix a bug on Chrome where the scroll isn't initially 0.
_this.modalRef.scrollTop = 0;
if (_this.props.onRendered) {
_this.props.onRendered();
}
};
_this.handleOpen = function () {
var doc = (0, _ownerDocument.default)(_this.mountNode);
var container = getContainer(_this.props.container, doc.body);
_this.props.manager.add((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)), container);
doc.addEventListener('keydown', _this.handleDocumentKeyDown);
doc.addEventListener('focus', _this.enforceFocus, true);
};
_this.handleClose = function () {
_this.props.manager.remove((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)));
var doc = (0, _ownerDocument.default)(_this.mountNode);
doc.removeEventListener('keydown', _this.handleDocumentKeyDown);
doc.removeEventListener('focus', _this.enforceFocus, true);
_this.restoreLastFocus();
};
_this.handleExited = function () {
_this.setState({
exited: true
});
_this.handleClose();
};
_this.handleBackdropClick = function (event) {
if (event.target !== event.currentTarget) {
return;
}
if (_this.props.onBackdropClick) {
_this.props.onBackdropClick(event);
}
if (!_this.props.disableBackdropClick && _this.props.onClose) {
_this.props.onClose(event, 'backdropClick');
}
};
_this.handleDocumentKeyDown = function (event) {
if (!_this.isTopModal() || (0, _keycode.default)(event) !== 'esc') {
return;
} // Ignore events that have been `event.preventDefault()` marked.
if (event.defaultPrevented) {
return;
}
if (_this.props.onEscapeKeyDown) {
_this.props.onEscapeKeyDown(event);
}
if (!_this.props.disableEscapeKeyDown && _this.props.onClose) {
_this.props.onClose(event, 'escapeKeyDown');
}
};
_this.checkForFocus = function () {
_this.lastFocus = (0, _ownerDocument.default)(_this.mountNode).activeElement;
};
_this.enforceFocus = function () {
if (_this.props.disableEnforceFocus || !_this.mounted || !_this.isTopModal()) {
return;
}
var currentActiveElement = (0, _ownerDocument.default)(_this.mountNode).activeElement;
if (_this.dialogRef && !_this.dialogRef.contains(currentActiveElement)) {
_this.dialogRef.focus();
}
};
_this.state = {
exited: !props.open
};
return _this;
}
(0, _createClass2.default)(Modal, [{
key: "componentDidMount",
value: function componentDidMount() {
this.mounted = true;
if (this.props.open) {
this.handleOpen();
}
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
if (!prevProps.open && this.props.open) {
this.checkForFocus();
}
if (prevProps.open && !this.props.open && !getHasTransition(this.props)) {
// Otherwise handleExited will call this.
this.handleClose();
} else if (!prevProps.open && this.props.open) {
this.handleOpen();
}
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
this.mounted = false;
if (this.props.open || getHasTransition(this.props) && !this.state.exited) {
this.handleClose();
}
}
}, {
key: "autoFocus",
value: function autoFocus() {
if (this.props.disableAutoFocus) {
return;
}
var currentActiveElement = (0, _ownerDocument.default)(this.mountNode).activeElement;
if (this.dialogRef && !this.dialogRef.contains(currentActiveElement)) {
this.lastFocus = currentActiveElement;
if (!this.dialogRef.hasAttribute('tabIndex')) {
process.env.NODE_ENV !== "production" ? (0, _warning.default)(false, ['Material-UI: the modal content node does not accept focus.', 'For the benefit of assistive technologies, ' + 'the tabIndex of the node is being set to "-1".'].join('\n')) : void 0;
this.dialogRef.setAttribute('tabIndex', -1);
}
this.dialogRef.focus();
}
}
}, {
key: "restoreLastFocus",
value: function restoreLastFocus() {
if (this.props.disableRestoreFocus) {
return;
}
if (this.lastFocus) {
// Not all elements in IE11 have a focus method.
// Because IE11 market share is low, we accept the restore focus being broken
// and we silent the issue.
if (this.lastFocus.focus) {
this.lastFocus.focus();
}
this.lastFocus = null;
}
}
}, {
key: "isTopModal",
value: function isTopModal() {
return this.props.manager.isTopModal(this);
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var _this$props = this.props,
BackdropComponent = _this$props.BackdropComponent,
BackdropProps = _this$props.BackdropProps,
children = _this$props.children,
classes = _this$props.classes,
className = _this$props.className,
container = _this$props.container,
disableAutoFocus = _this$props.disableAutoFocus,
disableBackdropClick = _this$props.disableBackdropClick,
disableEnforceFocus = _this$props.disableEnforceFocus,
disableEscapeKeyDown = _this$props.disableEscapeKeyDown,
disablePortal = _this$props.disablePortal,
disableRestoreFocus = _this$props.disableRestoreFocus,
hideBackdrop = _this$props.hideBackdrop,
keepMounted = _this$props.keepMounted,
manager = _this$props.manager,
onBackdropClick = _this$props.onBackdropClick,
onClose = _this$props.onClose,
onEscapeKeyDown = _this$props.onEscapeKeyDown,
onRendered = _this$props.onRendered,
open = _this$props.open,
other = (0, _objectWithoutProperties2.default)(_this$props, ["BackdropComponent", "BackdropProps", "children", "classes", "className", "container", "disableAutoFocus", "disableBackdropClick", "disableEnforceFocus", "disableEscapeKeyDown", "disablePortal", "disableRestoreFocus", "hideBackdrop", "keepMounted", "manager", "onBackdropClick", "onClose", "onEscapeKeyDown", "onRendered", "open"]);
var exited = this.state.exited;
var hasTransition = getHasTransition(this.props);
var childProps = {};
if (!keepMounted && !open && (!hasTransition || exited)) {
return null;
} // It's a Transition like component
if (hasTransition) {
childProps.onExited = (0, _helpers.createChainedFunction)(this.handleExited, children.props.onExited);
}
if (children.props.role === undefined) {
childProps.role = children.props.role || 'document';
}
if (children.props.tabIndex === undefined) {
childProps.tabIndex = children.props.tabIndex || '-1';
}
return _react.default.createElement(_Portal.default, {
ref: function ref(_ref2) {
_this2.mountNode = _ref2 ? _ref2.getMountNode() : _ref2;
},
container: container,
disablePortal: disablePortal,
onRendered: this.handleRendered
}, _react.default.createElement("div", (0, _extends2.default)({
ref: function ref(_ref) {
_this2.modalRef = _ref;
},
className: (0, _classnames.default)(classes.root, className, (0, _defineProperty2.default)({}, classes.hidden, exited))
}, other), hideBackdrop ? null : _react.default.createElement(BackdropComponent, (0, _extends2.default)({
open: open,
onClick: this.handleBackdropClick
}, BackdropProps)), _react.default.createElement(_RootRef.default, {
rootRef: function rootRef(ref) {
_this2.dialogRef = ref;
}
}, _react.default.cloneElement(children, childProps))));
}
}], [{
key: "getDerivedStateFromProps",
value: function getDerivedStateFromProps(nextProps) {
if (nextProps.open) {
return {
exited: false
};
}
if (!getHasTransition(nextProps)) {
// Otherwise let handleExited take care of marking exited.
return {
exited: true
};
}
return null;
}
}]);
return Modal;
}(_react.default.Component);
Modal.propTypes = process.env.NODE_ENV !== "production" ? {
/**
* A backdrop component. This property enables custom backdrop rendering.
*/
BackdropComponent: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.func, _propTypes.default.object]),
/**
* Properties applied to the [`Backdrop`](/api/backdrop/) element.
*/
BackdropProps: _propTypes.default.object,
/**
* A single child content element.
*/
children: _propTypes.default.element,
/**
* Override or extend the styles applied to the component.
* See [CSS API](#css-api) below for more details.
*/
classes: _propTypes.default.object.isRequired,
/**
* @ignore
*/
className: _propTypes.default.string,
/**
* A node, component instance, or function that returns either.
* The `container` will have the portal children appended to it.
*/
container: _propTypes.default.oneOfType([_propTypes.default.object, _propTypes.default.func]),
/**
* If `true`, the modal will not automatically shift focus to itself when it opens, and
* replace it to the last focused element when it closes.
* This also works correctly with any modal children that have the `disableAutoFocus` prop.
*
* Generally this should never be set to `true` as it makes the modal less
* accessible to assistive technologies, like screen readers.
*/
disableAutoFocus: _propTypes.default.bool,
/**
* If `true`, clicking the backdrop will not fire any callback.
*/
disableBackdropClick: _propTypes.default.bool,
/**
* If `true`, the modal will not prevent focus from leaving the modal while open.
*
* Generally this should never be set to `true` as it makes the modal less
* accessible to assistive technologies, like screen readers.
*/
disableEnforceFocus: _propTypes.default.bool,
/**
* If `true`, hitting escape will not fire any callback.
*/
disableEscapeKeyDown: _propTypes.default.bool,
/**
* Disable the portal behavior.
* The children stay within it's parent DOM hierarchy.
*/
disablePortal: _propTypes.default.bool,
/**
* If `true`, the modal will not restore focus to previously focused element once
* modal is hidden.
*/
disableRestoreFocus: _propTypes.default.bool,
/**
* If `true`, the backdrop is not rendered.
*/
hideBackdrop: _propTypes.default.bool,
/**
* Always keep the children in the DOM.
* This property can be useful in SEO situation or
* when you want to maximize the responsiveness of the Modal.
*/
keepMounted: _propTypes.default.bool,
/**
* A modal manager used to track and manage the state of open
* Modals. This enables customizing how modals interact within a container.
*/
manager: _propTypes.default.object,
/**
* Callback fired when the backdrop is clicked.
*/
onBackdropClick: _propTypes.default.func,
/**
* Callback fired when the component requests to be closed.
* The `reason` parameter can optionally be used to control the response to `onClose`.
*
* @param {object} event The event source of the callback
* @param {string} reason Can be:`"escapeKeyDown"`, `"backdropClick"`
*/
onClose: _propTypes.default.func,
/**
* Callback fired when the escape key is pressed,
* `disableEscapeKeyDown` is false and the modal is in focus.
*/
onEscapeKeyDown: _propTypes.default.func,
/**
* Callback fired once the children has been mounted into the `container`.
* It signals that the `open={true}` property took effect.
*/
onRendered: _propTypes.default.func,
/**
* If `true`, the modal is open.
*/
open: _propTypes.default.bool.isRequired
} : {};
Modal.defaultProps = {
disableAutoFocus: false,
disableBackdropClick: false,
disableEnforceFocus: false,
disableEscapeKeyDown: false,
disablePortal: false,
disableRestoreFocus: false,
hideBackdrop: false,
keepMounted: false,
// Modals don't open on the server so this won't conflict with concurrent requests.
manager: new _ModalManager.default(),
BackdropComponent: _Backdrop.default
};
var _default = (0, _withStyles.default)(styles, {
flip: false,
name: 'MuiModal'
})(Modal);
exports.default = _default;