You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

378 lines
13 KiB

  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. exports.__esModule = true;
  4. exports.default = void 0;
  5. var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread"));
  6. var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
  7. var _inheritsLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/inheritsLoose"));
  8. var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
  9. var _react = _interopRequireDefault(require("react"));
  10. var _propTypes = _interopRequireDefault(require("prop-types"));
  11. var _classnames = _interopRequireDefault(require("classnames"));
  12. var _Portal = _interopRequireDefault(require("./Portal"));
  13. var _Fade = _interopRequireDefault(require("./Fade"));
  14. var _utils = require("./utils");
  15. function noop() {}
  16. var FadePropTypes = _propTypes.default.shape(_Fade.default.propTypes);
  17. var propTypes = {
  18. isOpen: _propTypes.default.bool,
  19. autoFocus: _propTypes.default.bool,
  20. centered: _propTypes.default.bool,
  21. size: _propTypes.default.string,
  22. toggle: _propTypes.default.func,
  23. keyboard: _propTypes.default.bool,
  24. role: _propTypes.default.string,
  25. labelledBy: _propTypes.default.string,
  26. backdrop: _propTypes.default.oneOfType([_propTypes.default.bool, _propTypes.default.oneOf(['static'])]),
  27. onEnter: _propTypes.default.func,
  28. onExit: _propTypes.default.func,
  29. onOpened: _propTypes.default.func,
  30. onClosed: _propTypes.default.func,
  31. children: _propTypes.default.node,
  32. className: _propTypes.default.string,
  33. wrapClassName: _propTypes.default.string,
  34. modalClassName: _propTypes.default.string,
  35. backdropClassName: _propTypes.default.string,
  36. contentClassName: _propTypes.default.string,
  37. external: _propTypes.default.node,
  38. fade: _propTypes.default.bool,
  39. cssModule: _propTypes.default.object,
  40. zIndex: _propTypes.default.oneOfType([_propTypes.default.number, _propTypes.default.string]),
  41. backdropTransition: FadePropTypes,
  42. modalTransition: FadePropTypes,
  43. innerRef: _propTypes.default.oneOfType([_propTypes.default.object, _propTypes.default.string, _propTypes.default.func])
  44. };
  45. var propsToOmit = Object.keys(propTypes);
  46. var defaultProps = {
  47. isOpen: false,
  48. autoFocus: true,
  49. centered: false,
  50. role: 'dialog',
  51. backdrop: true,
  52. keyboard: true,
  53. zIndex: 1050,
  54. fade: true,
  55. onOpened: noop,
  56. onClosed: noop,
  57. modalTransition: {
  58. timeout: _utils.TransitionTimeouts.Modal
  59. },
  60. backdropTransition: {
  61. mountOnEnter: true,
  62. timeout: _utils.TransitionTimeouts.Fade // uses standard fade transition
  63. }
  64. };
  65. var Modal =
  66. /*#__PURE__*/
  67. function (_React$Component) {
  68. (0, _inheritsLoose2.default)(Modal, _React$Component);
  69. function Modal(props) {
  70. var _this;
  71. _this = _React$Component.call(this, props) || this;
  72. _this._element = null;
  73. _this._originalBodyPadding = null;
  74. _this.getFocusableChildren = _this.getFocusableChildren.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)));
  75. _this.handleBackdropClick = _this.handleBackdropClick.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)));
  76. _this.handleBackdropMouseDown = _this.handleBackdropMouseDown.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)));
  77. _this.handleEscape = _this.handleEscape.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)));
  78. _this.handleTab = _this.handleTab.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)));
  79. _this.onOpened = _this.onOpened.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)));
  80. _this.onClosed = _this.onClosed.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this)));
  81. _this.state = {
  82. isOpen: props.isOpen
  83. };
  84. if (props.isOpen) {
  85. _this.init();
  86. }
  87. return _this;
  88. }
  89. var _proto = Modal.prototype;
  90. _proto.componentDidMount = function componentDidMount() {
  91. if (this.props.onEnter) {
  92. this.props.onEnter();
  93. }
  94. if (this.state.isOpen && this.props.autoFocus) {
  95. this.setFocus();
  96. }
  97. this._isMounted = true;
  98. };
  99. _proto.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
  100. if (nextProps.isOpen && !this.props.isOpen) {
  101. this.setState({
  102. isOpen: nextProps.isOpen
  103. });
  104. }
  105. };
  106. _proto.componentWillUpdate = function componentWillUpdate(nextProps, nextState) {
  107. if (nextState.isOpen && !this.state.isOpen) {
  108. this.init();
  109. }
  110. };
  111. _proto.componentDidUpdate = function componentDidUpdate(prevProps, prevState) {
  112. if (this.props.autoFocus && this.state.isOpen && !prevState.isOpen) {
  113. this.setFocus();
  114. }
  115. if (this._element && prevProps.zIndex !== this.props.zIndex) {
  116. this._element.style.zIndex = this.props.zIndex;
  117. }
  118. };
  119. _proto.componentWillUnmount = function componentWillUnmount() {
  120. if (this.props.onExit) {
  121. this.props.onExit();
  122. }
  123. if (this.state.isOpen) {
  124. this.destroy();
  125. }
  126. this._isMounted = false;
  127. };
  128. _proto.onOpened = function onOpened(node, isAppearing) {
  129. this.props.onOpened();
  130. (this.props.modalTransition.onEntered || noop)(node, isAppearing);
  131. };
  132. _proto.onClosed = function onClosed(node) {
  133. // so all methods get called before it is unmounted
  134. this.props.onClosed();
  135. (this.props.modalTransition.onExited || noop)(node);
  136. this.destroy();
  137. if (this._isMounted) {
  138. this.setState({
  139. isOpen: false
  140. });
  141. }
  142. };
  143. _proto.setFocus = function setFocus() {
  144. if (this._dialog && this._dialog.parentNode && typeof this._dialog.parentNode.focus === 'function') {
  145. this._dialog.parentNode.focus();
  146. }
  147. };
  148. _proto.getFocusableChildren = function getFocusableChildren() {
  149. return this._element.querySelectorAll(_utils.focusableElements.join(', '));
  150. };
  151. _proto.getFocusedChild = function getFocusedChild() {
  152. var currentFocus;
  153. var focusableChildren = this.getFocusableChildren();
  154. try {
  155. currentFocus = document.activeElement;
  156. } catch (err) {
  157. currentFocus = focusableChildren[0];
  158. }
  159. return currentFocus;
  160. } // not mouseUp because scrollbar fires it, shouldn't close when user scrolls
  161. ;
  162. _proto.handleBackdropClick = function handleBackdropClick(e) {
  163. if (e.target === this._mouseDownElement) {
  164. e.stopPropagation();
  165. if (!this.props.isOpen || this.props.backdrop !== true) return;
  166. var backdrop = this._dialog ? this._dialog.parentNode : null;
  167. if (backdrop && e.target === backdrop && this.props.toggle) {
  168. this.props.toggle(e);
  169. }
  170. }
  171. };
  172. _proto.handleTab = function handleTab(e) {
  173. if (e.which !== 9) return;
  174. var focusableChildren = this.getFocusableChildren();
  175. var totalFocusable = focusableChildren.length;
  176. var currentFocus = this.getFocusedChild();
  177. var focusedIndex = 0;
  178. for (var i = 0; i < totalFocusable; i += 1) {
  179. if (focusableChildren[i] === currentFocus) {
  180. focusedIndex = i;
  181. break;
  182. }
  183. }
  184. if (e.shiftKey && focusedIndex === 0) {
  185. e.preventDefault();
  186. focusableChildren[totalFocusable - 1].focus();
  187. } else if (!e.shiftKey && focusedIndex === totalFocusable - 1) {
  188. e.preventDefault();
  189. focusableChildren[0].focus();
  190. }
  191. };
  192. _proto.handleBackdropMouseDown = function handleBackdropMouseDown(e) {
  193. this._mouseDownElement = e.target;
  194. };
  195. _proto.handleEscape = function handleEscape(e) {
  196. if (this.props.isOpen && this.props.keyboard && e.keyCode === 27 && this.props.toggle) {
  197. e.preventDefault();
  198. e.stopPropagation();
  199. this.props.toggle(e);
  200. }
  201. };
  202. _proto.init = function init() {
  203. try {
  204. this._triggeringElement = document.activeElement;
  205. } catch (err) {
  206. this._triggeringElement = null;
  207. }
  208. this._element = document.createElement('div');
  209. this._element.setAttribute('tabindex', '-1');
  210. this._element.style.position = 'relative';
  211. this._element.style.zIndex = this.props.zIndex;
  212. this._originalBodyPadding = (0, _utils.getOriginalBodyPadding)();
  213. (0, _utils.conditionallyUpdateScrollbar)();
  214. document.body.appendChild(this._element);
  215. if (Modal.openCount === 0) {
  216. document.body.className = (0, _classnames.default)(document.body.className, (0, _utils.mapToCssModules)('modal-open', this.props.cssModule));
  217. }
  218. Modal.openCount += 1;
  219. };
  220. _proto.destroy = function destroy() {
  221. if (this._element) {
  222. document.body.removeChild(this._element);
  223. this._element = null;
  224. }
  225. if (this._triggeringElement) {
  226. if (this._triggeringElement.focus) this._triggeringElement.focus();
  227. this._triggeringElement = null;
  228. }
  229. if (Modal.openCount <= 1) {
  230. var modalOpenClassName = (0, _utils.mapToCssModules)('modal-open', this.props.cssModule); // Use regex to prevent matching `modal-open` as part of a different class, e.g. `my-modal-opened`
  231. var modalOpenClassNameRegex = new RegExp("(^| )" + modalOpenClassName + "( |$)");
  232. document.body.className = document.body.className.replace(modalOpenClassNameRegex, ' ').trim();
  233. }
  234. Modal.openCount -= 1;
  235. (0, _utils.setScrollbarWidth)(this._originalBodyPadding);
  236. };
  237. _proto.renderModalDialog = function renderModalDialog() {
  238. var _classNames,
  239. _this2 = this;
  240. var attributes = (0, _utils.omit)(this.props, propsToOmit);
  241. var dialogBaseClass = 'modal-dialog';
  242. return _react.default.createElement("div", (0, _extends2.default)({}, attributes, {
  243. className: (0, _utils.mapToCssModules)((0, _classnames.default)(dialogBaseClass, this.props.className, (_classNames = {}, _classNames["modal-" + this.props.size] = this.props.size, _classNames[dialogBaseClass + "-centered"] = this.props.centered, _classNames)), this.props.cssModule),
  244. role: "document",
  245. ref: function ref(c) {
  246. _this2._dialog = c;
  247. }
  248. }), _react.default.createElement("div", {
  249. className: (0, _utils.mapToCssModules)((0, _classnames.default)('modal-content', this.props.contentClassName), this.props.cssModule)
  250. }, this.props.children));
  251. };
  252. _proto.render = function render() {
  253. if (this.state.isOpen) {
  254. var _this$props = this.props,
  255. wrapClassName = _this$props.wrapClassName,
  256. modalClassName = _this$props.modalClassName,
  257. backdropClassName = _this$props.backdropClassName,
  258. cssModule = _this$props.cssModule,
  259. isOpen = _this$props.isOpen,
  260. backdrop = _this$props.backdrop,
  261. role = _this$props.role,
  262. labelledBy = _this$props.labelledBy,
  263. external = _this$props.external,
  264. innerRef = _this$props.innerRef;
  265. var modalAttributes = {
  266. onClick: this.handleBackdropClick,
  267. onMouseDown: this.handleBackdropMouseDown,
  268. onKeyUp: this.handleEscape,
  269. onKeyDown: this.handleTab,
  270. style: {
  271. display: 'block'
  272. },
  273. 'aria-labelledby': labelledBy,
  274. role: role,
  275. tabIndex: '-1'
  276. };
  277. var hasTransition = this.props.fade;
  278. var modalTransition = (0, _objectSpread2.default)({}, _Fade.default.defaultProps, this.props.modalTransition, {
  279. baseClass: hasTransition ? this.props.modalTransition.baseClass : '',
  280. timeout: hasTransition ? this.props.modalTransition.timeout : 0
  281. });
  282. var backdropTransition = (0, _objectSpread2.default)({}, _Fade.default.defaultProps, this.props.backdropTransition, {
  283. baseClass: hasTransition ? this.props.backdropTransition.baseClass : '',
  284. timeout: hasTransition ? this.props.backdropTransition.timeout : 0
  285. });
  286. var Backdrop = backdrop && (hasTransition ? _react.default.createElement(_Fade.default, (0, _extends2.default)({}, backdropTransition, {
  287. in: isOpen && !!backdrop,
  288. cssModule: cssModule,
  289. className: (0, _utils.mapToCssModules)((0, _classnames.default)('modal-backdrop', backdropClassName), cssModule)
  290. })) : _react.default.createElement("div", {
  291. className: (0, _utils.mapToCssModules)((0, _classnames.default)('modal-backdrop', 'show', backdropClassName), cssModule)
  292. }));
  293. return _react.default.createElement(_Portal.default, {
  294. node: this._element
  295. }, _react.default.createElement("div", {
  296. className: (0, _utils.mapToCssModules)(wrapClassName)
  297. }, _react.default.createElement(_Fade.default, (0, _extends2.default)({}, modalAttributes, modalTransition, {
  298. in: isOpen,
  299. onEntered: this.onOpened,
  300. onExited: this.onClosed,
  301. cssModule: cssModule,
  302. className: (0, _utils.mapToCssModules)((0, _classnames.default)('modal', modalClassName), cssModule),
  303. innerRef: innerRef
  304. }), external, this.renderModalDialog()), Backdrop));
  305. }
  306. return null;
  307. };
  308. return Modal;
  309. }(_react.default.Component);
  310. Modal.propTypes = propTypes;
  311. Modal.defaultProps = defaultProps;
  312. Modal.openCount = 0;
  313. var _default = Modal;
  314. exports.default = _default;