UNPKG

8.44 kBJavaScriptView Raw
1import _extends from "@babel/runtime/helpers/esm/extends";
2import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3import * as React from 'react';
4import PropTypes from 'prop-types';
5import * as ReactDOM from 'react-dom';
6import debounce from '../utils/debounce';
7import { Transition } from 'react-transition-group';
8import { elementAcceptingRef } from '@material-ui/utils';
9import useForkRef from '../utils/useForkRef';
10import useTheme from '../styles/useTheme';
11import { duration } from '../styles/transitions';
12import { reflow, getTransitionProps } from '../transitions/utils'; // Translate the node so he can't be seen on the screen.
13// Later, we gonna translate back the node to his original location
14// with `none`.`
15
16function getTranslateValue(direction, node) {
17 const rect = node.getBoundingClientRect();
18 let transform;
19
20 if (node.fakeTransform) {
21 transform = node.fakeTransform;
22 } else {
23 const computedStyle = window.getComputedStyle(node);
24 transform = computedStyle.getPropertyValue('-webkit-transform') || computedStyle.getPropertyValue('transform');
25 }
26
27 let offsetX = 0;
28 let offsetY = 0;
29
30 if (transform && transform !== 'none' && typeof transform === 'string') {
31 const transformValues = transform.split('(')[1].split(')')[0].split(',');
32 offsetX = parseInt(transformValues[4], 10);
33 offsetY = parseInt(transformValues[5], 10);
34 }
35
36 if (direction === 'left') {
37 return `translateX(${window.innerWidth}px) translateX(${offsetX - rect.left}px)`;
38 }
39
40 if (direction === 'right') {
41 return `translateX(-${rect.left + rect.width - offsetX}px)`;
42 }
43
44 if (direction === 'up') {
45 return `translateY(${window.innerHeight}px) translateY(${offsetY - rect.top}px)`;
46 } // direction === 'down'
47
48
49 return `translateY(-${rect.top + rect.height - offsetY}px)`;
50}
51
52export function setTranslateValue(direction, node) {
53 const transform = getTranslateValue(direction, node);
54
55 if (transform) {
56 node.style.webkitTransform = transform;
57 node.style.transform = transform;
58 }
59}
60const defaultTimeout = {
61 enter: duration.enteringScreen,
62 exit: duration.leavingScreen
63};
64/**
65 * The Slide transition is used by the [Drawer](/components/drawers/) component.
66 * It uses [react-transition-group](https://github.com/reactjs/react-transition-group) internally.
67 */
68
69const Slide = /*#__PURE__*/React.forwardRef(function Slide(props, ref) {
70 const {
71 children,
72 direction = 'down',
73 in: inProp,
74 onEnter,
75 onEntered,
76 onEntering,
77 onExit,
78 onExited,
79 onExiting,
80 style,
81 timeout = defaultTimeout,
82 // eslint-disable-next-line react/prop-types
83 TransitionComponent = Transition
84 } = props,
85 other = _objectWithoutPropertiesLoose(props, ["children", "direction", "in", "onEnter", "onEntered", "onEntering", "onExit", "onExited", "onExiting", "style", "timeout", "TransitionComponent"]);
86
87 const theme = useTheme();
88 const childrenRef = React.useRef(null);
89 /**
90 * used in cloneElement(children, { ref: handleRef })
91 */
92
93 const handleOwnRef = React.useCallback(instance => {
94 // #StrictMode ready
95 childrenRef.current = ReactDOM.findDOMNode(instance);
96 }, []);
97 const handleRefIntermediary = useForkRef(children.ref, handleOwnRef);
98 const handleRef = useForkRef(handleRefIntermediary, ref);
99
100 const normalizedTransitionCallback = callback => isAppearing => {
101 if (callback) {
102 // onEnterXxx and onExitXxx callbacks have a different arguments.length value.
103 if (isAppearing === undefined) {
104 callback(childrenRef.current);
105 } else {
106 callback(childrenRef.current, isAppearing);
107 }
108 }
109 };
110
111 const handleEnter = normalizedTransitionCallback((node, isAppearing) => {
112 setTranslateValue(direction, node);
113 reflow(node);
114
115 if (onEnter) {
116 onEnter(node, isAppearing);
117 }
118 });
119 const handleEntering = normalizedTransitionCallback((node, isAppearing) => {
120 const transitionProps = getTransitionProps({
121 timeout,
122 style
123 }, {
124 mode: 'enter'
125 });
126 node.style.webkitTransition = theme.transitions.create('-webkit-transform', _extends({}, transitionProps, {
127 easing: theme.transitions.easing.easeOut
128 }));
129 node.style.transition = theme.transitions.create('transform', _extends({}, transitionProps, {
130 easing: theme.transitions.easing.easeOut
131 }));
132 node.style.webkitTransform = 'none';
133 node.style.transform = 'none';
134
135 if (onEntering) {
136 onEntering(node, isAppearing);
137 }
138 });
139 const handleEntered = normalizedTransitionCallback(onEntered);
140 const handleExiting = normalizedTransitionCallback(onExiting);
141 const handleExit = normalizedTransitionCallback(node => {
142 const transitionProps = getTransitionProps({
143 timeout,
144 style
145 }, {
146 mode: 'exit'
147 });
148 node.style.webkitTransition = theme.transitions.create('-webkit-transform', _extends({}, transitionProps, {
149 easing: theme.transitions.easing.sharp
150 }));
151 node.style.transition = theme.transitions.create('transform', _extends({}, transitionProps, {
152 easing: theme.transitions.easing.sharp
153 }));
154 setTranslateValue(direction, node);
155
156 if (onExit) {
157 onExit(node);
158 }
159 });
160 const handleExited = normalizedTransitionCallback(node => {
161 // No need for transitions when the component is hidden
162 node.style.webkitTransition = '';
163 node.style.transition = '';
164
165 if (onExited) {
166 onExited(node);
167 }
168 });
169 const updatePosition = React.useCallback(() => {
170 if (childrenRef.current) {
171 setTranslateValue(direction, childrenRef.current);
172 }
173 }, [direction]);
174 React.useEffect(() => {
175 // Skip configuration where the position is screen size invariant.
176 if (inProp || direction === 'down' || direction === 'right') {
177 return undefined;
178 }
179
180 const handleResize = debounce(() => {
181 if (childrenRef.current) {
182 setTranslateValue(direction, childrenRef.current);
183 }
184 });
185 window.addEventListener('resize', handleResize);
186 return () => {
187 handleResize.clear();
188 window.removeEventListener('resize', handleResize);
189 };
190 }, [direction, inProp]);
191 React.useEffect(() => {
192 if (!inProp) {
193 // We need to update the position of the drawer when the direction change and
194 // when it's hidden.
195 updatePosition();
196 }
197 }, [inProp, updatePosition]);
198 return /*#__PURE__*/React.createElement(TransitionComponent, _extends({
199 nodeRef: childrenRef,
200 onEnter: handleEnter,
201 onEntered: handleEntered,
202 onEntering: handleEntering,
203 onExit: handleExit,
204 onExited: handleExited,
205 onExiting: handleExiting,
206 appear: true,
207 in: inProp,
208 timeout: timeout
209 }, other), (state, childProps) => {
210 return /*#__PURE__*/React.cloneElement(children, _extends({
211 ref: handleRef,
212 style: _extends({
213 visibility: state === 'exited' && !inProp ? 'hidden' : undefined
214 }, style, children.props.style)
215 }, childProps));
216 });
217});
218process.env.NODE_ENV !== "production" ? Slide.propTypes = {
219 // ----------------------------- Warning --------------------------------
220 // | These PropTypes are generated from the TypeScript type definitions |
221 // | To update them edit the d.ts file and run "yarn proptypes" |
222 // ----------------------------------------------------------------------
223
224 /**
225 * A single child content element.
226 */
227 children: elementAcceptingRef,
228
229 /**
230 * Direction the child node will enter from.
231 */
232 direction: PropTypes.oneOf(['down', 'left', 'right', 'up']),
233
234 /**
235 * If `true`, show the component; triggers the enter or exit animation.
236 */
237 in: PropTypes.bool,
238
239 /**
240 * @ignore
241 */
242 onEnter: PropTypes.func,
243
244 /**
245 * @ignore
246 */
247 onEntered: PropTypes.func,
248
249 /**
250 * @ignore
251 */
252 onEntering: PropTypes.func,
253
254 /**
255 * @ignore
256 */
257 onExit: PropTypes.func,
258
259 /**
260 * @ignore
261 */
262 onExited: PropTypes.func,
263
264 /**
265 * @ignore
266 */
267 onExiting: PropTypes.func,
268
269 /**
270 * @ignore
271 */
272 style: PropTypes.object,
273
274 /**
275 * The duration for the transition, in milliseconds.
276 * You may specify a single timeout for all transitions, or individually with an object.
277 */
278 timeout: PropTypes.oneOfType([PropTypes.number, PropTypes.shape({
279 appear: PropTypes.number,
280 enter: PropTypes.number,
281 exit: PropTypes.number
282 })])
283} : void 0;
284export default Slide;
\No newline at end of file