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