|
- "use strict";
-
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
-
- exports.__esModule = true;
- exports["default"] = void 0;
-
- var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
-
- var _objectWithoutPropertiesLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutPropertiesLoose"));
-
- var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
-
- var _inheritsLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/inheritsLoose"));
-
- var _activeElement = _interopRequireDefault(require("dom-helpers/activeElement"));
-
- var _contains = _interopRequireDefault(require("dom-helpers/contains"));
-
- var _canUseDOM = _interopRequireDefault(require("dom-helpers/canUseDOM"));
-
- var _listen = _interopRequireDefault(require("dom-helpers/listen"));
-
- var _propTypes = _interopRequireDefault(require("prop-types"));
-
- var _react = _interopRequireDefault(require("react"));
-
- var _reactDom = _interopRequireDefault(require("react-dom"));
-
- var _ModalManager = _interopRequireDefault(require("./ModalManager"));
-
- var _ownerDocument = _interopRequireDefault(require("./utils/ownerDocument"));
-
- var _useWaitForDOMRef = _interopRequireDefault(require("./utils/useWaitForDOMRef"));
-
- /* eslint-disable react/prop-types */
- function omitProps(props, propTypes) {
- var keys = Object.keys(props);
- var newProps = {};
- keys.forEach(function (prop) {
- if (!Object.prototype.hasOwnProperty.call(propTypes, prop)) {
- newProps[prop] = props[prop];
- }
- });
- return newProps;
- }
-
- var manager;
- /**
- * Love them or hate them, `<Modal />` provides a solid foundation for creating dialogs, lightboxes, or whatever else.
- * The Modal component renders its `children` node in front of a backdrop component.
- *
- * The Modal offers a few helpful features over using just a `<Portal/>` component and some styles:
- *
- * - Manages dialog stacking when one-at-a-time just isn't enough.
- * - Creates a backdrop, for disabling interaction below the modal.
- * - It properly manages focus; moving to the modal content, and keeping it there until the modal is closed.
- * - It disables scrolling of the page content while open.
- * - Adds the appropriate ARIA roles are automatically.
- * - Easily pluggable animations via a `<Transition/>` component.
- *
- * Note that, in the same way the backdrop element prevents users from clicking or interacting
- * with the page content underneath the Modal, Screen readers also need to be signaled to not to
- * interact with page content while the Modal is open. To do this, we use a common technique of applying
- * the `aria-hidden='true'` attribute to the non-Modal elements in the Modal `container`. This means that for
- * a Modal to be truly modal, it should have a `container` that is _outside_ your app's
- * React hierarchy (such as the default: document.body).
- */
-
- var Modal =
- /*#__PURE__*/
- function (_React$Component) {
- (0, _inheritsLoose2["default"])(Modal, _React$Component);
-
- function Modal() {
- var _this;
-
- for (var _len = arguments.length, _args = new Array(_len), _key = 0; _key < _len; _key++) {
- _args[_key] = arguments[_key];
- }
-
- _this = _React$Component.call.apply(_React$Component, [this].concat(_args)) || this;
- _this.state = {
- exited: !_this.props.show
- };
-
- _this.onShow = function () {
- var _this$props = _this.props,
- container = _this$props.container,
- containerClassName = _this$props.containerClassName,
- onShow = _this$props.onShow;
-
- _this.getModalManager().add((0, _assertThisInitialized2["default"])(_this), container, containerClassName);
-
- _this.removeKeydownListener = (0, _listen["default"])(document, 'keydown', _this.handleDocumentKeyDown);
- _this.removeFocusListener = (0, _listen["default"])(document, 'focus', // the timeout is necessary b/c this will run before the new modal is mounted
- // and so steals focus from it
- function () {
- return setTimeout(_this.enforceFocus);
- }, true);
-
- if (onShow) {
- onShow();
- } // autofocus after onShow, to not trigger a focus event for previous
- // modals before this one is shown.
-
-
- _this.autoFocus();
- };
-
- _this.onHide = function () {
- _this.getModalManager().remove((0, _assertThisInitialized2["default"])(_this));
-
- _this.removeKeydownListener();
-
- _this.removeFocusListener();
-
- if (_this.props.restoreFocus) {
- _this.restoreLastFocus();
- }
- };
-
- _this.setDialogRef = function (ref) {
- _this.dialog = ref;
- };
-
- _this.setBackdropRef = function (ref) {
- _this.backdrop = ref && _reactDom["default"].findDOMNode(ref);
- };
-
- _this.handleHidden = function () {
- _this.setState({
- exited: true
- });
-
- _this.onHide();
-
- if (_this.props.onExited) {
- var _this$props2;
-
- (_this$props2 = _this.props).onExited.apply(_this$props2, arguments);
- }
- };
-
- _this.handleBackdropClick = function (e) {
- if (e.target !== e.currentTarget) {
- return;
- }
-
- if (_this.props.onBackdropClick) {
- _this.props.onBackdropClick(e);
- }
-
- if (_this.props.backdrop === true) {
- _this.props.onHide();
- }
- };
-
- _this.handleDocumentKeyDown = function (e) {
- if (_this.props.keyboard && e.keyCode === 27 && _this.isTopModal()) {
- if (_this.props.onEscapeKeyDown) {
- _this.props.onEscapeKeyDown(e);
- }
-
- _this.props.onHide();
- }
- };
-
- _this.enforceFocus = function () {
- if (!_this.props.enforceFocus || !_this._isMounted || !_this.isTopModal()) {
- return;
- }
-
- var currentActiveElement = (0, _activeElement["default"])((0, _ownerDocument["default"])((0, _assertThisInitialized2["default"])(_this)));
-
- if (_this.dialog && !(0, _contains["default"])(_this.dialog, currentActiveElement)) {
- _this.dialog.focus();
- }
- };
-
- _this.renderBackdrop = function () {
- var _this$props3 = _this.props,
- renderBackdrop = _this$props3.renderBackdrop,
- Transition = _this$props3.backdropTransition;
- var backdrop = renderBackdrop({
- ref: _this.setBackdropRef,
- onClick: _this.handleBackdropClick
- });
-
- if (Transition) {
- backdrop = _react["default"].createElement(Transition, {
- appear: true,
- "in": _this.props.show
- }, backdrop);
- }
-
- return backdrop;
- };
-
- return _this;
- }
-
- Modal.getDerivedStateFromProps = function getDerivedStateFromProps(nextProps) {
- if (nextProps.show) {
- return {
- exited: false
- };
- }
-
- if (!nextProps.transition) {
- // Otherwise let handleHidden take care of marking exited.
- return {
- exited: true
- };
- }
-
- return null;
- };
-
- var _proto = Modal.prototype;
-
- _proto.componentDidMount = function componentDidMount() {
- this._isMounted = true;
-
- if (this.props.show) {
- this.onShow();
- }
- };
-
- _proto.componentDidUpdate = function componentDidUpdate(prevProps) {
- var transition = this.props.transition;
-
- if (prevProps.show && !this.props.show && !transition) {
- // Otherwise handleHidden will call this.
- this.onHide();
- } else if (!prevProps.show && this.props.show) {
- this.onShow();
- }
- };
-
- _proto.componentWillUnmount = function componentWillUnmount() {
- var _this$props4 = this.props,
- show = _this$props4.show,
- transition = _this$props4.transition;
- this._isMounted = false;
-
- if (show || transition && !this.state.exited) {
- this.onHide();
- }
- };
-
- _proto.getSnapshotBeforeUpdate = function getSnapshotBeforeUpdate(prevProps) {
- if (_canUseDOM["default"] && !prevProps.show && this.props.show) {
- this.lastFocus = (0, _activeElement["default"])();
- }
-
- return null;
- };
-
- _proto.getModalManager = function getModalManager() {
- if (this.props.manager) {
- return this.props.manager;
- }
-
- if (!manager) {
- manager = new _ModalManager["default"]();
- }
-
- return manager;
- };
-
- _proto.restoreLastFocus = function restoreLastFocus() {
- // Support: <=IE11 doesn't support `focus()` on svg elements (RB: #917)
- if (this.lastFocus && this.lastFocus.focus) {
- this.lastFocus.focus(this.props.restoreFocusOptions);
- this.lastFocus = null;
- }
- };
-
- _proto.autoFocus = function autoFocus() {
- if (!this.props.autoFocus) return;
- var currentActiveElement = (0, _activeElement["default"])((0, _ownerDocument["default"])(this));
-
- if (this.dialog && !(0, _contains["default"])(this.dialog, currentActiveElement)) {
- this.lastFocus = currentActiveElement;
- this.dialog.focus();
- }
- };
-
- _proto.isTopModal = function isTopModal() {
- return this.getModalManager().isTopModal(this);
- };
-
- _proto.render = function render() {
- var _this$props5 = this.props,
- show = _this$props5.show,
- container = _this$props5.container,
- children = _this$props5.children,
- renderDialog = _this$props5.renderDialog,
- _this$props5$role = _this$props5.role,
- role = _this$props5$role === void 0 ? 'dialog' : _this$props5$role,
- Transition = _this$props5.transition,
- backdrop = _this$props5.backdrop,
- className = _this$props5.className,
- style = _this$props5.style,
- onExit = _this$props5.onExit,
- onExiting = _this$props5.onExiting,
- onEnter = _this$props5.onEnter,
- onEntering = _this$props5.onEntering,
- onEntered = _this$props5.onEntered,
- props = (0, _objectWithoutPropertiesLoose2["default"])(_this$props5, ["show", "container", "children", "renderDialog", "role", "transition", "backdrop", "className", "style", "onExit", "onExiting", "onEnter", "onEntering", "onEntered"]);
-
- if (!(show || Transition && !this.state.exited)) {
- return null;
- }
-
- var dialogProps = (0, _extends2["default"])({
- role: role,
- ref: this.setDialogRef,
- // apparently only works on the dialog role element
- 'aria-modal': role === 'dialog' ? true : undefined
- }, omitProps(props, Modal.propTypes), {
- style: style,
- className: className,
- tabIndex: '-1'
- });
- var dialog = renderDialog ? renderDialog(dialogProps) : _react["default"].createElement("div", dialogProps, _react["default"].cloneElement(children, {
- role: 'document'
- }));
-
- if (Transition) {
- dialog = _react["default"].createElement(Transition, {
- appear: true,
- unmountOnExit: true,
- "in": show,
- onExit: onExit,
- onExiting: onExiting,
- onExited: this.handleHidden,
- onEnter: onEnter,
- onEntering: onEntering,
- onEntered: onEntered
- }, dialog);
- }
-
- return _reactDom["default"].createPortal(_react["default"].createElement(_react["default"].Fragment, null, backdrop && this.renderBackdrop(), dialog), container);
- };
-
- return Modal;
- }(_react["default"].Component); // dumb HOC for the sake react-docgen
-
-
- Modal.propTypes = {
- /**
- * Set the visibility of the Modal
- */
- show: _propTypes["default"].bool,
-
- /**
- * A DOM element, a `ref` to an element, or function that returns either. The Modal is appended to it's `container` element.
- *
- * For the sake of assistive technologies, the container should usually be the document body, so that the rest of the
- * page content can be placed behind a virtual backdrop as well as a visual one.
- */
- container: _propTypes["default"].any,
-
- /**
- * A callback fired when the Modal is opening.
- */
- onShow: _propTypes["default"].func,
-
- /**
- * A callback fired when either the backdrop is clicked, or the escape key is pressed.
- *
- * The `onHide` callback only signals intent from the Modal,
- * you must actually set the `show` prop to `false` for the Modal to close.
- */
- onHide: _propTypes["default"].func,
-
- /**
- * Include a backdrop component.
- */
- backdrop: _propTypes["default"].oneOfType([_propTypes["default"].bool, _propTypes["default"].oneOf(['static'])]),
-
- /**
- * A function that returns the dialog component. Useful for custom
- * rendering. **Note:** the component should make sure to apply the provided ref.
- *
- * ```js
- * renderDialog={props => <MyDialog {...props} />}
- * ```
- */
- renderDialog: _propTypes["default"].func,
-
- /**
- * A function that returns a backdrop component. Useful for custom
- * backdrop rendering.
- *
- * ```js
- * renderBackdrop={props => <MyBackdrop {...props} />}
- * ```
- */
- renderBackdrop: _propTypes["default"].func,
-
- /**
- * A callback fired when the escape key, if specified in `keyboard`, is pressed.
- */
- onEscapeKeyDown: _propTypes["default"].func,
-
- /**
- * A callback fired when the backdrop, if specified, is clicked.
- */
- onBackdropClick: _propTypes["default"].func,
-
- /**
- * A css class or set of classes applied to the modal container when the modal is open,
- * and removed when it is closed.
- */
- containerClassName: _propTypes["default"].string,
-
- /**
- * Close the modal when escape key is pressed
- */
- keyboard: _propTypes["default"].bool,
-
- /**
- * A `react-transition-group@2.0.0` `<Transition/>` component used
- * to control animations for the dialog component.
- */
- transition: _propTypes["default"].elementType,
-
- /**
- * A `react-transition-group@2.0.0` `<Transition/>` component used
- * to control animations for the backdrop components.
- */
- backdropTransition: _propTypes["default"].elementType,
-
- /**
- * When `true` The modal will 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 `autoFocus` prop.
- *
- * Generally this should never be set to `false` as it makes the Modal less
- * accessible to assistive technologies, like screen readers.
- */
- autoFocus: _propTypes["default"].bool,
-
- /**
- * When `true` The modal will prevent focus from leaving the Modal while open.
- *
- * Generally this should never be set to `false` as it makes the Modal less
- * accessible to assistive technologies, like screen readers.
- */
- enforceFocus: _propTypes["default"].bool,
-
- /**
- * When `true` The modal will restore focus to previously focused element once
- * modal is hidden
- */
- restoreFocus: _propTypes["default"].bool,
-
- /**
- * Options passed to focus function when `restoreFocus` is set to `true`
- *
- * @link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#Parameters
- */
- restoreFocusOptions: _propTypes["default"].shape({
- preventScroll: _propTypes["default"].bool
- }),
-
- /**
- * Callback fired before the Modal transitions in
- */
- onEnter: _propTypes["default"].func,
-
- /**
- * Callback fired as the Modal begins to transition in
- */
- onEntering: _propTypes["default"].func,
-
- /**
- * Callback fired after the Modal finishes transitioning in
- */
- onEntered: _propTypes["default"].func,
-
- /**
- * Callback fired right before the Modal transitions out
- */
- onExit: _propTypes["default"].func,
-
- /**
- * Callback fired as the Modal begins to transition out
- */
- onExiting: _propTypes["default"].func,
-
- /**
- * Callback fired after the Modal finishes transitioning out
- */
- onExited: _propTypes["default"].func,
-
- /**
- * A ModalManager instance used to track and manage the state of open
- * Modals. Useful when customizing how modals interact within a container
- */
- manager: _propTypes["default"].object
- };
- Modal.defaultProps = {
- show: false,
- role: 'dialog',
- backdrop: true,
- keyboard: true,
- autoFocus: true,
- enforceFocus: true,
- restoreFocus: true,
- onHide: function onHide() {},
- renderBackdrop: function renderBackdrop(props) {
- return _react["default"].createElement("div", props);
- }
- };
-
- function forwardRef(Component) {
- // eslint-disable-next-line react/display-name
- var ModalWithContainer = _react["default"].forwardRef(function (props, ref) {
- var resolved = (0, _useWaitForDOMRef["default"])(props.container);
- return resolved ? _react["default"].createElement(Component, (0, _extends2["default"])({}, props, {
- ref: ref,
- container: resolved
- })) : null;
- });
-
- ModalWithContainer.Manager = _ModalManager["default"];
- ModalWithContainer._Inner = Component;
- return ModalWithContainer;
- }
-
- var ModalWithContainer = forwardRef(Modal);
- ModalWithContainer.Manager = _ModalManager["default"];
- var _default = ModalWithContainer;
- exports["default"] = _default;
- module.exports = exports.default;
|