UNPKG

11.9 kBJavaScriptView Raw
1'use client';
2
3import * as React from 'react';
4import PropTypes from 'prop-types';
5import { Transition } from 'react-transition-group';
6import chainPropTypes from '@mui/utils/chainPropTypes';
7import HTMLElementType from '@mui/utils/HTMLElementType';
8import elementAcceptingRef from '@mui/utils/elementAcceptingRef';
9import getReactElementRef from '@mui/utils/getReactElementRef';
10import debounce from "../utils/debounce.js";
11import useForkRef from "../utils/useForkRef.js";
12import { useTheme } from "../zero-styled/index.js";
13import { reflow, getTransitionProps } from "../transitions/utils.js";
14import { ownerWindow } from "../utils/index.js";
15
16// Translate the node so it can't be seen on the screen.
17// Later, we're going to translate the node back to its original location with `none`.
18import { jsx as _jsx } from "react/jsx-runtime";
19function getTranslateValue(direction, node, resolvedContainer) {
20 const rect = node.getBoundingClientRect();
21 const containerRect = resolvedContainer && resolvedContainer.getBoundingClientRect();
22 const containerWindow = ownerWindow(node);
23 let transform;
24 if (node.fakeTransform) {
25 transform = node.fakeTransform;
26 } else {
27 const computedStyle = containerWindow.getComputedStyle(node);
28 transform = computedStyle.getPropertyValue('-webkit-transform') || computedStyle.getPropertyValue('transform');
29 }
30 let offsetX = 0;
31 let offsetY = 0;
32 if (transform && transform !== 'none' && typeof transform === 'string') {
33 const transformValues = transform.split('(')[1].split(')')[0].split(',');
34 offsetX = parseInt(transformValues[4], 10);
35 offsetY = parseInt(transformValues[5], 10);
36 }
37 if (direction === 'left') {
38 if (containerRect) {
39 return `translateX(${containerRect.right + offsetX - rect.left}px)`;
40 }
41 return `translateX(${containerWindow.innerWidth + offsetX - rect.left}px)`;
42 }
43 if (direction === 'right') {
44 if (containerRect) {
45 return `translateX(-${rect.right - containerRect.left - offsetX}px)`;
46 }
47 return `translateX(-${rect.left + rect.width - offsetX}px)`;
48 }
49 if (direction === 'up') {
50 if (containerRect) {
51 return `translateY(${containerRect.bottom + offsetY - rect.top}px)`;
52 }
53 return `translateY(${containerWindow.innerHeight + offsetY - rect.top}px)`;
54 }
55
56 // direction === 'down'
57 if (containerRect) {
58 return `translateY(-${rect.top - containerRect.top + rect.height - offsetY}px)`;
59 }
60 return `translateY(-${rect.top + rect.height - offsetY}px)`;
61}
62function resolveContainer(containerPropProp) {
63 return typeof containerPropProp === 'function' ? containerPropProp() : containerPropProp;
64}
65export function setTranslateValue(direction, node, containerProp) {
66 const resolvedContainer = resolveContainer(containerProp);
67 const transform = getTranslateValue(direction, node, resolvedContainer);
68 if (transform) {
69 node.style.webkitTransform = transform;
70 node.style.transform = transform;
71 }
72}
73
74/**
75 * The Slide transition is used by the [Drawer](/material-ui/react-drawer/) component.
76 * It uses [react-transition-group](https://github.com/reactjs/react-transition-group) internally.
77 */
78const Slide = /*#__PURE__*/React.forwardRef(function Slide(props, ref) {
79 const theme = useTheme();
80 const defaultEasing = {
81 enter: theme.transitions.easing.easeOut,
82 exit: theme.transitions.easing.sharp
83 };
84 const defaultTimeout = {
85 enter: theme.transitions.duration.enteringScreen,
86 exit: theme.transitions.duration.leavingScreen
87 };
88 const {
89 addEndListener,
90 appear = true,
91 children,
92 container: containerProp,
93 direction = 'down',
94 easing: easingProp = defaultEasing,
95 in: inProp,
96 onEnter,
97 onEntered,
98 onEntering,
99 onExit,
100 onExited,
101 onExiting,
102 style,
103 timeout = defaultTimeout,
104 // eslint-disable-next-line react/prop-types
105 TransitionComponent = Transition,
106 ...other
107 } = props;
108 const childrenRef = React.useRef(null);
109 const handleRef = useForkRef(getReactElementRef(children), childrenRef, ref);
110 const normalizedTransitionCallback = callback => isAppearing => {
111 if (callback) {
112 // onEnterXxx and onExitXxx callbacks have a different arguments.length value.
113 if (isAppearing === undefined) {
114 callback(childrenRef.current);
115 } else {
116 callback(childrenRef.current, isAppearing);
117 }
118 }
119 };
120 const handleEnter = normalizedTransitionCallback((node, isAppearing) => {
121 setTranslateValue(direction, node, containerProp);
122 reflow(node);
123 if (onEnter) {
124 onEnter(node, isAppearing);
125 }
126 });
127 const handleEntering = normalizedTransitionCallback((node, isAppearing) => {
128 const transitionProps = getTransitionProps({
129 timeout,
130 style,
131 easing: easingProp
132 }, {
133 mode: 'enter'
134 });
135 node.style.webkitTransition = theme.transitions.create('-webkit-transform', {
136 ...transitionProps
137 });
138 node.style.transition = theme.transitions.create('transform', {
139 ...transitionProps
140 });
141 node.style.webkitTransform = 'none';
142 node.style.transform = 'none';
143 if (onEntering) {
144 onEntering(node, isAppearing);
145 }
146 });
147 const handleEntered = normalizedTransitionCallback(onEntered);
148 const handleExiting = normalizedTransitionCallback(onExiting);
149 const handleExit = normalizedTransitionCallback(node => {
150 const transitionProps = getTransitionProps({
151 timeout,
152 style,
153 easing: easingProp
154 }, {
155 mode: 'exit'
156 });
157 node.style.webkitTransition = theme.transitions.create('-webkit-transform', transitionProps);
158 node.style.transition = theme.transitions.create('transform', transitionProps);
159 setTranslateValue(direction, node, containerProp);
160 if (onExit) {
161 onExit(node);
162 }
163 });
164 const handleExited = normalizedTransitionCallback(node => {
165 // No need for transitions when the component is hidden
166 node.style.webkitTransition = '';
167 node.style.transition = '';
168 if (onExited) {
169 onExited(node);
170 }
171 });
172 const handleAddEndListener = next => {
173 if (addEndListener) {
174 // Old call signature before `react-transition-group` implemented `nodeRef`
175 addEndListener(childrenRef.current, next);
176 }
177 };
178 const updatePosition = React.useCallback(() => {
179 if (childrenRef.current) {
180 setTranslateValue(direction, childrenRef.current, containerProp);
181 }
182 }, [direction, containerProp]);
183 React.useEffect(() => {
184 // Skip configuration where the position is screen size invariant.
185 if (inProp || direction === 'down' || direction === 'right') {
186 return undefined;
187 }
188 const handleResize = debounce(() => {
189 if (childrenRef.current) {
190 setTranslateValue(direction, childrenRef.current, containerProp);
191 }
192 });
193 const containerWindow = ownerWindow(childrenRef.current);
194 containerWindow.addEventListener('resize', handleResize);
195 return () => {
196 handleResize.clear();
197 containerWindow.removeEventListener('resize', handleResize);
198 };
199 }, [direction, inProp, containerProp]);
200 React.useEffect(() => {
201 if (!inProp) {
202 // We need to update the position of the drawer when the direction change and
203 // when it's hidden.
204 updatePosition();
205 }
206 }, [inProp, updatePosition]);
207 return /*#__PURE__*/_jsx(TransitionComponent, {
208 nodeRef: childrenRef,
209 onEnter: handleEnter,
210 onEntered: handleEntered,
211 onEntering: handleEntering,
212 onExit: handleExit,
213 onExited: handleExited,
214 onExiting: handleExiting,
215 addEndListener: handleAddEndListener,
216 appear: appear,
217 in: inProp,
218 timeout: timeout,
219 ...other,
220 children: (state, childProps) => {
221 return /*#__PURE__*/React.cloneElement(children, {
222 ref: handleRef,
223 style: {
224 visibility: state === 'exited' && !inProp ? 'hidden' : undefined,
225 ...style,
226 ...children.props.style
227 },
228 ...childProps
229 });
230 }
231 });
232});
233process.env.NODE_ENV !== "production" ? Slide.propTypes /* remove-proptypes */ = {
234 // ┌────────────────────────────── Warning ──────────────────────────────┐
235 // │ These PropTypes are generated from the TypeScript type definitions. │
236 // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
237 // └─────────────────────────────────────────────────────────────────────┘
238 /**
239 * Add a custom transition end trigger. Called with the transitioning DOM
240 * node and a done callback. Allows for more fine grained transition end
241 * logic. Note: Timeouts are still used as a fallback if provided.
242 */
243 addEndListener: PropTypes.func,
244 /**
245 * Perform the enter transition when it first mounts if `in` is also `true`.
246 * Set this to `false` to disable this behavior.
247 * @default true
248 */
249 appear: PropTypes.bool,
250 /**
251 * A single child content element.
252 */
253 children: elementAcceptingRef.isRequired,
254 /**
255 * An HTML element, or a function that returns one.
256 * It's used to set the container the Slide is transitioning from.
257 */
258 container: chainPropTypes(PropTypes.oneOfType([HTMLElementType, PropTypes.func]), props => {
259 if (props.open) {
260 const resolvedContainer = resolveContainer(props.container);
261 if (resolvedContainer && resolvedContainer.nodeType === 1) {
262 const box = resolvedContainer.getBoundingClientRect();
263 if (process.env.NODE_ENV !== 'test' && box.top === 0 && box.left === 0 && box.right === 0 && box.bottom === 0) {
264 return new Error(['MUI: The `container` prop provided to the component is invalid.', 'The anchor element should be part of the document layout.', "Make sure the element is present in the document or that it's not display none."].join('\n'));
265 }
266 } else if (!resolvedContainer || typeof resolvedContainer.getBoundingClientRect !== 'function' || resolvedContainer.contextElement != null && resolvedContainer.contextElement.nodeType !== 1) {
267 return new Error(['MUI: The `container` prop provided to the component is invalid.', 'It should be an HTML element instance.'].join('\n'));
268 }
269 }
270 return null;
271 }),
272 /**
273 * Direction the child node will enter from.
274 * @default 'down'
275 */
276 direction: PropTypes.oneOf(['down', 'left', 'right', 'up']),
277 /**
278 * The transition timing function.
279 * You may specify a single easing or a object containing enter and exit values.
280 * @default {
281 * enter: theme.transitions.easing.easeOut,
282 * exit: theme.transitions.easing.sharp,
283 * }
284 */
285 easing: PropTypes.oneOfType([PropTypes.shape({
286 enter: PropTypes.string,
287 exit: PropTypes.string
288 }), PropTypes.string]),
289 /**
290 * If `true`, the component will transition in.
291 */
292 in: PropTypes.bool,
293 /**
294 * @ignore
295 */
296 onEnter: PropTypes.func,
297 /**
298 * @ignore
299 */
300 onEntered: PropTypes.func,
301 /**
302 * @ignore
303 */
304 onEntering: PropTypes.func,
305 /**
306 * @ignore
307 */
308 onExit: PropTypes.func,
309 /**
310 * @ignore
311 */
312 onExited: PropTypes.func,
313 /**
314 * @ignore
315 */
316 onExiting: PropTypes.func,
317 /**
318 * @ignore
319 */
320 style: PropTypes.object,
321 /**
322 * The duration for the transition, in milliseconds.
323 * You may specify a single timeout for all transitions, or individually with an object.
324 * @default {
325 * enter: theme.transitions.duration.enteringScreen,
326 * exit: theme.transitions.duration.leavingScreen,
327 * }
328 */
329 timeout: PropTypes.oneOfType([PropTypes.number, PropTypes.shape({
330 appear: PropTypes.number,
331 enter: PropTypes.number,
332 exit: PropTypes.number
333 })])
334} : void 0;
335export default Slide;
\No newline at end of file