UNPKG

10.8 kBJavaScriptView Raw
1import _extends from "@babel/runtime/helpers/esm/extends";
2import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3import classNames from 'classnames';
4import addEventListener from 'dom-helpers/addEventListener';
5import canUseDOM from 'dom-helpers/canUseDOM';
6import ownerDocument from 'dom-helpers/ownerDocument';
7import removeEventListener from 'dom-helpers/removeEventListener';
8import getScrollbarSize from 'dom-helpers/scrollbarSize';
9import useCallbackRef from '@restart/hooks/useCallbackRef';
10import useEventCallback from '@restart/hooks/useEventCallback';
11import useWillUnmount from '@restart/hooks/useWillUnmount';
12import transitionEnd from 'dom-helpers/transitionEnd';
13import React, { useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react';
14import BaseModal from 'react-overlays/Modal';
15import warning from 'warning';
16import BootstrapModalManager from './BootstrapModalManager';
17import Fade from './Fade';
18import ModalBody from './ModalBody';
19import ModalContext from './ModalContext';
20import ModalDialog from './ModalDialog';
21import ModalFooter from './ModalFooter';
22import ModalHeader from './ModalHeader';
23import ModalTitle from './ModalTitle';
24import { useBootstrapPrefix } from './ThemeProvider';
25var manager;
26var defaultProps = {
27 show: false,
28 backdrop: true,
29 keyboard: true,
30 autoFocus: true,
31 enforceFocus: true,
32 restoreFocus: true,
33 animation: true,
34 dialogAs: ModalDialog
35};
36/* eslint-disable no-use-before-define, react/no-multi-comp */
37
38function DialogTransition(props) {
39 return /*#__PURE__*/React.createElement(Fade, props);
40}
41
42function BackdropTransition(props) {
43 return /*#__PURE__*/React.createElement(Fade, props);
44}
45/* eslint-enable no-use-before-define */
46
47
48var Modal = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
49 var bsPrefix = _ref.bsPrefix,
50 className = _ref.className,
51 style = _ref.style,
52 dialogClassName = _ref.dialogClassName,
53 contentClassName = _ref.contentClassName,
54 children = _ref.children,
55 Dialog = _ref.dialogAs,
56 ariaLabelledby = _ref['aria-labelledby'],
57 show = _ref.show,
58 animation = _ref.animation,
59 backdrop = _ref.backdrop,
60 keyboard = _ref.keyboard,
61 onEscapeKeyDown = _ref.onEscapeKeyDown,
62 onShow = _ref.onShow,
63 onHide = _ref.onHide,
64 container = _ref.container,
65 autoFocus = _ref.autoFocus,
66 enforceFocus = _ref.enforceFocus,
67 restoreFocus = _ref.restoreFocus,
68 restoreFocusOptions = _ref.restoreFocusOptions,
69 onEntered = _ref.onEntered,
70 onExit = _ref.onExit,
71 onExiting = _ref.onExiting,
72 onEnter = _ref.onEnter,
73 onEntering = _ref.onEntering,
74 onExited = _ref.onExited,
75 backdropClassName = _ref.backdropClassName,
76 propsManager = _ref.manager,
77 props = _objectWithoutPropertiesLoose(_ref, ["bsPrefix", "className", "style", "dialogClassName", "contentClassName", "children", "dialogAs", "aria-labelledby", "show", "animation", "backdrop", "keyboard", "onEscapeKeyDown", "onShow", "onHide", "container", "autoFocus", "enforceFocus", "restoreFocus", "restoreFocusOptions", "onEntered", "onExit", "onExiting", "onEnter", "onEntering", "onExited", "backdropClassName", "manager"]);
78
79 var _useState = useState({}),
80 modalStyle = _useState[0],
81 setStyle = _useState[1];
82
83 var _useState2 = useState(false),
84 animateStaticModal = _useState2[0],
85 setAnimateStaticModal = _useState2[1];
86
87 var waitingForMouseUpRef = useRef(false);
88 var ignoreBackdropClickRef = useRef(false);
89 var removeStaticModalAnimationRef = useRef(null); // TODO: what's this type
90
91 var _useCallbackRef = useCallbackRef(),
92 modal = _useCallbackRef[0],
93 setModalRef = _useCallbackRef[1];
94
95 var handleHide = useEventCallback(onHide);
96 bsPrefix = useBootstrapPrefix(bsPrefix, 'modal');
97 useImperativeHandle(ref, function () {
98 return {
99 get _modal() {
100 process.env.NODE_ENV !== "production" ? warning(false, 'Accessing `_modal` is not supported and will be removed in a future release') : void 0;
101 return modal;
102 }
103
104 };
105 }, [modal]);
106 var modalContext = useMemo(function () {
107 return {
108 onHide: handleHide
109 };
110 }, [handleHide]);
111
112 function getModalManager() {
113 if (propsManager) return propsManager;
114 if (!manager) manager = new BootstrapModalManager();
115 return manager;
116 }
117
118 function updateDialogStyle(node) {
119 if (!canUseDOM) return;
120 var containerIsOverflowing = getModalManager().isContainerOverflowing(modal);
121 var modalIsOverflowing = node.scrollHeight > ownerDocument(node).documentElement.clientHeight;
122 setStyle({
123 paddingRight: containerIsOverflowing && !modalIsOverflowing ? getScrollbarSize() : undefined,
124 paddingLeft: !containerIsOverflowing && modalIsOverflowing ? getScrollbarSize() : undefined
125 });
126 }
127
128 var handleWindowResize = useEventCallback(function () {
129 if (modal) {
130 updateDialogStyle(modal.dialog);
131 }
132 });
133 useWillUnmount(function () {
134 removeEventListener(window, 'resize', handleWindowResize);
135
136 if (removeStaticModalAnimationRef.current) {
137 removeStaticModalAnimationRef.current();
138 }
139 }); // We prevent the modal from closing during a drag by detecting where the
140 // the click originates from. If it starts in the modal and then ends outside
141 // don't close.
142
143 var handleDialogMouseDown = function handleDialogMouseDown() {
144 waitingForMouseUpRef.current = true;
145 };
146
147 var handleMouseUp = function handleMouseUp(e) {
148 if (waitingForMouseUpRef.current && modal && e.target === modal.dialog) {
149 ignoreBackdropClickRef.current = true;
150 }
151
152 waitingForMouseUpRef.current = false;
153 };
154
155 var handleStaticModalAnimation = function handleStaticModalAnimation() {
156 setAnimateStaticModal(true);
157 removeStaticModalAnimationRef.current = transitionEnd(modal.dialog, function () {
158 setAnimateStaticModal(false);
159 });
160 };
161
162 var handleStaticBackdropClick = function handleStaticBackdropClick(e) {
163 if (e.target !== e.currentTarget) {
164 return;
165 }
166
167 handleStaticModalAnimation();
168 };
169
170 var handleClick = function handleClick(e) {
171 if (backdrop === 'static') {
172 handleStaticBackdropClick(e);
173 return;
174 }
175
176 if (ignoreBackdropClickRef.current || e.target !== e.currentTarget) {
177 ignoreBackdropClickRef.current = false;
178 return;
179 }
180
181 onHide();
182 };
183
184 var handleEscapeKeyDown = function handleEscapeKeyDown(e) {
185 if (!keyboard && backdrop === 'static') {
186 // Call preventDefault to stop modal from closing in react-overlays,
187 // then play our animation.
188 e.preventDefault();
189 handleStaticModalAnimation();
190 } else if (keyboard && onEscapeKeyDown) {
191 onEscapeKeyDown(e);
192 }
193 };
194
195 var handleEnter = function handleEnter(node) {
196 if (node) {
197 node.style.display = 'block';
198 updateDialogStyle(node);
199 }
200
201 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
202 args[_key - 1] = arguments[_key];
203 }
204
205 if (onEnter) onEnter.apply(void 0, [node].concat(args));
206 };
207
208 var handleExit = function handleExit(node) {
209 if (removeStaticModalAnimationRef.current) {
210 removeStaticModalAnimationRef.current();
211 }
212
213 for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
214 args[_key2 - 1] = arguments[_key2];
215 }
216
217 if (onExit) onExit.apply(void 0, [node].concat(args));
218 };
219
220 var handleEntering = function handleEntering(node) {
221 for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
222 args[_key3 - 1] = arguments[_key3];
223 }
224
225 if (onEntering) onEntering.apply(void 0, [node].concat(args)); // FIXME: This should work even when animation is disabled.
226
227 addEventListener(window, 'resize', handleWindowResize);
228 };
229
230 var handleExited = function handleExited(node) {
231 if (node) node.style.display = ''; // RHL removes it sometimes
232
233 for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) {
234 args[_key4 - 1] = arguments[_key4];
235 }
236
237 if (onExited) onExited.apply(void 0, args); // FIXME: This should work even when animation is disabled.
238
239 removeEventListener(window, 'resize', handleWindowResize);
240 };
241
242 var renderBackdrop = useCallback(function (backdropProps) {
243 return /*#__PURE__*/React.createElement("div", _extends({}, backdropProps, {
244 className: classNames(bsPrefix + "-backdrop", backdropClassName, !animation && 'show')
245 }));
246 }, [animation, backdropClassName, bsPrefix]);
247
248 var baseModalStyle = _extends({}, style, modalStyle); // Sets `display` always block when `animation` is false
249
250
251 if (!animation) {
252 baseModalStyle.display = 'block';
253 }
254
255 var renderDialog = function renderDialog(dialogProps) {
256 return /*#__PURE__*/React.createElement("div", _extends({
257 role: "dialog"
258 }, dialogProps, {
259 style: baseModalStyle,
260 className: classNames(className, bsPrefix, animateStaticModal && bsPrefix + "-static"),
261 onClick: backdrop ? handleClick : undefined,
262 onMouseUp: handleMouseUp,
263 "aria-labelledby": ariaLabelledby
264 }), /*#__PURE__*/React.createElement(Dialog, _extends({}, props, {
265 onMouseDown: handleDialogMouseDown,
266 className: dialogClassName,
267 contentClassName: contentClassName
268 }), children));
269 };
270
271 return /*#__PURE__*/React.createElement(ModalContext.Provider, {
272 value: modalContext
273 }, /*#__PURE__*/React.createElement(BaseModal, {
274 show: show,
275 ref: setModalRef,
276 backdrop: backdrop,
277 container: container,
278 keyboard: true // Always set true - see handleEscapeKeyDown
279 ,
280 autoFocus: autoFocus,
281 enforceFocus: enforceFocus,
282 restoreFocus: restoreFocus,
283 restoreFocusOptions: restoreFocusOptions,
284 onEscapeKeyDown: handleEscapeKeyDown,
285 onShow: onShow,
286 onHide: onHide,
287 onEnter: handleEnter,
288 onEntering: handleEntering,
289 onEntered: onEntered,
290 onExit: handleExit,
291 onExiting: onExiting,
292 onExited: handleExited,
293 manager: getModalManager(),
294 containerClassName: bsPrefix + "-open",
295 transition: animation ? DialogTransition : undefined,
296 backdropTransition: animation ? BackdropTransition : undefined,
297 renderBackdrop: renderBackdrop,
298 renderDialog: renderDialog
299 }));
300});
301Modal.displayName = 'Modal';
302Modal.defaultProps = defaultProps;
303Modal.Body = ModalBody;
304Modal.Header = ModalHeader;
305Modal.Title = ModalTitle;
306Modal.Footer = ModalFooter;
307Modal.Dialog = ModalDialog;
308Modal.TRANSITION_DURATION = 300;
309Modal.BACKDROP_TRANSITION_DURATION = 150;
310export default Modal;
\No newline at end of file