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, {
221 ownerState,
222 ...restChildProps
223 }) => {
224 return /*#__PURE__*/React.cloneElement(children, {
225 ref: handleRef,
226 style: {
227 visibility: state === 'exited' && !inProp ? 'hidden' : undefined,
228 ...style,
229 ...children.props.style
230 },
231 ...restChildProps
232 });
233 }
234 });
235});
236process.env.NODE_ENV !== "production" ? Slide.propTypes /* remove-proptypes */ = {
237 // ┌────────────────────────────── Warning ──────────────────────────────┐
238 // │ These PropTypes are generated from the TypeScript type definitions. │
239 // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
240 // └─────────────────────────────────────────────────────────────────────┘
241 /**
242 * Add a custom transition end trigger. Called with the transitioning DOM
243 * node and a done callback. Allows for more fine grained transition end
244 * logic. Note: Timeouts are still used as a fallback if provided.
245 */
246 addEndListener: PropTypes.func,
247 /**
248 * Perform the enter transition when it first mounts if `in` is also `true`.
249 * Set this to `false` to disable this behavior.
250 * @default true
251 */
252 appear: PropTypes.bool,
253 /**
254 * A single child content element.
255 */
256 children: elementAcceptingRef.isRequired,
257 /**
258 * An HTML element, or a function that returns one.
259 * It's used to set the container the Slide is transitioning from.
260 */
261 container: chainPropTypes(PropTypes.oneOfType([HTMLElementType, PropTypes.func]), props => {
262 if (props.open) {
263 const resolvedContainer = resolveContainer(props.container);
264 if (resolvedContainer && resolvedContainer.nodeType === 1) {
265 const box = resolvedContainer.getBoundingClientRect();
266 if (process.env.NODE_ENV !== 'test' && box.top === 0 && box.left === 0 && box.right === 0 && box.bottom === 0) {
267 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'));
268 }
269 } else if (!resolvedContainer || typeof resolvedContainer.getBoundingClientRect !== 'function' || resolvedContainer.contextElement != null && resolvedContainer.contextElement.nodeType !== 1) {
270 return new Error(['MUI: The `container` prop provided to the component is invalid.', 'It should be an HTML element instance.'].join('\n'));
271 }
272 }
273 return null;
274 }),
275 /**
276 * Direction the child node will enter from.
277 * @default 'down'
278 */
279 direction: PropTypes.oneOf(['down', 'left', 'right', 'up']),
280 /**
281 * The transition timing function.
282 * You may specify a single easing or a object containing enter and exit values.
283 * @default {
284 * enter: theme.transitions.easing.easeOut,
285 * exit: theme.transitions.easing.sharp,
286 * }
287 */
288 easing: PropTypes.oneOfType([PropTypes.shape({
289 enter: PropTypes.string,
290 exit: PropTypes.string
291 }), PropTypes.string]),
292 /**
293 * If `true`, the component will transition in.
294 */
295 in: PropTypes.bool,
296 /**
297 * @ignore
298 */
299 onEnter: PropTypes.func,
300 /**
301 * @ignore
302 */
303 onEntered: PropTypes.func,
304 /**
305 * @ignore
306 */
307 onEntering: PropTypes.func,
308 /**
309 * @ignore
310 */
311 onExit: PropTypes.func,
312 /**
313 * @ignore
314 */
315 onExited: PropTypes.func,
316 /**
317 * @ignore
318 */
319 onExiting: PropTypes.func,
320 /**
321 * @ignore
322 */
323 style: PropTypes.object,
324 /**
325 * The duration for the transition, in milliseconds.
326 * You may specify a single timeout for all transitions, or individually with an object.
327 * @default {
328 * enter: theme.transitions.duration.enteringScreen,
329 * exit: theme.transitions.duration.leavingScreen,
330 * }
331 */
332 timeout: PropTypes.oneOfType([PropTypes.number, PropTypes.shape({
333 appear: PropTypes.number,
334 enter: PropTypes.number,
335 exit: PropTypes.number
336 })])
337} : void 0;
338export default Slide;
\No newline at end of file