UNPKG

cpui-components

Version:

344 lines (284 loc) 13.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Modal = undefined; 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 _react2 = _interopRequireDefault(_react); var _propTypes = require("prop-types"); var _propTypes2 = _interopRequireDefault(_propTypes); var _classnames = require("classnames"); var _classnames2 = _interopRequireDefault(_classnames); var _blacklist = require("blacklist"); var _blacklist2 = _interopRequireDefault(_blacklist); var _reactDom = require("react-dom"); var _reactDom2 = _interopRequireDefault(_reactDom); var _Transition = require("react-transition-group/Transition"); var _Transition2 = _interopRequireDefault(_Transition); var _constants = require("./../../constants"); var _ModalBody = require("./../ModalBody"); var _ModalBody2 = _interopRequireDefault(_ModalBody); var _ModalFooter = require("./../ModalFooter"); var _ModalFooter2 = _interopRequireDefault(_ModalFooter); var _ModalHeader = require("./../ModalHeader"); var _ModalHeader2 = _interopRequireDefault(_ModalHeader); var _ally = require("ally.js"); var _ally2 = _interopRequireDefault(_ally); 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; } var TransitionPortal = function (_Component) { _inherits(TransitionPortal, _Component); function TransitionPortal() { var _ref; var _temp, _this, _ret; _classCallCheck(this, TransitionPortal); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = TransitionPortal.__proto__ || Object.getPrototypeOf(TransitionPortal)).call.apply(_ref, [this].concat(args))), _this), _this.componentDidMount = function () { if (!_constants.canUseDOM) { return; } var p = document.createElement("div"); if (_this.props.className != null) { p.className = _this.props.className; } document.body.appendChild(p); _this.portalElement = p; _this.componentDidUpdate(); }, _this.componentDidUpdate = function () { if (!_constants.canUseDOM) { return; } if (_this.props.className != null) { _this.portalElement.className = _this.props.className; } _reactDom2.default.render(_react2.default.createElement( _Transition2.default, _this.props, _this.props.children ), _this.portalElement); }, _this.componentWillUnmount = function () { if (!_constants.canUseDOM) { return; } document.body.removeChild(_this.portalElement); }, _this.portalElement = null, _this.render = function () { return null; }, _temp), _possibleConstructorReturn(_this, _ret); } return TransitionPortal; }(_react.Component); var Modal = exports.Modal = function (_Component2) { _inherits(Modal, _Component2); function Modal() { var _ref2; var _temp2, _this2, _ret2; _classCallCheck(this, Modal); for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } return _ret2 = (_temp2 = (_this2 = _possibleConstructorReturn(this, (_ref2 = Modal.__proto__ || Object.getPrototypeOf(Modal)).call.apply(_ref2, [this].concat(args))), _this2), _this2.componentWillReceiveProps = function (nextProps) { if (!_constants.canUseDOM) { return; } var target = document.body; var scrollbarWidth = window.innerWidth - document.body.clientWidth; // 1. if (!_this2.props.in && nextProps.in) { setTimeout(function () { return _this2.handleAccessibility(); }); target.style.overflow = "hidden"; target.style.paddingRight = scrollbarWidth + "px"; } else if (_this2.props.in && !nextProps.in) { setTimeout(function () { return _this2.removeAccessibilityHandlers(); }); target.style.overflow = ""; target.style.paddingRight = ""; } }, _this2.handleClose = function () { var _this2$props = _this2.props, backdropClosesModal = _this2$props.backdropClosesModal, onCancel = _this2$props.onCancel; if (backdropClosesModal) { onCancel(); } }, _this2.handleDialogClick = function (event) { event.stopPropagation(); }, _this2.renderDialog = function (transitionState) { var _this2$props2 = _this2.props, children = _this2$props2.children, width = _this2$props2.width, transitionTimeout = _this2$props2.transitionTimeout; var thisStateTimeout = transitionTimeout; if ((typeof transitionTimeout === "undefined" ? "undefined" : _typeof(transitionTimeout)) === "object" && transitionTimeout !== null) { //transitionTimeout is object switch (transitionState) { case "entering": case "entered": if (transitionTimeout.enter != null) { thisStateTimeout = transitionTimeout.enter; } break; case "exiting": case "exited": if (transitionTimeout.exit != null) { thisStateTimeout = transitionTimeout.exit; } break; default: break; } } var defaultStyle = { transition: "opacity " + thisStateTimeout + "ms ease-in-out", opacity: 0 }; var transitionStyles = { entering: { opacity: 1 }, entered: { opacity: 1 } }; var style = width && !isNaN(width) ? { width: width + 20 } : null; var dialogClassname = (0, _classnames2.default)("cp-PopOver", "cp-PopOver--modal", "Modal-fade-" + transitionState, width && isNaN(width) ? "cp-PopOver--" + width : null); return _react2.default.createElement( "div", { className: dialogClassname, style: _extends({}, defaultStyle, transitionStyles[transitionState], { style: style }), onClick: _this2.handleDialogClick, ref: function ref(element) { _this2.modalElement = element; } }, _react2.default.createElement( "div", { className: "Modal-content" }, children ) ); }, _this2.renderBackdrop = function () { var isOpen = _this2.props.in; if (!isOpen) { return null; } return _react2.default.createElement("div", { className: "cp-PopOver-backdrop" }); }, _this2.render = function () { // cp-AdminWrap class may cause some issues if this component is ever used outside the admin context within CivicEngage. var className = (0, _classnames2.default)("cp-PopOverWrapper", "cp-AdminWrap", { "is-open": _this2.props.in }, _this2.props.className); var props = (0, _blacklist2.default)(_this2.props, "backdropClosesModal", "className", "onCancel", "transitionTimeout"); return _react2.default.createElement( "div", null, _react2.default.createElement( TransitionPortal, _extends({}, props, { className: className, "data-modal": "true", onClick: _this2.handleClose, timeout: _this2.props.transitionTimeout }), function (state) { return _this2.renderDialog(state); } ), _react2.default.createElement( TransitionPortal, { timeout: _this2.props.transitionTimeout }, function (state) { return _this2.renderBackdrop(state); } ) ); }, _temp2), _possibleConstructorReturn(_this2, _ret2); } _createClass(Modal, [{ key: "handleAccessibility", value: function handleAccessibility() { // Remember the element that was focused before we opened the modal // so we can return focus to it once we close the modal. this.focusedElementBeforeModalOpened = document.activeElement; // We're using a transition to reveal the modal, // so wait until the element is visible, before // finding the first keyboard focusable element // and passing focus to it, otherwise the browser // might scroll the document to reveal the element // receiving focus if (this.props.autoFocusFirstElement) { _ally2.default.when.visibleArea({ context: this.modalElement, callback: function callback(context) { // the modal is visible on screen, so find the first // keyboard focusable element (giving any element with // autoFocus attribute precendence). If the modal does // not contain any keyboard focusabe elements, focus will // be given to the modal itself. var element = _ally2.default.query.firstTabbable({ context: context, defaultToContext: true }); element.focus(); } }); } // Make sure that no element outside of the modal // can be interacted with while the modal is visible. this.disabledHandle = _ally2.default.maintain.disabled({ filter: this.modalElement }); // Make sure that no element outside of the modal // is exposed via the Accessibility Tree, to prevent // screen readers from navigating to content it shouldn't // be seeing while the modal is open. this.hiddenHandle = _ally2.default.maintain.hidden({ filter: this.modalElement }); // React to escape keys as mandated by ARIA Practices this.keyHandle = _ally2.default.when.key({ escape: this.handleClose }); } }, { key: "removeAccessibilityHandlers", value: function removeAccessibilityHandlers() { // undo listening to keyboard this.keyHandle && this.keyHandle.disengage(); // undo hiding elements outside of the modal this.hiddenHandle && this.hiddenHandle.disengage(); // undo disabling elements outside of the modal this.disabledHandle && this.disabledHandle.disengage(); // return focus to where it was before we opened the modal this.focusedElementBeforeModalOpened && this.focusedElementBeforeModalOpened.focus(); } }, { key: "handleModalClick", value: function handleModalClick(event) { if (event.target.dataset.modal) this.handleClose(); } }]); return Modal; }(_react.Component); Modal.propTypes = { autoFocusFirstElement: _propTypes2.default.bool, backdropClosesModal: _propTypes2.default.bool, className: _propTypes2.default.string, in: _propTypes2.default.bool, onCancel: _propTypes2.default.func, width: _propTypes2.default.oneOfType([_propTypes2.default.oneOf(["small", "medium", "large"]), _propTypes2.default.number]), transitionTimeout: _propTypes2.default.oneOfType([_propTypes2.default.shape({ enter: _propTypes2.default.number, exit: _propTypes2.default.number }), _propTypes2.default.number]) }; Modal.defaultProps = { width: "medium", transitionTimeout: 333 }; Modal.Body = _ModalBody2.default; Modal.Footer = _ModalFooter2.default; Modal.Header = _ModalHeader2.default; exports.default = Modal;