react-modal-construction-kit
Version:
A flexible Modal component kit for React
365 lines (311 loc) • 11.1 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getTransitionStyles = 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 _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 _Portal = require('../Portal/Portal');
var _Portal2 = _interopRequireDefault(_Portal);
var _reactTransitionGroup = require('react-transition-group');
var _utils = require('../utils');
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 getTransitionStyles = exports.getTransitionStyles = function getTransitionStyles() {
return {
exited: {
display: 'none'
},
entering: {
opacity: 0.01,
transform: 'scale(1.05)'
},
entered: {
opacity: 1,
transform: 'none'
},
exiting: {
opacity: 0.01,
transform: 'scale(0.90)'
}
};
};
var getStyle = function getStyle(_ref) {
var zIndex = _ref.zIndex,
isCentered = _ref.isCentered,
transitionDuration = _ref.transitionDuration;
return {
component: {
overflowX: 'hidden',
overflowY: 'auto',
position: 'fixed',
top: 0,
right: 0,
bottom: 0,
left: 0,
zIndex: zIndex,
outline: 0,
transition: 'opacity ' + transitionDuration / 2 + 'ms linear, transform ' + transitionDuration + 'ms ease-out'
},
dialog: {
display: 'flex',
alignItems: 'center',
position: 'relative',
maxWidth: '500px',
margin: isCentered ? '0 auto' : '1.75rem auto',
minHeight: isCentered && '100%',
width: 'auto',
boxSizing: 'border-box',
pointerEvents: 'none'
},
content: {
position: 'relative',
WebkitBoxOrient: 'vertical',
WebkitBoxDirection: 'normal',
MsFlexDirection: 'column',
flexDirection: 'column',
width: '100%',
pointerEvents: 'auto',
backgroundColor: 'white',
backgroundClip: 'padding-box',
outline: 0,
display: 'flex',
boxSizing: 'border-box'
}
};
};
var Modal = function (_React$Component) {
_inherits(Modal, _React$Component);
function Modal(props) {
_classCallCheck(this, Modal);
var _this = _possibleConstructorReturn(this, (Modal.__proto__ || Object.getPrototypeOf(Modal)).call(this, props));
_this.onOpened = function (node, isAppearing) {
var _this$props = _this.props,
onOpened = _this$props.onOpened,
onEntered = _this$props.onEntered;
onOpened();
onEntered(node, isAppearing);
};
_this.onClosed = function (node) {
var _this$props2 = _this.props,
onClosed = _this$props2.onClosed,
onExited = _this$props2.onExited;
onClosed();
onExited(node);
_this.destroy();
if (_this._isMounted) {
_this.setState({ isOpen: false });
}
};
_this.handleEscape = function (e) {
var _this$props3 = _this.props,
isOpen = _this$props3.isOpen,
hasEscapeClose = _this$props3.hasEscapeClose,
onClosed = _this$props3.onClosed;
if (isOpen && hasEscapeClose && e.keyCode === 27 && onClosed) {
onClosed();
}
};
_this.handleClick = function (e) {
var hasClickedOutside = !_this.contentRef.contains(e.target);
if (hasClickedOutside) {
_this.props.onClosed();
}
};
_this.element = null;
_this.originalBodyPadding = null;
_this.state = {
isOpen: props.isOpen
};
if (props.isOpen) {
_this.init();
}
return _this;
}
_createClass(Modal, [{
key: 'componentDidMount',
value: function componentDidMount() {
if (this.props.onEnter) {
this.props.onEnter();
}
if (this.state.isOpen && this.props.autoFocus) {
this.setFocus();
}
window.addEventListener('keydown', this.handleEscape, true);
this._isMounted = true;
}
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps) {
if (nextProps.isOpen && !this.props.isOpen) {
this.setState({ isOpen: nextProps.isOpen });
}
}
}, {
key: 'componentWillUpdate',
value: function componentWillUpdate(nextProps, nextState) {
if (nextState.isOpen && !this.state.isOpen) {
this.init();
}
}
}, {
key: 'componentDidUpdate',
value: function componentDidUpdate(prevProps, prevState) {
if (this.props.autoFocus && this.state.isOpen && !prevState.isOpen) {
this.setFocus();
}
}
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
if (this.props.onExit) {
this.props.onExit();
}
if (this.state.isOpen) {
this.destroy();
}
window.removeEventListener('keydown', this.handleEscape, true);
this._isMounted = false;
}
}, {
key: 'setFocus',
value: function setFocus() {
if (this.dialogRef && this.dialogRef.parentNode && typeof this.dialogRef.parentNode.focus === 'function') {
this.dialogRef.parentNode.focus();
}
}
}, {
key: 'init',
value: function init() {
this.element = document.createElement('div');
this.element.setAttribute('tabindex', '-1');
this.element.style.position = 'relative';
this.element.style.zIndex = this.props.zIndex;
this.originalBodyPadding = (0, _utils.getOriginalBodyPadding)();
(0, _utils.conditionallyUpdateScrollbar)();
document.body.appendChild(this.element);
if (!this._bodyStyleAdded) {
document.body.style.overflow = 'hidden';
this._bodyStyleAdded = true;
}
}
}, {
key: 'destroy',
value: function destroy() {
if (this.element) {
document.body.removeChild(this.element);
this.element = null;
}
if (this._bodyStyleAdded) {
document.body.style.overflow = null;
this._bodyStyleAdded = false;
}
(0, _utils.setScrollbarWidth)(this.originalBodyPadding);
}
}, {
key: 'render',
value: function render() {
var _this2 = this;
if (!this.state.isOpen) {
return null;
}
var _props = this.props,
isOpen = _props.isOpen,
role = _props.role,
transitionDuration = _props.transitionDuration,
children = _props.children,
className = _props.className,
dialogClassName = _props.dialogClassName,
contentClassName = _props.contentClassName,
hasOutsideClickClose = _props.hasOutsideClickClose;
var style = getStyle(this.props);
return _react2.default.createElement(
_Portal2.default,
{ node: this.element },
_react2.default.createElement(
_reactTransitionGroup.Transition,
{
appear: true,
onEntered: this.onOpened,
onExited: this.onClosed,
timeout: transitionDuration,
'in': isOpen },
function (state) {
return _react2.default.createElement(
'div',
{
onClick: hasOutsideClickClose ? _this2.handleClick : null,
tabIndex: '-1',
role: role,
className: className,
style: _extends({}, style.component, getTransitionStyles()[state]) },
_react2.default.createElement(
'div',
{
className: dialogClassName,
style: style.dialog,
role: 'document',
ref: function ref(c) {
_this2.dialogRef = c;
} },
_react2.default.createElement(
'div',
{
className: contentClassName,
ref: function ref(c) {
_this2.contentRef = c;
},
style: style.content },
children
)
)
);
}
)
);
}
}]);
return Modal;
}(_react2.default.Component);
Modal.propTypes = {
isOpen: _propTypes2.default.bool,
autoFocus: _propTypes2.default.bool,
hasEscapeClose: _propTypes2.default.bool,
hasOutsideClickClose: _propTypes2.default.bool,
role: _propTypes2.default.string,
onEnter: _propTypes2.default.func,
onExit: _propTypes2.default.func,
onOpened: _propTypes2.default.func,
onClosed: _propTypes2.default.func,
zIndex: _propTypes2.default.number,
children: _propTypes2.default.node.isRequired,
onEntered: _propTypes2.default.func,
onExited: _propTypes2.default.func,
transitionDuration: _propTypes2.default.number,
className: _propTypes2.default.string,
dialogClassName: _propTypes2.default.string,
contentClassName: _propTypes2.default.string,
// eslint-disable-next-line react/no-unused-prop-types
isCentered: _propTypes2.default.bool
};
Modal.defaultProps = {
isOpen: false,
autoFocus: true,
role: 'dialog',
zIndex: 750,
onOpened: _utils.noop,
hasEscapeClose: true,
hasOutsideClickClose: true,
onClosed: _utils.noop,
transitionDuration: 300,
onEntered: _utils.noop,
onExited: _utils.noop,
isCentered: true
};
exports.default = Modal;