Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Slide] Convert to function component #15344

Merged
merged 12 commits into from
Apr 19, 2019
243 changes: 116 additions & 127 deletions packages/material-ui/src/Slide/Slide.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import EventListener from 'react-event-listener';
import debounce from 'debounce'; // < 1kb payload overhead when lodash/debounce is > 3kb.
import Transition from 'react-transition-group/Transition';
import ownerWindow from '../utils/ownerWindow';
import { setRef } from '../utils/reactHelpers';
import { useForkRef } from '../utils/reactHelpers';
import withTheme from '../styles/withTheme';
import { duration } from '../styles/transitions';
import { reflow, getTransitionProps } from '../transitions/utils';
Expand All @@ -17,8 +16,7 @@ const GUTTER = 24;
// Translate the node so he can't be seen on the screen.
// Later, we gonna translate back the node to his original location
// with `translate3d(0, 0, 0)`.`
function getTranslateValue(props, node) {
const { direction } = props;
function getTranslateValue(direction, node) {
const rect = node.getBoundingClientRect();

let transform;
Expand Down Expand Up @@ -60,8 +58,8 @@ function getTranslateValue(props, node) {
return `translateY(-${rect.top + rect.height + GUTTER - offsetY}px)`;
}

export function setTranslateValue(props, node) {
const transform = getTranslateValue(props, node);
export function setTranslateValue(direction, node) {
const transform = getTranslateValue(direction, node);

if (transform) {
node.style.webkitTransform = transform;
Expand All @@ -73,65 +71,49 @@ export function setTranslateValue(props, node) {
* The Slide transition is used by the [Drawer](/demos/drawers/) component.
* It uses [react-transition-group](https://github.com/reactjs/react-transition-group) internally.
*/
class Slide extends React.Component {
mounted = false;

constructor() {
super();

if (typeof window !== 'undefined') {
this.handleResize = debounce(() => {
// Skip configuration where the position is screen size invariant.
if (this.props.in || this.props.direction === 'down' || this.props.direction === 'right') {
return;
}

if (this.childDOMNode) {
setTranslateValue(this.props, this.childDOMNode);
}
}, 166); // Corresponds to 10 frames at 60 Hz.
}
}

componentDidMount() {
this.mounted = true;

// state.mounted handle SSR, once the component is mounted, we need
// to properly hide it.
if (!this.props.in) {
// We need to set initial translate values of transition element
// otherwise component will be shown when in=false.
this.updatePosition();
}
}

componentDidUpdate(prevProps) {
if (prevProps.direction !== this.props.direction && !this.props.in) {
// We need to update the position of the drawer when the direction change and
// when it's hidden.
this.updatePosition();
}
}

componentWillUnmount() {
this.handleResize.clear();
}
function Slide(props) {
const {
children,
direction,
in: inProp,
onEnter,
onEntering,
onExit,
onExited,
style,
theme,
timeout,
...other
} = props;

const childrenRef = React.useRef();
/**
* used in cloneElement(children, { ref: handleRef })
*/
const handleOwnRef = ref => {
// #StrictMode ready
childrenRef.current = ReactDOM.findDOMNode(ref);
};
const handleRef = useForkRef(children.ref, handleOwnRef);

handleEnter = node => {
setTranslateValue(this.props, node);
const handleEnter = () => {
const node = childrenRef.current;
setTranslateValue(direction, node);
reflow(node);

if (this.props.onEnter) {
this.props.onEnter(node);
if (onEnter) {
onEnter(node);
}
};

handleEntering = node => {
const { theme } = this.props;

const transitionProps = getTransitionProps(this.props, {
mode: 'enter',
});
const handleEntering = () => {
const node = childrenRef.current;
const transitionProps = getTransitionProps(
{ timeout, style },
{
mode: 'enter',
},
);
node.style.webkitTransition = theme.transitions.create('-webkit-transform', {
...transitionProps,
easing: theme.transitions.easing.easeOut,
Expand All @@ -142,17 +124,19 @@ class Slide extends React.Component {
});
node.style.webkitTransform = 'translate(0, 0)';
node.style.transform = 'translate(0, 0)';
if (this.props.onEntering) {
this.props.onEntering(node);
if (onEntering) {
onEntering(node);
}
};

handleExit = node => {
const { theme } = this.props;

const transitionProps = getTransitionProps(this.props, {
mode: 'exit',
});
const handleExit = () => {
const node = childrenRef.current;
const transitionProps = getTransitionProps(
{ timeout, style },
{
mode: 'exit',
},
);
node.style.webkitTransition = theme.transitions.create('-webkit-transform', {
...transitionProps,
easing: theme.transitions.easing.sharp,
Expand All @@ -161,84 +145,89 @@ class Slide extends React.Component {
...transitionProps,
easing: theme.transitions.easing.sharp,
});
setTranslateValue(this.props, node);
setTranslateValue(direction, node);

if (this.props.onExit) {
this.props.onExit(node);
if (onExit) {
onExit(node);
}
};

handleExited = node => {
const handleExited = () => {
const node = childrenRef.current;
// No need for transitions when the component is hidden
node.style.webkitTransition = '';
node.style.transition = '';

if (this.props.onExited) {
this.props.onExited(node);
if (onExited) {
onExited(node);
}
};

/**
* used in cloneElement(children, { ref: handleRef })
*/
handleRef = ref => {
// #StrictMode ready
this.childDOMNode = ReactDOM.findDOMNode(ref);
setRef(this.props.children.ref, ref);
};
const updatePosition = React.useCallback(() => {
if (childrenRef.current) {
setTranslateValue(direction, childrenRef.current);
}
}, [direction]);

React.useEffect(() => {
// Skip configuration where the position is screen size invariant.
if (!inProp && direction !== 'down' && direction !== 'right') {
const handleResize = debounce(() => {
if (childrenRef.current) {
setTranslateValue(direction, childrenRef.current);
}
}, 166); // Corresponds to 10 frames at 60 Hz.

updatePosition() {
if (this.childDOMNode) {
setTranslateValue(this.props, this.childDOMNode);
window.addEventListener('resize', handleResize);

return () => {
handleResize.clear();
window.removeEventListener('resize', handleResize);
};
}
}

render() {
const {
children,
direction,
in: inProp,
onEnter,
onEntering,
onExit,
onExited,
style,
theme,
...other
} = this.props;

return (
<EventListener target="window" onResize={this.handleResize}>
<Transition
onEnter={this.handleEnter}
onEntering={this.handleEntering}
onExit={this.handleExit}
onExited={this.handleExited}
appear
in={inProp}
{...other}
>
{(state, childProps) => {
return React.cloneElement(children, {
ref: this.handleRef,
style: {
visibility: state === 'exited' && !inProp ? 'hidden' : undefined,
...style,
...children.props.style,
},
...childProps,
});
}}
</Transition>
</EventListener>
);
}
return () => {};
oliviertassinari marked this conversation as resolved.
Show resolved Hide resolved
}, [direction, inProp]);

React.useEffect(() => {
if (!inProp) {
// We need to update the position of the drawer when the direction change and
// when it's hidden.
updatePosition();
}
}, [inProp, updatePosition]);

return (
<Transition
onEnter={handleEnter}
onEntering={handleEntering}
onExit={handleExit}
onExited={handleExited}
appear
in={inProp}
timeout={timeout}
{...other}
>
{(state, childProps) => {
return React.cloneElement(children, {
ref: handleRef,
joshwooding marked this conversation as resolved.
Show resolved Hide resolved
style: {
visibility: state === 'exited' && !inProp ? 'hidden' : undefined,
...style,
...children.props.style,
},
...childProps,
});
}}
</Transition>
);
}

Slide.propTypes = {
/**
* A single child content element. The component used as a child must be able
* to hold a ref.
* A single child content element.
*
* ⚠️The component used as a child [must be able to hold a ref](/guides/composition/#children).
*/
children: PropTypes.element,
/**
Expand Down
Loading