UNPKG

14.6 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
4
5var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
6
7Object.defineProperty(exports, "__esModule", {
8 value: true
9});
10exports.default = exports.styles = void 0;
11
12var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
13
14var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
15
16var React = _interopRequireWildcard(require("react"));
17
18var ReactDOM = _interopRequireWildcard(require("react-dom"));
19
20var _propTypes = _interopRequireDefault(require("prop-types"));
21
22var _styles = require("@material-ui/styles");
23
24var _utils = require("@material-ui/utils");
25
26var _deprecatedPropType = _interopRequireDefault(require("../utils/deprecatedPropType"));
27
28var _ownerDocument = _interopRequireDefault(require("../utils/ownerDocument"));
29
30var _Portal = _interopRequireDefault(require("../Portal"));
31
32var _createChainedFunction = _interopRequireDefault(require("../utils/createChainedFunction"));
33
34var _useForkRef = _interopRequireDefault(require("../utils/useForkRef"));
35
36var _useEventCallback = _interopRequireDefault(require("../utils/useEventCallback"));
37
38var _zIndex = _interopRequireDefault(require("../styles/zIndex"));
39
40var _ModalManager = _interopRequireWildcard(require("./ModalManager"));
41
42var _Unstable_TrapFocus = _interopRequireDefault(require("../Unstable_TrapFocus"));
43
44var _SimpleBackdrop = _interopRequireDefault(require("./SimpleBackdrop"));
45
46function getContainer(container) {
47 container = typeof container === 'function' ? container() : container;
48 return ReactDOM.findDOMNode(container);
49}
50
51function getHasTransition(props) {
52 return props.children ? props.children.props.hasOwnProperty('in') : false;
53} // A modal manager used to track and manage the state of open Modals.
54// Modals don't open on the server so this won't conflict with concurrent requests.
55
56
57var defaultManager = new _ModalManager.default();
58
59var styles = function styles(theme) {
60 return {
61 /* Styles applied to the root element. */
62 root: {
63 position: 'fixed',
64 zIndex: theme.zIndex.modal,
65 right: 0,
66 bottom: 0,
67 top: 0,
68 left: 0
69 },
70
71 /* Styles applied to the root element if the `Modal` has exited. */
72 hidden: {
73 visibility: 'hidden'
74 }
75 };
76};
77/**
78 * Modal is a lower-level construct that is leveraged by the following components:
79 *
80 * - [Dialog](/api/dialog/)
81 * - [Drawer](/api/drawer/)
82 * - [Menu](/api/menu/)
83 * - [Popover](/api/popover/)
84 *
85 * If you are creating a modal dialog, you probably want to use the [Dialog](/api/dialog/) component
86 * rather than directly using Modal.
87 *
88 * This component shares many concepts with [react-overlays](https://react-bootstrap.github.io/react-overlays/#modals).
89 */
90
91
92exports.styles = styles;
93var Modal = /*#__PURE__*/React.forwardRef(function Modal(inProps, ref) {
94 var theme = (0, _styles.useTheme)();
95 var props = (0, _styles.getThemeProps)({
96 name: 'MuiModal',
97 props: (0, _extends2.default)({}, inProps),
98 theme: theme
99 });
100 var _props$BackdropCompon = props.BackdropComponent,
101 BackdropComponent = _props$BackdropCompon === void 0 ? _SimpleBackdrop.default : _props$BackdropCompon,
102 BackdropProps = props.BackdropProps,
103 children = props.children,
104 _props$closeAfterTran = props.closeAfterTransition,
105 closeAfterTransition = _props$closeAfterTran === void 0 ? false : _props$closeAfterTran,
106 container = props.container,
107 _props$disableAutoFoc = props.disableAutoFocus,
108 disableAutoFocus = _props$disableAutoFoc === void 0 ? false : _props$disableAutoFoc,
109 _props$disableBackdro = props.disableBackdropClick,
110 disableBackdropClick = _props$disableBackdro === void 0 ? false : _props$disableBackdro,
111 _props$disableEnforce = props.disableEnforceFocus,
112 disableEnforceFocus = _props$disableEnforce === void 0 ? false : _props$disableEnforce,
113 _props$disableEscapeK = props.disableEscapeKeyDown,
114 disableEscapeKeyDown = _props$disableEscapeK === void 0 ? false : _props$disableEscapeK,
115 _props$disablePortal = props.disablePortal,
116 disablePortal = _props$disablePortal === void 0 ? false : _props$disablePortal,
117 _props$disableRestore = props.disableRestoreFocus,
118 disableRestoreFocus = _props$disableRestore === void 0 ? false : _props$disableRestore,
119 _props$disableScrollL = props.disableScrollLock,
120 disableScrollLock = _props$disableScrollL === void 0 ? false : _props$disableScrollL,
121 _props$hideBackdrop = props.hideBackdrop,
122 hideBackdrop = _props$hideBackdrop === void 0 ? false : _props$hideBackdrop,
123 _props$keepMounted = props.keepMounted,
124 keepMounted = _props$keepMounted === void 0 ? false : _props$keepMounted,
125 _props$manager = props.manager,
126 manager = _props$manager === void 0 ? defaultManager : _props$manager,
127 onBackdropClick = props.onBackdropClick,
128 onClose = props.onClose,
129 onEscapeKeyDown = props.onEscapeKeyDown,
130 onRendered = props.onRendered,
131 open = props.open,
132 other = (0, _objectWithoutProperties2.default)(props, ["BackdropComponent", "BackdropProps", "children", "closeAfterTransition", "container", "disableAutoFocus", "disableBackdropClick", "disableEnforceFocus", "disableEscapeKeyDown", "disablePortal", "disableRestoreFocus", "disableScrollLock", "hideBackdrop", "keepMounted", "manager", "onBackdropClick", "onClose", "onEscapeKeyDown", "onRendered", "open"]);
133
134 var _React$useState = React.useState(true),
135 exited = _React$useState[0],
136 setExited = _React$useState[1];
137
138 var modal = React.useRef({});
139 var mountNodeRef = React.useRef(null);
140 var modalRef = React.useRef(null);
141 var handleRef = (0, _useForkRef.default)(modalRef, ref);
142 var hasTransition = getHasTransition(props);
143
144 var getDoc = function getDoc() {
145 return (0, _ownerDocument.default)(mountNodeRef.current);
146 };
147
148 var getModal = function getModal() {
149 modal.current.modalRef = modalRef.current;
150 modal.current.mountNode = mountNodeRef.current;
151 return modal.current;
152 };
153
154 var handleMounted = function handleMounted() {
155 manager.mount(getModal(), {
156 disableScrollLock: disableScrollLock
157 }); // Fix a bug on Chrome where the scroll isn't initially 0.
158
159 modalRef.current.scrollTop = 0;
160 };
161
162 var handleOpen = (0, _useEventCallback.default)(function () {
163 var resolvedContainer = getContainer(container) || getDoc().body;
164 manager.add(getModal(), resolvedContainer); // The element was already mounted.
165
166 if (modalRef.current) {
167 handleMounted();
168 }
169 });
170 var isTopModal = React.useCallback(function () {
171 return manager.isTopModal(getModal());
172 }, [manager]);
173 var handlePortalRef = (0, _useEventCallback.default)(function (node) {
174 mountNodeRef.current = node;
175
176 if (!node) {
177 return;
178 }
179
180 if (onRendered) {
181 onRendered();
182 }
183
184 if (open && isTopModal()) {
185 handleMounted();
186 } else {
187 (0, _ModalManager.ariaHidden)(modalRef.current, true);
188 }
189 });
190 var handleClose = React.useCallback(function () {
191 manager.remove(getModal());
192 }, [manager]);
193 React.useEffect(function () {
194 return function () {
195 handleClose();
196 };
197 }, [handleClose]);
198 React.useEffect(function () {
199 if (open) {
200 handleOpen();
201 } else if (!hasTransition || !closeAfterTransition) {
202 handleClose();
203 }
204 }, [open, handleClose, hasTransition, closeAfterTransition, handleOpen]);
205
206 if (!keepMounted && !open && (!hasTransition || exited)) {
207 return null;
208 }
209
210 var handleEnter = function handleEnter() {
211 setExited(false);
212 };
213
214 var handleExited = function handleExited() {
215 setExited(true);
216
217 if (closeAfterTransition) {
218 handleClose();
219 }
220 };
221
222 var handleBackdropClick = function handleBackdropClick(event) {
223 if (event.target !== event.currentTarget) {
224 return;
225 }
226
227 if (onBackdropClick) {
228 onBackdropClick(event);
229 }
230
231 if (!disableBackdropClick && onClose) {
232 onClose(event, 'backdropClick');
233 }
234 };
235
236 var handleKeyDown = function handleKeyDown(event) {
237 // The handler doesn't take event.defaultPrevented into account:
238 //
239 // event.preventDefault() is meant to stop default behaviours like
240 // clicking a checkbox to check it, hitting a button to submit a form,
241 // and hitting left arrow to move the cursor in a text input etc.
242 // Only special HTML elements have these default behaviors.
243 if (event.key !== 'Escape' || !isTopModal()) {
244 return;
245 }
246
247 if (onEscapeKeyDown) {
248 onEscapeKeyDown(event);
249 }
250
251 if (!disableEscapeKeyDown) {
252 // Swallow the event, in case someone is listening for the escape key on the body.
253 event.stopPropagation();
254
255 if (onClose) {
256 onClose(event, 'escapeKeyDown');
257 }
258 }
259 };
260
261 var inlineStyle = styles(theme || {
262 zIndex: _zIndex.default
263 });
264 var childProps = {};
265
266 if (children.props.tabIndex === undefined) {
267 childProps.tabIndex = children.props.tabIndex || '-1';
268 } // It's a Transition like component
269
270
271 if (hasTransition) {
272 childProps.onEnter = (0, _createChainedFunction.default)(handleEnter, children.props.onEnter);
273 childProps.onExited = (0, _createChainedFunction.default)(handleExited, children.props.onExited);
274 }
275
276 return /*#__PURE__*/React.createElement(_Portal.default, {
277 ref: handlePortalRef,
278 container: container,
279 disablePortal: disablePortal
280 }, /*#__PURE__*/React.createElement("div", (0, _extends2.default)({
281 ref: handleRef,
282 onKeyDown: handleKeyDown,
283 role: "presentation"
284 }, other, {
285 style: (0, _extends2.default)({}, inlineStyle.root, !open && exited ? inlineStyle.hidden : {}, other.style)
286 }), hideBackdrop ? null : /*#__PURE__*/React.createElement(BackdropComponent, (0, _extends2.default)({
287 open: open,
288 onClick: handleBackdropClick
289 }, BackdropProps)), /*#__PURE__*/React.createElement(_Unstable_TrapFocus.default, {
290 disableEnforceFocus: disableEnforceFocus,
291 disableAutoFocus: disableAutoFocus,
292 disableRestoreFocus: disableRestoreFocus,
293 getDoc: getDoc,
294 isEnabled: isTopModal,
295 open: open
296 }, /*#__PURE__*/React.cloneElement(children, childProps))));
297});
298process.env.NODE_ENV !== "production" ? Modal.propTypes = {
299 /**
300 * A backdrop component. This prop enables custom backdrop rendering.
301 */
302 BackdropComponent: _propTypes.default.elementType,
303
304 /**
305 * Props applied to the [`Backdrop`](/api/backdrop/) element.
306 */
307 BackdropProps: _propTypes.default.object,
308
309 /**
310 * A single child content element.
311 */
312 children: _utils.elementAcceptingRef.isRequired,
313
314 /**
315 * When set to true the Modal waits until a nested Transition is completed before closing.
316 */
317 closeAfterTransition: _propTypes.default.bool,
318
319 /**
320 * A HTML element, component instance, or function that returns either.
321 * The `container` will have the portal children appended to it.
322 *
323 * By default, it uses the body of the top-level document object,
324 * so it's simply `document.body` most of the time.
325 */
326 container: _propTypes.default
327 /* @typescript-to-proptypes-ignore */
328 .oneOfType([_utils.HTMLElementType, _propTypes.default.instanceOf(React.Component), _propTypes.default.func]),
329
330 /**
331 * If `true`, the modal will not automatically shift focus to itself when it opens, and
332 * replace it to the last focused element when it closes.
333 * This also works correctly with any modal children that have the `disableAutoFocus` prop.
334 *
335 * Generally this should never be set to `true` as it makes the modal less
336 * accessible to assistive technologies, like screen readers.
337 */
338 disableAutoFocus: _propTypes.default.bool,
339
340 /**
341 * If `true`, clicking the backdrop will not fire `onClose`.
342 */
343 disableBackdropClick: (0, _deprecatedPropType.default)(_propTypes.default.bool, 'Use the onClose prop with the `reason` argument to filter the `backdropClick` events.'),
344
345 /**
346 * If `true`, the modal will not prevent focus from leaving the modal while open.
347 *
348 * Generally this should never be set to `true` as it makes the modal less
349 * accessible to assistive technologies, like screen readers.
350 */
351 disableEnforceFocus: _propTypes.default.bool,
352
353 /**
354 * If `true`, hitting escape will not fire `onClose`.
355 */
356 disableEscapeKeyDown: _propTypes.default.bool,
357
358 /**
359 * Disable the portal behavior.
360 * The children stay within it's parent DOM hierarchy.
361 */
362 disablePortal: _propTypes.default.bool,
363
364 /**
365 * If `true`, the modal will not restore focus to previously focused element once
366 * modal is hidden.
367 */
368 disableRestoreFocus: _propTypes.default.bool,
369
370 /**
371 * Disable the scroll lock behavior.
372 */
373 disableScrollLock: _propTypes.default.bool,
374
375 /**
376 * If `true`, the backdrop is not rendered.
377 */
378 hideBackdrop: _propTypes.default.bool,
379
380 /**
381 * Always keep the children in the DOM.
382 * This prop can be useful in SEO situation or
383 * when you want to maximize the responsiveness of the Modal.
384 */
385 keepMounted: _propTypes.default.bool,
386
387 /**
388 * @ignore
389 */
390 manager: _propTypes.default.object,
391
392 /**
393 * Callback fired when the backdrop is clicked.
394 */
395 onBackdropClick: (0, _deprecatedPropType.default)(_propTypes.default.func, 'Use the onClose prop with the `reason` argument to handle the `backdropClick` events.'),
396
397 /**
398 * Callback fired when the component requests to be closed.
399 * The `reason` parameter can optionally be used to control the response to `onClose`.
400 *
401 * @param {object} event The event source of the callback.
402 * @param {string} reason Can be: `"escapeKeyDown"`, `"backdropClick"`.
403 */
404 onClose: _propTypes.default.func,
405
406 /**
407 * Callback fired when the escape key is pressed,
408 * `disableEscapeKeyDown` is false and the modal is in focus.
409 */
410 onEscapeKeyDown: (0, _deprecatedPropType.default)(_propTypes.default.func, 'Use the onClose prop with the `reason` argument to handle the `escapeKeyDown` events.'),
411
412 /**
413 * Callback fired once the children has been mounted into the `container`.
414 * It signals that the `open={true}` prop took effect.
415 *
416 * This prop will be removed in v5, the ref can be used instead.
417 */
418 onRendered: (0, _deprecatedPropType.default)(_propTypes.default.func, 'Use the ref instead.'),
419
420 /**
421 * If `true`, the modal is open.
422 */
423 open: _propTypes.default.bool.isRequired
424} : void 0;
425var _default = Modal;
426exports.default = _default;
\No newline at end of file