Created
April 5, 2022 15:11
-
-
Save koba04/5575ded08b7ece5ca5326c797949d653 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import PropTypes from 'prop-types'; | |
import React from 'react'; | |
import type { ReactElement } from 'react'; | |
import ReactDOM from 'react-dom'; | |
import TransitionGroup from './TransitionGroup'; | |
import type { Props as TransitionProps } from './Transition'; | |
type Props = Omit<TransitionProps, 'children'> & { | |
children: [ReactElement<TransitionProps>, ReactElement<TransitionProps>]; | |
}; | |
type HandlerNames = | |
| 'onEnter' | |
| 'onEntering' | |
| 'onEntered' | |
| 'onExit' | |
| 'onExiting' | |
| 'onExited'; | |
type Args = [HTMLElement | boolean, boolean | undefined]; | |
type ChildElement = ReactElement<TransitionProps>; | |
type ReplaceElements = [ChildElement, ChildElement]; | |
/** | |
* The `<ReplaceTransition>` component is a specialized `Transition` component | |
* that animates between two children. | |
* | |
* ```jsx | |
* <ReplaceTransition in> | |
* <Fade><div>I appear first</div></Fade> | |
* <Fade><div>I replace the above</div></Fade> | |
* </ReplaceTransition> | |
* ``` | |
*/ | |
class ReplaceTransition extends React.Component<Props> { | |
handleEnter = (...args: Args) => this.handleLifecycle('onEnter', 0, args); | |
handleEntering = (...args: Args) => | |
this.handleLifecycle('onEntering', 0, args); | |
handleEntered = (...args: Args) => this.handleLifecycle('onEntered', 0, args); | |
handleExit = (...args: Args) => this.handleLifecycle('onExit', 1, args); | |
handleExiting = (...args: Args) => this.handleLifecycle('onExiting', 1, args); | |
handleExited = (...args: Args) => this.handleLifecycle('onExited', 1, args); | |
handleLifecycle(handler: HandlerNames, idx: number, originalArgs: Args) { | |
const { children } = this.props; | |
// @ts-expect-error FIXME: Type 'string' is not assignable to type 'ReactElement<Props, string | JSXElementConstructor<any>>'.ts(2322) | |
const child: ChildElement = React.Children.toArray(children)[idx]; | |
// @ts-expect-error FIXME: Type 'false' is not assignable to type '(((boolean | HTMLElement) & (HTMLElement | undefined)) & (HTMLElement | undefined)) & (HTMLElement | undefined)'.ts(2345) | |
if (child.props[handler]) child.props[handler](...originalArgs); | |
if (this.props[handler]) { | |
const maybeNode = child.props.nodeRef | |
? undefined | |
: ReactDOM.findDOMNode(this); | |
// @ts-expect-error FIXME: Argument of type 'Element | Text | null | undefined' is not assignable to parameter of type 'HTMLElement'.ts(2769) | |
this.props[handler](maybeNode); | |
} | |
} | |
render() { | |
const { children, in: inProp, ...props } = this.props; | |
// @ts-expect-error FIXME: Target requires 2 element(s) but source may have fewer.ts(2322) | |
const [first, second]: ReplaceElements = React.Children.toArray(children); | |
// @ts-expect-error The operand of a 'delete' operator must be optional.ts(2790) | |
delete props.onEnter; | |
// @ts-expect-error | |
delete props.onEntering; | |
// @ts-expect-error | |
delete props.onEntered; | |
// @ts-expect-error | |
delete props.onExit; | |
// @ts-expect-error | |
delete props.onExiting; | |
// @ts-expect-error | |
delete props.onExited; | |
return ( | |
<TransitionGroup {...props}> | |
{inProp | |
? React.cloneElement(first, { | |
key: 'first', | |
onEnter: this.handleEnter, | |
onEntering: this.handleEntering, | |
onEntered: this.handleEntered, | |
}) | |
: React.cloneElement(second, { | |
key: 'second', | |
onEnter: this.handleExit, | |
onEntering: this.handleExiting, | |
onEntered: this.handleExited, | |
})} | |
</TransitionGroup> | |
); | |
} | |
} | |
// @ts-expect-error To make TS migration diffs minimum, I've left propTypes here instead of defining a static property | |
ReplaceTransition.propTypes = { | |
in: PropTypes.bool.isRequired, | |
children(props: any, propName: HandlerNames) { | |
if (React.Children.count(props[propName]) !== 2) | |
return new Error( | |
`"${propName}" must be exactly two transition components.` | |
); | |
return null; | |
}, | |
}; | |
export default ReplaceTransition; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment