UNPKG

12.8 kBJavaScriptView Raw
1'use client';
2
3import * as React from 'react';
4import PropTypes from 'prop-types';
5import clsx from 'clsx';
6import HTMLElementType from '@mui/utils/HTMLElementType';
7import elementAcceptingRef from '@mui/utils/elementAcceptingRef';
8import composeClasses from '@mui/utils/composeClasses';
9import FocusTrap from "../Unstable_TrapFocus/index.js";
10import Portal from "../Portal/index.js";
11import { styled } from "../zero-styled/index.js";
12import memoTheme from "../utils/memoTheme.js";
13import { useDefaultProps } from "../DefaultPropsProvider/index.js";
14import Backdrop from "../Backdrop/index.js";
15import useModal from "./useModal.js";
16import { getModalUtilityClass } from "./modalClasses.js";
17import useSlot from "../utils/useSlot.js";
18import { useForkRef } from "../utils/index.js";
19import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
20const useUtilityClasses = ownerState => {
21 const {
22 open,
23 exited,
24 classes
25 } = ownerState;
26 const slots = {
27 root: ['root', !open && exited && 'hidden'],
28 backdrop: ['backdrop']
29 };
30 return composeClasses(slots, getModalUtilityClass, classes);
31};
32const ModalRoot = styled('div', {
33 name: 'MuiModal',
34 slot: 'Root',
35 overridesResolver: (props, styles) => {
36 const {
37 ownerState
38 } = props;
39 return [styles.root, !ownerState.open && ownerState.exited && styles.hidden];
40 }
41})(memoTheme(({
42 theme
43}) => ({
44 position: 'fixed',
45 zIndex: (theme.vars || theme).zIndex.modal,
46 right: 0,
47 bottom: 0,
48 top: 0,
49 left: 0,
50 variants: [{
51 props: ({
52 ownerState
53 }) => !ownerState.open && ownerState.exited,
54 style: {
55 visibility: 'hidden'
56 }
57 }]
58})));
59const ModalBackdrop = styled(Backdrop, {
60 name: 'MuiModal',
61 slot: 'Backdrop',
62 overridesResolver: (props, styles) => {
63 return styles.backdrop;
64 }
65})({
66 zIndex: -1
67});
68
69/**
70 * Modal is a lower-level construct that is leveraged by the following components:
71 *
72 * - [Dialog](/material-ui/api/dialog/)
73 * - [Drawer](/material-ui/api/drawer/)
74 * - [Menu](/material-ui/api/menu/)
75 * - [Popover](/material-ui/api/popover/)
76 *
77 * If you are creating a modal dialog, you probably want to use the [Dialog](/material-ui/api/dialog/) component
78 * rather than directly using Modal.
79 *
80 * This component shares many concepts with [react-overlays](https://react-bootstrap.github.io/react-overlays/#modals).
81 */
82const Modal = /*#__PURE__*/React.forwardRef(function Modal(inProps, ref) {
83 const props = useDefaultProps({
84 name: 'MuiModal',
85 props: inProps
86 });
87 const {
88 BackdropComponent = ModalBackdrop,
89 BackdropProps,
90 classes: classesProp,
91 className,
92 closeAfterTransition = false,
93 children,
94 container,
95 component,
96 components = {},
97 componentsProps = {},
98 disableAutoFocus = false,
99 disableEnforceFocus = false,
100 disableEscapeKeyDown = false,
101 disablePortal = false,
102 disableRestoreFocus = false,
103 disableScrollLock = false,
104 hideBackdrop = false,
105 keepMounted = false,
106 onBackdropClick,
107 onClose,
108 onTransitionEnter,
109 onTransitionExited,
110 open,
111 slotProps = {},
112 slots = {},
113 // eslint-disable-next-line react/prop-types
114 theme,
115 ...other
116 } = props;
117 const propsWithDefaults = {
118 ...props,
119 closeAfterTransition,
120 disableAutoFocus,
121 disableEnforceFocus,
122 disableEscapeKeyDown,
123 disablePortal,
124 disableRestoreFocus,
125 disableScrollLock,
126 hideBackdrop,
127 keepMounted
128 };
129 const {
130 getRootProps,
131 getBackdropProps,
132 getTransitionProps,
133 portalRef,
134 isTopModal,
135 exited,
136 hasTransition
137 } = useModal({
138 ...propsWithDefaults,
139 rootRef: ref
140 });
141 const ownerState = {
142 ...propsWithDefaults,
143 exited
144 };
145 const classes = useUtilityClasses(ownerState);
146 const childProps = {};
147 if (children.props.tabIndex === undefined) {
148 childProps.tabIndex = '-1';
149 }
150
151 // It's a Transition like component
152 if (hasTransition) {
153 const {
154 onEnter,
155 onExited
156 } = getTransitionProps();
157 childProps.onEnter = onEnter;
158 childProps.onExited = onExited;
159 }
160 const externalForwardedProps = {
161 ...other,
162 slots: {
163 root: components.Root,
164 backdrop: components.Backdrop,
165 ...slots
166 },
167 slotProps: {
168 ...componentsProps,
169 ...slotProps
170 }
171 };
172 const [RootSlot, rootProps] = useSlot('root', {
173 elementType: ModalRoot,
174 externalForwardedProps,
175 getSlotProps: getRootProps,
176 additionalProps: {
177 ref,
178 as: component
179 },
180 ownerState,
181 className: clsx(className, classes?.root, !ownerState.open && ownerState.exited && classes?.hidden)
182 });
183 const [BackdropSlot, backdropProps] = useSlot('backdrop', {
184 elementType: BackdropComponent,
185 externalForwardedProps,
186 additionalProps: BackdropProps,
187 getSlotProps: otherHandlers => {
188 return getBackdropProps({
189 ...otherHandlers,
190 onClick: event => {
191 if (onBackdropClick) {
192 onBackdropClick(event);
193 }
194 if (otherHandlers?.onClick) {
195 otherHandlers.onClick(event);
196 }
197 }
198 });
199 },
200 className: clsx(BackdropProps?.className, classes?.backdrop),
201 ownerState
202 });
203 const backdropRef = useForkRef(BackdropProps?.ref, backdropProps.ref);
204 if (!keepMounted && !open && (!hasTransition || exited)) {
205 return null;
206 }
207 return /*#__PURE__*/_jsx(Portal, {
208 ref: portalRef,
209 container: container,
210 disablePortal: disablePortal,
211 children: /*#__PURE__*/_jsxs(RootSlot, {
212 ...rootProps,
213 children: [!hideBackdrop && BackdropComponent ? /*#__PURE__*/_jsx(BackdropSlot, {
214 ...backdropProps,
215 ref: backdropRef
216 }) : null, /*#__PURE__*/_jsx(FocusTrap, {
217 disableEnforceFocus: disableEnforceFocus,
218 disableAutoFocus: disableAutoFocus,
219 disableRestoreFocus: disableRestoreFocus,
220 isEnabled: isTopModal,
221 open: open,
222 children: /*#__PURE__*/React.cloneElement(children, childProps)
223 })]
224 })
225 });
226});
227process.env.NODE_ENV !== "production" ? Modal.propTypes /* remove-proptypes */ = {
228 // ┌────────────────────────────── Warning ──────────────────────────────┐
229 // │ These PropTypes are generated from the TypeScript type definitions. │
230 // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
231 // └─────────────────────────────────────────────────────────────────────┘
232 /**
233 * A backdrop component. This prop enables custom backdrop rendering.
234 * @deprecated Use `slots.backdrop` instead. While this prop currently works, it will be removed in the next major version.
235 * Use the `slots.backdrop` prop to make your application ready for the next version of Material UI.
236 * @default styled(Backdrop, {
237 * name: 'MuiModal',
238 * slot: 'Backdrop',
239 * overridesResolver: (props, styles) => {
240 * return styles.backdrop;
241 * },
242 * })({
243 * zIndex: -1,
244 * })
245 */
246 BackdropComponent: PropTypes.elementType,
247 /**
248 * Props applied to the [`Backdrop`](https://mui.com/material-ui/api/backdrop/) element.
249 * @deprecated Use `slotProps.backdrop` instead.
250 */
251 BackdropProps: PropTypes.object,
252 /**
253 * A single child content element.
254 */
255 children: elementAcceptingRef.isRequired,
256 /**
257 * Override or extend the styles applied to the component.
258 */
259 classes: PropTypes.object,
260 /**
261 * @ignore
262 */
263 className: PropTypes.string,
264 /**
265 * When set to true the Modal waits until a nested Transition is completed before closing.
266 * @default false
267 */
268 closeAfterTransition: PropTypes.bool,
269 /**
270 * The component used for the root node.
271 * Either a string to use a HTML element or a component.
272 */
273 component: PropTypes.elementType,
274 /**
275 * The components used for each slot inside.
276 *
277 * @deprecated Use the `slots` prop instead. This prop will be removed in v7. See [Migrating from deprecated APIs](https://mui.com/material-ui/migration/migrating-from-deprecated-apis/) for more details.
278 *
279 * @default {}
280 */
281 components: PropTypes.shape({
282 Backdrop: PropTypes.elementType,
283 Root: PropTypes.elementType
284 }),
285 /**
286 * The extra props for the slot components.
287 * You can override the existing props or add new ones.
288 *
289 * @deprecated Use the `slotProps` prop instead. This prop will be removed in v7. See [Migrating from deprecated APIs](https://mui.com/material-ui/migration/migrating-from-deprecated-apis/) for more details.
290 *
291 * @default {}
292 */
293 componentsProps: PropTypes.shape({
294 backdrop: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
295 root: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
296 }),
297 /**
298 * An HTML element or function that returns one.
299 * The `container` will have the portal children appended to it.
300 *
301 * You can also provide a callback, which is called in a React layout effect.
302 * This lets you set the container from a ref, and also makes server-side rendering possible.
303 *
304 * By default, it uses the body of the top-level document object,
305 * so it's simply `document.body` most of the time.
306 */
307 container: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([HTMLElementType, PropTypes.func]),
308 /**
309 * If `true`, the modal will not automatically shift focus to itself when it opens, and
310 * replace it to the last focused element when it closes.
311 * This also works correctly with any modal children that have the `disableAutoFocus` prop.
312 *
313 * Generally this should never be set to `true` as it makes the modal less
314 * accessible to assistive technologies, like screen readers.
315 * @default false
316 */
317 disableAutoFocus: PropTypes.bool,
318 /**
319 * If `true`, the modal will not prevent focus from leaving the modal while open.
320 *
321 * Generally this should never be set to `true` as it makes the modal less
322 * accessible to assistive technologies, like screen readers.
323 * @default false
324 */
325 disableEnforceFocus: PropTypes.bool,
326 /**
327 * If `true`, hitting escape will not fire the `onClose` callback.
328 * @default false
329 */
330 disableEscapeKeyDown: PropTypes.bool,
331 /**
332 * The `children` will be under the DOM hierarchy of the parent component.
333 * @default false
334 */
335 disablePortal: PropTypes.bool,
336 /**
337 * If `true`, the modal will not restore focus to previously focused element once
338 * modal is hidden or unmounted.
339 * @default false
340 */
341 disableRestoreFocus: PropTypes.bool,
342 /**
343 * Disable the scroll lock behavior.
344 * @default false
345 */
346 disableScrollLock: PropTypes.bool,
347 /**
348 * If `true`, the backdrop is not rendered.
349 * @default false
350 */
351 hideBackdrop: PropTypes.bool,
352 /**
353 * Always keep the children in the DOM.
354 * This prop can be useful in SEO situation or
355 * when you want to maximize the responsiveness of the Modal.
356 * @default false
357 */
358 keepMounted: PropTypes.bool,
359 /**
360 * Callback fired when the backdrop is clicked.
361 * @deprecated Use the `onClose` prop with the `reason` argument to handle the `backdropClick` events.
362 */
363 onBackdropClick: PropTypes.func,
364 /**
365 * Callback fired when the component requests to be closed.
366 * The `reason` parameter can optionally be used to control the response to `onClose`.
367 *
368 * @param {object} event The event source of the callback.
369 * @param {string} reason Can be: `"escapeKeyDown"`, `"backdropClick"`.
370 */
371 onClose: PropTypes.func,
372 /**
373 * A function called when a transition enters.
374 */
375 onTransitionEnter: PropTypes.func,
376 /**
377 * A function called when a transition has exited.
378 */
379 onTransitionExited: PropTypes.func,
380 /**
381 * If `true`, the component is shown.
382 */
383 open: PropTypes.bool.isRequired,
384 /**
385 * The props used for each slot inside the Modal.
386 * @default {}
387 */
388 slotProps: PropTypes.shape({
389 backdrop: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
390 root: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
391 }),
392 /**
393 * The components used for each slot inside the Modal.
394 * Either a string to use a HTML element or a component.
395 * @default {}
396 */
397 slots: PropTypes.shape({
398 backdrop: PropTypes.elementType,
399 root: PropTypes.elementType
400 }),
401 /**
402 * The system prop that allows defining system overrides as well as additional CSS styles.
403 */
404 sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object])
405} : void 0;
406export default Modal;
\No newline at end of file