1 | 'use client';
|
2 |
|
3 | import * as React from 'react';
|
4 | import { unstable_ownerDocument as ownerDocument, unstable_useForkRef as useForkRef, unstable_useEventCallback as useEventCallback, unstable_createChainedFunction as createChainedFunction } from '@mui/utils';
|
5 | import extractEventHandlers from '@mui/utils/extractEventHandlers';
|
6 | import { ModalManager, ariaHidden } from "./ModalManager.js";
|
7 | function getContainer(container) {
|
8 | return typeof container === 'function' ? container() : container;
|
9 | }
|
10 | function getHasTransition(children) {
|
11 | return children ? children.props.hasOwnProperty('in') : false;
|
12 | }
|
13 |
|
14 |
|
15 |
|
16 | const manager = new ModalManager();
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | function useModal(parameters) {
|
28 | const {
|
29 | container,
|
30 | disableEscapeKeyDown = false,
|
31 | disableScrollLock = false,
|
32 | closeAfterTransition = false,
|
33 | onTransitionEnter,
|
34 | onTransitionExited,
|
35 | children,
|
36 | onClose,
|
37 | open,
|
38 | rootRef
|
39 | } = parameters;
|
40 |
|
41 |
|
42 | const modal = React.useRef({});
|
43 | const mountNodeRef = React.useRef(null);
|
44 | const modalRef = React.useRef(null);
|
45 | const handleRef = useForkRef(modalRef, rootRef);
|
46 | const [exited, setExited] = React.useState(!open);
|
47 | const hasTransition = getHasTransition(children);
|
48 | let ariaHiddenProp = true;
|
49 | if (parameters['aria-hidden'] === 'false' || parameters['aria-hidden'] === false) {
|
50 | ariaHiddenProp = false;
|
51 | }
|
52 | const getDoc = () => ownerDocument(mountNodeRef.current);
|
53 | const getModal = () => {
|
54 | modal.current.modalRef = modalRef.current;
|
55 | modal.current.mount = mountNodeRef.current;
|
56 | return modal.current;
|
57 | };
|
58 | const handleMounted = () => {
|
59 | manager.mount(getModal(), {
|
60 | disableScrollLock
|
61 | });
|
62 |
|
63 |
|
64 | if (modalRef.current) {
|
65 | modalRef.current.scrollTop = 0;
|
66 | }
|
67 | };
|
68 | const handleOpen = useEventCallback(() => {
|
69 | const resolvedContainer = getContainer(container) || getDoc().body;
|
70 | manager.add(getModal(), resolvedContainer);
|
71 |
|
72 |
|
73 | if (modalRef.current) {
|
74 | handleMounted();
|
75 | }
|
76 | });
|
77 | const isTopModal = () => manager.isTopModal(getModal());
|
78 | const handlePortalRef = useEventCallback(node => {
|
79 | mountNodeRef.current = node;
|
80 | if (!node) {
|
81 | return;
|
82 | }
|
83 | if (open && isTopModal()) {
|
84 | handleMounted();
|
85 | } else if (modalRef.current) {
|
86 | ariaHidden(modalRef.current, ariaHiddenProp);
|
87 | }
|
88 | });
|
89 | const handleClose = React.useCallback(() => {
|
90 | manager.remove(getModal(), ariaHiddenProp);
|
91 | }, [ariaHiddenProp]);
|
92 | React.useEffect(() => {
|
93 | return () => {
|
94 | handleClose();
|
95 | };
|
96 | }, [handleClose]);
|
97 | React.useEffect(() => {
|
98 | if (open) {
|
99 | handleOpen();
|
100 | } else if (!hasTransition || !closeAfterTransition) {
|
101 | handleClose();
|
102 | }
|
103 | }, [open, handleClose, hasTransition, closeAfterTransition, handleOpen]);
|
104 | const createHandleKeyDown = otherHandlers => event => {
|
105 | otherHandlers.onKeyDown?.(event);
|
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 | if (event.key !== 'Escape' || event.which === 229 ||
|
114 |
|
115 | !isTopModal()) {
|
116 | return;
|
117 | }
|
118 | if (!disableEscapeKeyDown) {
|
119 |
|
120 | event.stopPropagation();
|
121 | if (onClose) {
|
122 | onClose(event, 'escapeKeyDown');
|
123 | }
|
124 | }
|
125 | };
|
126 | const createHandleBackdropClick = otherHandlers => event => {
|
127 | otherHandlers.onClick?.(event);
|
128 | if (event.target !== event.currentTarget) {
|
129 | return;
|
130 | }
|
131 | if (onClose) {
|
132 | onClose(event, 'backdropClick');
|
133 | }
|
134 | };
|
135 | const getRootProps = (otherHandlers = {}) => {
|
136 | const propsEventHandlers = extractEventHandlers(parameters);
|
137 |
|
138 |
|
139 | delete propsEventHandlers.onTransitionEnter;
|
140 | delete propsEventHandlers.onTransitionExited;
|
141 | const externalEventHandlers = {
|
142 | ...propsEventHandlers,
|
143 | ...otherHandlers
|
144 | };
|
145 | return {
|
146 | |
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 | role: 'presentation',
|
153 | ...externalEventHandlers,
|
154 | onKeyDown: createHandleKeyDown(externalEventHandlers),
|
155 | ref: handleRef
|
156 | };
|
157 | };
|
158 | const getBackdropProps = (otherHandlers = {}) => {
|
159 | const externalEventHandlers = otherHandlers;
|
160 | return {
|
161 | 'aria-hidden': true,
|
162 | ...externalEventHandlers,
|
163 | onClick: createHandleBackdropClick(externalEventHandlers),
|
164 | open
|
165 | };
|
166 | };
|
167 | const getTransitionProps = () => {
|
168 | const handleEnter = () => {
|
169 | setExited(false);
|
170 | if (onTransitionEnter) {
|
171 | onTransitionEnter();
|
172 | }
|
173 | };
|
174 | const handleExited = () => {
|
175 | setExited(true);
|
176 | if (onTransitionExited) {
|
177 | onTransitionExited();
|
178 | }
|
179 | if (closeAfterTransition) {
|
180 | handleClose();
|
181 | }
|
182 | };
|
183 | return {
|
184 | onEnter: createChainedFunction(handleEnter, children?.props.onEnter),
|
185 | onExited: createChainedFunction(handleExited, children?.props.onExited)
|
186 | };
|
187 | };
|
188 | return {
|
189 | getRootProps,
|
190 | getBackdropProps,
|
191 | getTransitionProps,
|
192 | rootRef: handleRef,
|
193 | portalRef: handlePortalRef,
|
194 | isTopModal,
|
195 | exited,
|
196 | hasTransition
|
197 | };
|
198 | }
|
199 | export default useModal; |
\ | No newline at end of file |