From 2b5b968dd2150c363c8b5428e5f9a5863901e03a Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Fri, 14 Jul 2017 10:59:25 -0400 Subject: [PATCH] Fixes (#113) * Add more documentation * Fix onExited callbacks --- src/CSSTransition.js | 2 +- src/Transition.js | 56 ++++++++++++++++++++++++++++++------------ src/TransitionGroup.js | 34 ++++++++++++++++++------- www/package.json | 15 ++++++----- 4 files changed, 73 insertions(+), 34 deletions(-) diff --git a/src/CSSTransition.js b/src/CSSTransition.js index f12f19d3..a3da236d 100644 --- a/src/CSSTransition.js +++ b/src/CSSTransition.js @@ -191,7 +191,7 @@ class CSSTransition extends React.Component { const { className } = this.getClassNames('exit') this.removeClasses(node, 'appear'); - this.removeClasses(node, 'exit'); + this.removeClasses(node, 'enter'); addClass(node, className) if (this.props.onExit) { diff --git a/src/Transition.js b/src/Transition.js index 5262ad14..b4105a3d 100644 --- a/src/Transition.js +++ b/src/Transition.js @@ -13,7 +13,7 @@ export const EXITING = 'exiting'; /** * The Transition component lets you describe a transition from one component * state to another _over time_ with a simple declarative API. Most commonly - * It's used to animate the mounting and unmounting of Component, but can also + * it's used to animate the mounting and unmounting of a component, but can also * be used to describe in-place transition states as well. * * By default the `Transition` component does not alter the behavior of the @@ -50,6 +50,22 @@ export const EXITING = 'exiting'; * ); * ``` * + * As noted the `Transition` component doesn't _do_ anything by itself to its child component. + * What it does do is track transition states over time so you can adjust you + * component (such as adding styles of classes) as it changes states. + * + * There are 4 main states a Transition can be in: + * - `ENTERING` + * - `ENTERED` + * - `EXITING` + * - `EXITED` + * + * Transition state is toggled via the `in` prop. When `true` the component begins the + * "Enter" stage. During this stage, the component will shift from its current transitions state, + * to `'entering'` for the duration of the transition and then to the `'entered'` stage once + * it's complete. So in the following example: ``, + * the component will immediately shift to `'entering'` and stay there for 500ms and switch to `'entered'`. + * When `in` is `false` the same thing happens except the states are `'exiting'` to `'exited'`. */ class Transition extends React.Component { static contextTypes = { @@ -280,10 +296,7 @@ class Transition extends React.Component { Transition.propTypes = { /** - * Generally a React element to animate, all unknown props on Transition are - * transfered to the **single** child element. - * - * For advanced uses a `function` child can be used instead of a React element. + * A `function` child can be used instead of a React element. * This function is called with the current transition status * ('entering', 'entered', 'exiting', 'exited', 'unmounted'), which can used * to apply context specific props to a component. @@ -307,17 +320,25 @@ Transition.propTypes = { in: PropTypes.bool, /** - * Wait until the first "enter" transition to mount the component (add it to the DOM) + * By default the child component is mounted immediately along with + * the parent `Transition` component. If you want to "lazy mount" the component on the + * first `in={true}` you can set `mountOnEnter`. After the first enter transition the component will stay + * mounted even on exit unless you also specify `unmountOnExit` */ mountOnEnter: PropTypes.bool, /** - * Unmount the component (remove it from the DOM) when it is not shown + * By default the child component is mounted in the DOM after it enteres the `'exited'` state. + * If you'd prefer to completely unmonut the component after it exits, set `unmountOnExit`. */ unmountOnExit: PropTypes.bool, /** - * Enable or disable appear (entering on mount) transitions. + * Normally a component is not transitioned on it's initial mount. If you + * want to transition on the first mount set `appear` to `true`, and the + * component will enter the component. + * + * > Note: there are no specific "appear" states. `apprear` only an additional `enter` transition. */ appear: PropTypes.bool, @@ -363,44 +384,47 @@ Transition.propTypes = { addEndListener: PropTypes.func, /** - * Callback fired before the "entering" status is applied. + * Callback fired before the "entering" status is applied. An extra parameter + * `isAppearing` is supplied to indicate if the enter stage is occuring on the initial mount * - * @type Function(node: HtmlElement, isAppearing: bool) + * @type Function(node: HtmlElement, isAppearing: bool) -> void */ onEnter: PropTypes.func, /** - * Callback fired after the "entering" status is applied. + * Callback fired after the "entering" status is applied. An extra parameter + * `isAppearing` is supplied to indicate if the enter stage is occuring on the initial mount * * @type Function(node: HtmlElement, isAppearing: bool) */ onEntering: PropTypes.func, /** - * Callback fired after the "enter" status is applied. + * Callback fired after the "enter" status is applied. An extra parameter + * `isAppearing` is supplied to indicate if the enter stage is occuring on the initial mount * - * @type Function(node: HtmlElement, isAppearing: bool) + * @type Function(node: HtmlElement, isAppearing: bool) -> void */ onEntered: PropTypes.func, /** * Callback fired before the "exiting" status is applied. * - * @type Function(node: HtmlElement) + * @type Function(node: HtmlElement) -> void */ onExit: PropTypes.func, /** * Callback fired after the "exiting" status is applied. * - * @type Function(node: HtmlElement) + * @type Function(node: HtmlElement) -> void */ onExiting: PropTypes.func, /** * Callback fired after the "exited" status is applied. * - * @type Function(node: HtmlElement) + * @type Function(node: HtmlElement) -> void */ onExited: PropTypes.func, }; diff --git a/src/TransitionGroup.js b/src/TransitionGroup.js index c5f80546..05df7912 100644 --- a/src/TransitionGroup.js +++ b/src/TransitionGroup.js @@ -37,10 +37,23 @@ const propTypes = { * on individual children Transitions. */ exit: PropTypes.bool, + + /** + * You may need to apply reactive updates to a child as it is exiting. + * This is generally done by using `cloneElement` however in the case of an exiting + * child the element has already been removed and not accessible to the consumer. + * + * If you do need to update a child as it leaves you can provide a `childFactory` + * to wrap every child, even the ones that are leaving. + * + * @type Function(child: ReactElement) -> ReactElement + */ + childFactory: PropTypes.func, }; const defaultProps = { component: 'div', + childFactory: child => child, }; /** @@ -111,11 +124,8 @@ class TransitionGroup extends React.Component { // Initial children should all be entering, dependent on appear this.state = { children: getChildMapping(props.children, child => { - const onExited = () => { - if (child.props.onExited) - child.props.onExited(); - - this.handleExited(child.key); + const onExited = (node) => { + this.handleExited(child.key, node, child.props.onExited); } return cloneElement(child, { @@ -156,7 +166,9 @@ class TransitionGroup extends React.Component { if (!isValidElement(child)) return; - const onExited = () => this.handleExited(key); + const onExited = (node) => { + this.handleExited(child.key, node, child.props.onExited); + } const hasPrev = key in prevChildMapping; const hasNext = key in nextChildMapping; @@ -195,20 +207,24 @@ class TransitionGroup extends React.Component { this.setState({ children }); } - handleExited = (key) => { + handleExited = (key, node, originalHandler) => { let currentChildMapping = getChildMapping(this.props.children); if (key in currentChildMapping) return + if (originalHandler) + originalHandler(node) + this.setState((state) => { let children = { ...state.children }; + delete children[key]; return { children }; }); }; render() { - const { component: Component, ...props } = this.props; + const { component: Component, childFactory, ...props } = this.props; const { children } = this.state; delete props.appear; @@ -217,7 +233,7 @@ class TransitionGroup extends React.Component { return ( - {values(children)} + {values(children).map(childFactory)} ); } diff --git a/www/package.json b/www/package.json index a65cee26..622ab5f6 100644 --- a/www/package.json +++ b/www/package.json @@ -13,14 +13,13 @@ "license": "MIT", "dependencies": { "bootstrap": "^4.0.0-alpha.6", - "gatsby": "^1.0.0-beta.7", - "gatsby-link": "^1.0.0-beta.6", - "gatsby-plugin-sass": "^1.0.0-beta.1", - "gatsby-remark-prismjs": "^1.0.0-beta.6", - "gatsby-source-filesystem": "^1.0.0-beta.6", - "gatsby-transformer-react-docgen": "^1.0.0-beta.6", - "gatsby-transformer-remark": "^1.0.0-beta.6", - "graphql-type-json": "^0.1.4", + "gatsby": "^1.0.0", + "gatsby-link": "^1.0.0", + "gatsby-plugin-sass": "^1.0.0", + "gatsby-remark-prismjs": "^1.0.0", + "gatsby-source-filesystem": "^1.0.0", + "gatsby-transformer-react-docgen": "^1.0.0", + "gatsby-transformer-remark": "^1.0.0", "lodash": "^4.17.4" }, "devDependencies": {