UNPKG

6.52 kBJavaScriptView Raw
1"use strict";
2'use client';
3
4var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
5var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
6Object.defineProperty(exports, "__esModule", {
7 value: true
8});
9exports.default = void 0;
10var React = _interopRequireWildcard(require("react"));
11var _utils = require("@mui/utils");
12var _extractEventHandlers = _interopRequireDefault(require("@mui/utils/extractEventHandlers"));
13var _ModalManager = require("./ModalManager");
14function getContainer(container) {
15 return typeof container === 'function' ? container() : container;
16}
17function getHasTransition(children) {
18 return children ? children.props.hasOwnProperty('in') : false;
19}
20
21// A modal manager used to track and manage the state of open Modals.
22// Modals don't open on the server so this won't conflict with concurrent requests.
23const manager = new _ModalManager.ModalManager();
24/**
25 *
26 * Demos:
27 *
28 * - [Modal](https://mui.com/base-ui/react-modal/#hook)
29 *
30 * API:
31 *
32 * - [useModal API](https://mui.com/base-ui/react-modal/hooks-api/#use-modal)
33 */
34function useModal(parameters) {
35 const {
36 container,
37 disableEscapeKeyDown = false,
38 disableScrollLock = false,
39 closeAfterTransition = false,
40 onTransitionEnter,
41 onTransitionExited,
42 children,
43 onClose,
44 open,
45 rootRef
46 } = parameters;
47
48 // @ts-ignore internal logic
49 const modal = React.useRef({});
50 const mountNodeRef = React.useRef(null);
51 const modalRef = React.useRef(null);
52 const handleRef = (0, _utils.unstable_useForkRef)(modalRef, rootRef);
53 const [exited, setExited] = React.useState(!open);
54 const hasTransition = getHasTransition(children);
55 let ariaHiddenProp = true;
56 if (parameters['aria-hidden'] === 'false' || parameters['aria-hidden'] === false) {
57 ariaHiddenProp = false;
58 }
59 const getDoc = () => (0, _utils.unstable_ownerDocument)(mountNodeRef.current);
60 const getModal = () => {
61 modal.current.modalRef = modalRef.current;
62 modal.current.mount = mountNodeRef.current;
63 return modal.current;
64 };
65 const handleMounted = () => {
66 manager.mount(getModal(), {
67 disableScrollLock
68 });
69
70 // Fix a bug on Chrome where the scroll isn't initially 0.
71 if (modalRef.current) {
72 modalRef.current.scrollTop = 0;
73 }
74 };
75 const handleOpen = (0, _utils.unstable_useEventCallback)(() => {
76 const resolvedContainer = getContainer(container) || getDoc().body;
77 manager.add(getModal(), resolvedContainer);
78
79 // The element was already mounted.
80 if (modalRef.current) {
81 handleMounted();
82 }
83 });
84 const isTopModal = () => manager.isTopModal(getModal());
85 const handlePortalRef = (0, _utils.unstable_useEventCallback)(node => {
86 mountNodeRef.current = node;
87 if (!node) {
88 return;
89 }
90 if (open && isTopModal()) {
91 handleMounted();
92 } else if (modalRef.current) {
93 (0, _ModalManager.ariaHidden)(modalRef.current, ariaHiddenProp);
94 }
95 });
96 const handleClose = React.useCallback(() => {
97 manager.remove(getModal(), ariaHiddenProp);
98 }, [ariaHiddenProp]);
99 React.useEffect(() => {
100 return () => {
101 handleClose();
102 };
103 }, [handleClose]);
104 React.useEffect(() => {
105 if (open) {
106 handleOpen();
107 } else if (!hasTransition || !closeAfterTransition) {
108 handleClose();
109 }
110 }, [open, handleClose, hasTransition, closeAfterTransition, handleOpen]);
111 const createHandleKeyDown = otherHandlers => event => {
112 otherHandlers.onKeyDown?.(event);
113
114 // The handler doesn't take event.defaultPrevented into account:
115 //
116 // event.preventDefault() is meant to stop default behaviors like
117 // clicking a checkbox to check it, hitting a button to submit a form,
118 // and hitting left arrow to move the cursor in a text input etc.
119 // Only special HTML elements have these default behaviors.
120 if (event.key !== 'Escape' || event.which === 229 ||
121 // Wait until IME is settled.
122 !isTopModal()) {
123 return;
124 }
125 if (!disableEscapeKeyDown) {
126 // Swallow the event, in case someone is listening for the escape key on the body.
127 event.stopPropagation();
128 if (onClose) {
129 onClose(event, 'escapeKeyDown');
130 }
131 }
132 };
133 const createHandleBackdropClick = otherHandlers => event => {
134 otherHandlers.onClick?.(event);
135 if (event.target !== event.currentTarget) {
136 return;
137 }
138 if (onClose) {
139 onClose(event, 'backdropClick');
140 }
141 };
142 const getRootProps = (otherHandlers = {}) => {
143 const propsEventHandlers = (0, _extractEventHandlers.default)(parameters);
144
145 // The custom event handlers shouldn't be spread on the root element
146 delete propsEventHandlers.onTransitionEnter;
147 delete propsEventHandlers.onTransitionExited;
148 const externalEventHandlers = {
149 ...propsEventHandlers,
150 ...otherHandlers
151 };
152 return {
153 /*
154 * Marking an element with the role presentation indicates to assistive technology
155 * that this element should be ignored; it exists to support the web application and
156 * is not meant for humans to interact with directly.
157 * https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-static-element-interactions.md
158 */
159 role: 'presentation',
160 ...externalEventHandlers,
161 onKeyDown: createHandleKeyDown(externalEventHandlers),
162 ref: handleRef
163 };
164 };
165 const getBackdropProps = (otherHandlers = {}) => {
166 const externalEventHandlers = otherHandlers;
167 return {
168 'aria-hidden': true,
169 ...externalEventHandlers,
170 onClick: createHandleBackdropClick(externalEventHandlers),
171 open
172 };
173 };
174 const getTransitionProps = () => {
175 const handleEnter = () => {
176 setExited(false);
177 if (onTransitionEnter) {
178 onTransitionEnter();
179 }
180 };
181 const handleExited = () => {
182 setExited(true);
183 if (onTransitionExited) {
184 onTransitionExited();
185 }
186 if (closeAfterTransition) {
187 handleClose();
188 }
189 };
190 return {
191 onEnter: (0, _utils.unstable_createChainedFunction)(handleEnter, children?.props.onEnter),
192 onExited: (0, _utils.unstable_createChainedFunction)(handleExited, children?.props.onExited)
193 };
194 };
195 return {
196 getRootProps,
197 getBackdropProps,
198 getTransitionProps,
199 rootRef: handleRef,
200 portalRef: handlePortalRef,
201 isTopModal,
202 exited,
203 hasTransition
204 };
205}
206var _default = exports.default = useModal;
\No newline at end of file