1 | import _extends from "@babel/runtime/helpers/esm/extends";
|
2 | import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | import activeElement from 'dom-helpers/activeElement';
|
8 | import contains from 'dom-helpers/contains';
|
9 | import canUseDOM from 'dom-helpers/canUseDOM';
|
10 | import listen from 'dom-helpers/listen';
|
11 | import PropTypes from 'prop-types';
|
12 | import React, { useState, useRef, useCallback, useImperativeHandle, forwardRef, useEffect } from 'react';
|
13 | import ReactDOM from 'react-dom';
|
14 | import useMounted from '@restart/hooks/useMounted';
|
15 | import useWillUnmount from '@restart/hooks/useWillUnmount';
|
16 | import usePrevious from '@restart/hooks/usePrevious';
|
17 | import useEventCallback from '@restart/hooks/useEventCallback';
|
18 | import ModalManager from './ModalManager';
|
19 | import useWaitForDOMRef from './useWaitForDOMRef';
|
20 | var manager;
|
21 |
|
22 | function getManager() {
|
23 | if (!manager) manager = new ModalManager();
|
24 | return manager;
|
25 | }
|
26 |
|
27 | function useModalManager(provided) {
|
28 | var modalManager = provided || getManager();
|
29 | var modal = useRef({
|
30 | dialog: null,
|
31 | backdrop: null
|
32 | });
|
33 | return Object.assign(modal.current, {
|
34 | add: function add(container, className) {
|
35 | return modalManager.add(modal.current, container, className);
|
36 | },
|
37 | remove: function remove() {
|
38 | return modalManager.remove(modal.current);
|
39 | },
|
40 | isTopModal: function isTopModal() {
|
41 | return modalManager.isTopModal(modal.current);
|
42 | },
|
43 | setDialogRef: useCallback(function (ref) {
|
44 | modal.current.dialog = ref;
|
45 | }, []),
|
46 | setBackdropRef: useCallback(function (ref) {
|
47 | modal.current.backdrop = ref;
|
48 | }, [])
|
49 | });
|
50 | }
|
51 |
|
52 | var Modal = forwardRef(function (_ref, ref) {
|
53 | var _ref$show = _ref.show,
|
54 | show = _ref$show === void 0 ? false : _ref$show,
|
55 | _ref$role = _ref.role,
|
56 | role = _ref$role === void 0 ? 'dialog' : _ref$role,
|
57 | className = _ref.className,
|
58 | style = _ref.style,
|
59 | children = _ref.children,
|
60 | _ref$backdrop = _ref.backdrop,
|
61 | backdrop = _ref$backdrop === void 0 ? true : _ref$backdrop,
|
62 | _ref$keyboard = _ref.keyboard,
|
63 | keyboard = _ref$keyboard === void 0 ? true : _ref$keyboard,
|
64 | onBackdropClick = _ref.onBackdropClick,
|
65 | onEscapeKeyDown = _ref.onEscapeKeyDown,
|
66 | transition = _ref.transition,
|
67 | backdropTransition = _ref.backdropTransition,
|
68 | _ref$autoFocus = _ref.autoFocus,
|
69 | autoFocus = _ref$autoFocus === void 0 ? true : _ref$autoFocus,
|
70 | _ref$enforceFocus = _ref.enforceFocus,
|
71 | enforceFocus = _ref$enforceFocus === void 0 ? true : _ref$enforceFocus,
|
72 | _ref$restoreFocus = _ref.restoreFocus,
|
73 | restoreFocus = _ref$restoreFocus === void 0 ? true : _ref$restoreFocus,
|
74 | restoreFocusOptions = _ref.restoreFocusOptions,
|
75 | renderDialog = _ref.renderDialog,
|
76 | _ref$renderBackdrop = _ref.renderBackdrop,
|
77 | renderBackdrop = _ref$renderBackdrop === void 0 ? function (props) {
|
78 | return React.createElement("div", props);
|
79 | } : _ref$renderBackdrop,
|
80 | providedManager = _ref.manager,
|
81 | containerRef = _ref.container,
|
82 | containerClassName = _ref.containerClassName,
|
83 | onShow = _ref.onShow,
|
84 | _ref$onHide = _ref.onHide,
|
85 | onHide = _ref$onHide === void 0 ? function () {} : _ref$onHide,
|
86 | onExit = _ref.onExit,
|
87 | onExited = _ref.onExited,
|
88 | onExiting = _ref.onExiting,
|
89 | onEnter = _ref.onEnter,
|
90 | onEntering = _ref.onEntering,
|
91 | onEntered = _ref.onEntered,
|
92 | rest = _objectWithoutPropertiesLoose(_ref, ["show", "role", "className", "style", "children", "backdrop", "keyboard", "onBackdropClick", "onEscapeKeyDown", "transition", "backdropTransition", "autoFocus", "enforceFocus", "restoreFocus", "restoreFocusOptions", "renderDialog", "renderBackdrop", "manager", "container", "containerClassName", "onShow", "onHide", "onExit", "onExited", "onExiting", "onEnter", "onEntering", "onEntered"]);
|
93 |
|
94 | var container = useWaitForDOMRef(containerRef);
|
95 | var modal = useModalManager(providedManager);
|
96 | var isMounted = useMounted();
|
97 | var prevShow = usePrevious(show);
|
98 |
|
99 | var _useState = useState(!show),
|
100 | exited = _useState[0],
|
101 | setExited = _useState[1];
|
102 |
|
103 | var lastFocusRef = useRef(null);
|
104 | useImperativeHandle(ref, function () {
|
105 | return modal;
|
106 | }, [modal]);
|
107 |
|
108 | if (canUseDOM && !prevShow && show) {
|
109 | lastFocusRef.current = activeElement();
|
110 | }
|
111 |
|
112 | if (!transition && !show && !exited) {
|
113 | setExited(true);
|
114 | } else if (show && exited) {
|
115 | setExited(false);
|
116 | }
|
117 |
|
118 | var handleShow = useEventCallback(function () {
|
119 | modal.add(container, containerClassName);
|
120 | removeKeydownListenerRef.current = listen(document, 'keydown', handleDocumentKeyDown);
|
121 | removeFocusListenerRef.current = listen(document, 'focus',
|
122 |
|
123 | function () {
|
124 | return setTimeout(handleEnforceFocus);
|
125 | }, true);
|
126 |
|
127 | if (onShow) {
|
128 | onShow();
|
129 | }
|
130 |
|
131 |
|
132 |
|
133 | if (autoFocus) {
|
134 | var currentActiveElement = activeElement(document);
|
135 |
|
136 | if (modal.dialog && currentActiveElement && !contains(modal.dialog, currentActiveElement)) {
|
137 | lastFocusRef.current = currentActiveElement;
|
138 | modal.dialog.focus();
|
139 | }
|
140 | }
|
141 | });
|
142 | var handleHide = useEventCallback(function () {
|
143 | modal.remove();
|
144 | removeKeydownListenerRef.current == null ? void 0 : removeKeydownListenerRef.current();
|
145 | removeFocusListenerRef.current == null ? void 0 : removeFocusListenerRef.current();
|
146 |
|
147 | if (restoreFocus) {
|
148 | var _lastFocusRef$current;
|
149 |
|
150 |
|
151 | (_lastFocusRef$current = lastFocusRef.current) == null ? void 0 : _lastFocusRef$current.focus == null ? void 0 : _lastFocusRef$current.focus(restoreFocusOptions);
|
152 | lastFocusRef.current = null;
|
153 | }
|
154 | });
|
155 |
|
156 |
|
157 |
|
158 | useEffect(function () {
|
159 | if (!show || !container) return;
|
160 | handleShow();
|
161 | }, [show, container,
|
162 |
|
163 | handleShow]);
|
164 |
|
165 |
|
166 |
|
167 | useEffect(function () {
|
168 | if (!exited) return;
|
169 | handleHide();
|
170 | }, [exited, handleHide]);
|
171 | useWillUnmount(function () {
|
172 | handleHide();
|
173 | });
|
174 |
|
175 | var handleEnforceFocus = useEventCallback(function () {
|
176 | if (!enforceFocus || !isMounted() || !modal.isTopModal()) {
|
177 | return;
|
178 | }
|
179 |
|
180 | var currentActiveElement = activeElement();
|
181 |
|
182 | if (modal.dialog && currentActiveElement && !contains(modal.dialog, currentActiveElement)) {
|
183 | modal.dialog.focus();
|
184 | }
|
185 | });
|
186 | var handleBackdropClick = useEventCallback(function (e) {
|
187 | if (e.target !== e.currentTarget) {
|
188 | return;
|
189 | }
|
190 |
|
191 | onBackdropClick == null ? void 0 : onBackdropClick(e);
|
192 |
|
193 | if (backdrop === true) {
|
194 | onHide();
|
195 | }
|
196 | });
|
197 |
|
198 | var handleDocumentKeyDown = function handleDocumentKeyDown(e) {
|
199 | if (keyboard && e.keyCode === 27 && modal.isTopModal()) {
|
200 | onEscapeKeyDown == null ? void 0 : onEscapeKeyDown(e);
|
201 | onHide();
|
202 | }
|
203 | };
|
204 |
|
205 | var removeFocusListenerRef = useRef();
|
206 | var removeKeydownListenerRef = useRef();
|
207 |
|
208 | var handleHidden = function handleHidden() {
|
209 | setExited(true);
|
210 |
|
211 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
212 | args[_key] = arguments[_key];
|
213 | }
|
214 |
|
215 | onExited == null ? void 0 : onExited.apply(void 0, args);
|
216 | };
|
217 |
|
218 | var Transition = transition;
|
219 |
|
220 | if (!container || !(show || Transition && !exited)) {
|
221 | return null;
|
222 | }
|
223 |
|
224 | var dialogProps = _extends({
|
225 | role: role,
|
226 | ref: modal.setDialogRef,
|
227 |
|
228 | 'aria-modal': role === 'dialog' ? true : undefined
|
229 | }, rest, {
|
230 | style: style,
|
231 | className: className,
|
232 | tabIndex: -1
|
233 | });
|
234 |
|
235 | var dialog = renderDialog ? renderDialog(dialogProps) : React.createElement("div", dialogProps, React.cloneElement(children, {
|
236 | role: 'document'
|
237 | }));
|
238 |
|
239 | if (Transition) {
|
240 | dialog = React.createElement(Transition, {
|
241 | appear: true,
|
242 | unmountOnExit: true,
|
243 | "in": !!show,
|
244 | onExit: onExit,
|
245 | onExiting: onExiting,
|
246 | onExited: handleHidden,
|
247 | onEnter: onEnter,
|
248 | onEntering: onEntering,
|
249 | onEntered: onEntered
|
250 | }, dialog);
|
251 | }
|
252 |
|
253 | var backdropElement = null;
|
254 |
|
255 | if (backdrop) {
|
256 | var BackdropTransition = backdropTransition;
|
257 | backdropElement = renderBackdrop({
|
258 | ref: modal.setBackdropRef,
|
259 | onClick: handleBackdropClick
|
260 | });
|
261 |
|
262 | if (BackdropTransition) {
|
263 | backdropElement = React.createElement(BackdropTransition, {
|
264 | appear: true,
|
265 | "in": !!show
|
266 | }, backdropElement);
|
267 | }
|
268 | }
|
269 |
|
270 | return React.createElement(React.Fragment, null, ReactDOM.createPortal( React.createElement(React.Fragment, null, backdropElement, dialog), container));
|
271 | });
|
272 | var propTypes = {
|
273 | |
274 |
|
275 |
|
276 | show: PropTypes.bool,
|
277 |
|
278 | |
279 |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 | container: PropTypes.any,
|
285 |
|
286 | |
287 |
|
288 |
|
289 | onShow: PropTypes.func,
|
290 |
|
291 | |
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 | onHide: PropTypes.func,
|
298 |
|
299 | |
300 |
|
301 |
|
302 | backdrop: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf(['static'])]),
|
303 |
|
304 | |
305 |
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 | renderDialog: PropTypes.func,
|
313 |
|
314 | |
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 |
|
322 | renderBackdrop: PropTypes.func,
|
323 |
|
324 | |
325 |
|
326 |
|
327 | onEscapeKeyDown: PropTypes.func,
|
328 |
|
329 | |
330 |
|
331 |
|
332 | onBackdropClick: PropTypes.func,
|
333 |
|
334 | |
335 |
|
336 |
|
337 |
|
338 | containerClassName: PropTypes.string,
|
339 |
|
340 | |
341 |
|
342 |
|
343 | keyboard: PropTypes.bool,
|
344 |
|
345 | |
346 |
|
347 |
|
348 |
|
349 | transition: PropTypes.elementType,
|
350 |
|
351 | |
352 |
|
353 |
|
354 |
|
355 | backdropTransition: PropTypes.elementType,
|
356 |
|
357 | |
358 |
|
359 |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 |
|
365 | autoFocus: PropTypes.bool,
|
366 |
|
367 | |
368 |
|
369 |
|
370 |
|
371 |
|
372 |
|
373 | enforceFocus: PropTypes.bool,
|
374 |
|
375 | |
376 |
|
377 |
|
378 |
|
379 | restoreFocus: PropTypes.bool,
|
380 |
|
381 | |
382 |
|
383 |
|
384 |
|
385 |
|
386 | restoreFocusOptions: PropTypes.shape({
|
387 | preventScroll: PropTypes.bool
|
388 | }),
|
389 |
|
390 | |
391 |
|
392 |
|
393 | onEnter: PropTypes.func,
|
394 |
|
395 | |
396 |
|
397 |
|
398 | onEntering: PropTypes.func,
|
399 |
|
400 | |
401 |
|
402 |
|
403 | onEntered: PropTypes.func,
|
404 |
|
405 | |
406 |
|
407 |
|
408 | onExit: PropTypes.func,
|
409 |
|
410 | |
411 |
|
412 |
|
413 | onExiting: PropTypes.func,
|
414 |
|
415 | |
416 |
|
417 |
|
418 | onExited: PropTypes.func,
|
419 |
|
420 | |
421 |
|
422 |
|
423 |
|
424 | manager: PropTypes.instanceOf(ModalManager)
|
425 | };
|
426 | Modal.displayName = 'Modal';
|
427 | Modal.propTypes = propTypes;
|
428 | export default Object.assign(Modal, {
|
429 | Manager: ModalManager
|
430 | }); |
\ | No newline at end of file |