UNPKG

@wordpress/components

Version:
177 lines (149 loc) 4.43 kB
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