@wordpress/components
Version:
UI components for WordPress.
177 lines (149 loc) • 4.43 kB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
import { createElement } from "@wordpress/element";
/**
* WordPress dependencies
*/
import { Component, createPortal } from '@wordpress/element';
import { withInstanceId } from '@wordpress/compose';
import deprecated from '@wordpress/deprecated';
/**
* Internal dependencies
*/
import ModalFrame from './frame';
import ModalHeader from './header';
import * as ariaHelper from './aria-helper'; // Used to count the number of open modals.
let parentElement,
openModalCount = 0;
class Modal extends Component {
constructor(props) {
super(props);
this.prepareDOM();
}
/**
* Appends the modal's node to the DOM, so the portal can render the
* modal in it. Also calls the openFirstModal when this is the first modal to be
* opened.
*/
componentDidMount() {
openModalCount++;
if (openModalCount === 1) {
this.openFirstModal();
}
}
/**
* Removes the modal's node from the DOM. Also calls closeLastModal when this is
* the last modal to be closed.
*/
componentWillUnmount() {
openModalCount--;
if (openModalCount === 0) {
this.closeLastModal();
}
this.cleanDOM();
}
/**
* Prepares the DOM for the modals to be rendered.
*
* Every modal is mounted in a separate div appended to a parent div
* that is appended to the document body.
*
* The parent div will be created if it does not yet exist, and the
* separate div for this specific modal will be appended to that.
*/
prepareDOM() {
if (!parentElement) {
parentElement = document.createElement('div');
document.body.appendChild(parentElement);
}
this.node = document.createElement('div');
parentElement.appendChild(this.node);
}
/**
* Removes the specific mounting point for this modal from the DOM.
*/
cleanDOM() {
parentElement.removeChild(this.node);
}
/**
* Prepares the DOM for this modal and any additional modal to be mounted.
*
* It appends an additional div to the body for the modals to be rendered in,
* it hides any other elements from screen-readers and adds an additional class
* to the body to prevent scrolling while the modal is open.
*/
openFirstModal() {
ariaHelper.hideApp(parentElement);
document.body.classList.add(this.props.bodyOpenClassName);
}
/**
* Cleans up the DOM after the last modal is closed and makes the app available
* for screen-readers again.
*/
closeLastModal() {
document.body.classList.remove(this.props.bodyOpenClassName);
ariaHelper.showApp();
}
/**
* Renders the modal.
*
* @return {WPElement} The modal element.
*/
render() {
const {
onRequestClose,
title,
icon,
closeButtonLabel,
children,
aria,
instanceId,
isDismissible,
isDismissable,
//Deprecated
// Many of the documented props for Modal are passed straight through
// to the ModalFrame component and handled there.
...otherProps
} = this.props;
const headingId = title ? `components-modal-header-${instanceId}` : aria.labelledby;
if (isDismissable) {
deprecated('isDismissable prop of the Modal component', {
since: '5.4',
alternative: 'isDismissible prop (renamed) of the Modal component'
});
} // Disable reason: this stops mouse events from triggering tooltips and
// other elements underneath the modal overlay.
return createPortal(createElement(ModalFrame, _extends({
onRequestClose: onRequestClose,
aria: {
labelledby: headingId,
describedby: aria.describedby
}
}, otherProps), createElement("div", {
className: 'components-modal__content',
role: "document"
}, createElement(ModalHeader, {
closeLabel: closeButtonLabel,
headingId: title && headingId,
icon: icon,
isDismissible: isDismissible || isDismissable,
onClose: onRequestClose,
title: title
}), children)), this.node);
}
}
Modal.defaultProps = {
bodyOpenClassName: 'modal-open',
role: 'dialog',
title: null,
focusOnMount: true,
shouldCloseOnEsc: true,
shouldCloseOnClickOutside: true,
isDismissible: true,
/* accessibility */
aria: {
labelledby: null,
describedby: null
}
};
export default withInstanceId(Modal);
//# sourceMappingURL=index.js.map