From ed373e6c0b87109e0e12ac1a018443ea085239bf Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Tue, 6 Dec 2016 23:10:36 +0000
Subject: [PATCH 01/22] Full rewrite.
---
.eslintrc | 2 +-
src/ReactCSSTransitionReplace.jsx | 264 ++++++++++++------------------
2 files changed, 109 insertions(+), 157 deletions(-)
diff --git a/.eslintrc b/.eslintrc
index cd43ac6..8f23b45 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -124,7 +124,7 @@
*/
"indent": [1, 2, {"SwitchCase": 1}], // http://eslint.org/docs/rules/indent
"brace-style": [2, // http://eslint.org/docs/rules/brace-style
- "stroustrup", {
+ "1tbs", {
"allowSingleLine": true
}],
"quotes": [
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index ea9ea59..666ea1a 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -30,8 +30,7 @@ function createTransitionTimeoutPropValidator(transitionType) {
+ 'https://fb.me/react-animation-transition-group-timeout for more ' + 'information.')
// If the duration isn't a number
- }
- else if (typeof props[timeoutPropName] != 'number') {
+ } else if (typeof props[timeoutPropName] != 'number') {
return new Error(timeoutPropName + ' must be a number (in milliseconds)')
}
}
@@ -65,7 +64,6 @@ export default class ReactCSSTransitionReplace extends React.Component {
transitionEnterTimeout: createTransitionTimeoutPropValidator('Enter'),
transitionLeaveTimeout: createTransitionTimeoutPropValidator('Leave'),
overflowHidden: PropTypes.bool,
- changeWidth: PropTypes.bool,
}
static defaultProps = {
@@ -74,23 +72,25 @@ export default class ReactCSSTransitionReplace extends React.Component {
transitionLeave: true,
overflowHidden: true,
component: 'span',
- changeWidth: false,
+ childComponent: 'span',
}
state = {
+ currentKey: '1',
currentChild: this.props.children ? React.Children.only(this.props.children) : undefined,
- currentChildKey: this.props.children ? '1' : '',
- nextChild: undefined,
- activeHeightTransition: false,
- nextChildKey: '',
+ prevChildren: {},
height: null,
- width: null,
- isLeaving: false,
+ }
+
+ componentWillMount() {
+ this.shouldEnterCurrent = false
+ this.keysToLeave = []
+ this.transitioningKeys = {}
}
componentDidMount() {
if (this.props.transitionAppear && this.state.currentChild) {
- this.appearCurrent()
+ this.performAppear(this.state.currentKey)
}
}
@@ -99,152 +99,115 @@ export default class ReactCSSTransitionReplace extends React.Component {
}
componentWillReceiveProps(nextProps) {
- // Setting false indicates that the child has changed, but it is a removal so there is no next child.
- const nextChild = nextProps.children ? React.Children.only(nextProps.children) : false
- const currentChild = this.state.currentChild
-
- // Avoid silencing the transition when this.state.nextChild exists because it means that there’s
- // already a transition ongoing that has to be replaced.
- if (currentChild && nextChild && nextChild.key === currentChild.key && !this.state.nextChild) {
- // Nothing changed, but we are re-rendering so update the currentChild.
- return this.setState({
- currentChild: nextChild,
- })
- }
+ const nextChild = nextProps.children ? React.Children.only(nextProps.children) : null
+ const {currentChild} = this.state
- if (!currentChild && !nextChild && this.state.nextChild) {
- // The container was empty before and the entering element is being removed again while
- // transitioning in. Since a CSS transition can't be reversed cleanly midway the height
- // is just forced back to zero immediately and the child removed.
- return this.cancelTransition()
+ if ((!currentChild && !nextChild) || (currentChild && nextChild && currentChild.key === nextChild.key)) {
+ return
}
const {state} = this
+ const {currentKey} = state
- // When transitionLeave is set to false, refs.curr does not exist when refs.next is being
- // transitioned into existence. When another child is set for this component at the point
- // where only refs.next exists, we want to use the width/height of refs.next instead of
- // refs.curr.
- const ref = this.refs.curr || this.refs.next
-
- // Set the next child to start the transition, and set the current height.
- this.setState({
- nextChild,
- activeHeightTransition: false,
- nextChildKey: state.currentChildKey ? String(Number(state.currentChildKey) + 1) : '1',
- height: state.currentChild ? ReactDOM.findDOMNode(ref).offsetHeight : 0,
- width: state.currentChild && this.props.changeWidth ? ReactDOM.findDOMNode(ref).offsetWidth : null,
- })
-
- // Enqueue setting the next height to trigger the height transition.
- this.enqueueHeightTransition(nextChild)
- }
+ const nextState = {
+ currentKey: String(Number(currentKey) + 1),
+ currentChild: nextChild,
+ height: 0,
+ }
- componentDidUpdate() {
- if (!this.isTransitioning && !this.state.isLeaving) {
- const {currentChild, nextChild} = this.state
+ if (nextChild) {
+ this.shouldEnterCurrent = true
+ }
- if (currentChild && (nextChild || nextChild === false || nextChild === null) && this.props.transitionLeave) {
- this.leaveCurrent()
+ if (currentChild) {
+ nextState.height = ReactDOM.findDOMNode(this.refs[currentKey]).offsetHeight
+ nextState.prevChildren = {
+ ...state.prevChildren,
+ [currentKey]: currentChild,
}
- if (nextChild) {
- this.enterNext()
+ if (!this.transitioningKeys[currentKey]) {
+ this.keysToLeave.push(currentKey)
}
}
+
+ this.setState(nextState)
}
- enqueueHeightTransition(nextChild, tickCount = 0) {
- this.timeout = setTimeout(() => {
- if (!nextChild) {
- return this.setState({
- activeHeightTransition: true,
- height: 0,
- width: this.props.changeWidth ? 0 : null,
- })
- }
+ componentDidUpdate() {
+ if (this.shouldEnterCurrent) {
+ this.shouldEnterCurrent = false
+ this.performEnter(this.state.currentKey)
+ }
- const nextNode = ReactDOM.findDOMNode(this.refs.next)
- if (nextNode) {
- this.setState({
- activeHeightTransition: true,
- height: nextNode.offsetHeight,
- width: this.props.changeWidth ? nextNode.offsetWidth : null,
- })
- }
- else {
- // The DOM hasn't rendered the entering element yet, so wait another tick.
- // Getting stuck in a loop shouldn't happen, but it's better to be safe.
- if (tickCount < 10) {
- this.enqueueHeightTransition(nextChild, tickCount + 1)
- }
- }
- }, TICK)
+ const keysToLeave = this.keysToLeave
+ this.keysToLeave = []
+ keysToLeave.forEach(this.performLeave)
}
- appearCurrent() {
- this.refs.curr.componentWillAppear(this._handleDoneAppearing)
- this.isTransitioning = true
+ performAppear(key) {
+ this.transitioningKeys[key] = true
+ this.refs[key].componentWillAppear(this.handleDoneAppearing.bind(this, key))
}
- _handleDoneAppearing = () => {
- this.isTransitioning = false
+ handleDoneAppearing = (key) => {
+ delete this.transitioningKeys[key]
+ if (key !== this.state.currentKey) {
+ // This child was removed before it had fully appeared. Remove it.
+ this.performLeave(key)
+ }
}
- enterNext() {
- this.refs.next.componentWillEnter(this._handleDoneEntering)
- this.isTransitioning = true
+ performEnter(key) {
+ this.transitioningKeys[key] = true
+ this.refs[key].componentWillEnter(this.handleDoneEntering.bind(this, key))
+ this.enqueueHeightTransition()
}
- _handleDoneEntering = () => {
- const {state} = this
-
- this.isTransitioning = false
- this.setState({
- currentChild: state.nextChild,
- currentChildKey: state.nextChildKey,
- activeHeightTransition: false,
- nextChild: undefined,
- nextChildKey: '',
- height: null,
- width: null,
- })
+ handleDoneEntering(key) {
+ delete this.transitioningKeys[key]
+ if (key === this.state.currentKey) {
+ // The current child has finished entering so the height transition is also cleared.
+ this.setState({height: null})
+ } else {
+ // This child was removed before it had fully appeared. Remove it.
+ this.performLeave(key)
+ }
}
- leaveCurrent() {
- this.refs.curr.componentWillLeave(this._handleDoneLeaving)
- this.isTransitioning = true
- this.setState({isLeaving: true})
+ performLeave = (key) => {
+ this.transitioningKeys[key] = true
+ this.refs[key].componentWillLeave(this.handleDoneLeaving.bind(this, key))
+ if (!this.state.currentChild) {
+ // The enter transition dominates, but if there is no
+ // entering component the height is set to zero.
+ this.enqueueHeightTransition()
+ }
}
- // When the leave transition time-out expires the animation classes are removed, so the
- // element must be removed from the DOM if the enter transition is still in progress.
- _handleDoneLeaving = () => {
- if (this.isTransitioning) {
- const state = {currentChild: undefined, isLeaving: false}
+ handleDoneLeaving(key) {
+ delete this.transitioningKeys[key]
- if (!this.state.nextChild) {
- this.isTransitioning = false
- state.height = null
- state.width = null
- }
+ const nextState = {prevChildren: {...this.state.prevChildren}}
+ delete nextState.prevChildren[key]
- this.setState(state)
+ if (!this.state.currentChild) {
+ nextState.height = null
}
+
+ this.setState(nextState)
}
- cancelTransition() {
- this.isTransitioning = false
- clearTimeout(this.timeout)
- return this.setState({
- nextChild: undefined,
- activeHeightTransition: false,
- nextChildKey: '',
- height: null,
- width: null,
- })
+ enqueueHeightTransition() {
+ const {state} = this
+ this.timeout = setTimeout(() => {
+ if (!state.currentChild) {
+ return this.setState({height: 0})
+ }
+ this.setState({height: ReactDOM.findDOMNode(this.refs[state.currentKey]).offsetHeight})
+ }, TICK)
}
- _wrapChild(child, moreProps) {
+ wrapChild(child, moreProps) {
let transitionName = this.props.transitionName
if (typeof transitionName == 'object' && transitionName !== null) {
@@ -268,42 +231,22 @@ export default class ReactCSSTransitionReplace extends React.Component {
}
render() {
- const {currentChild, currentChildKey, nextChild, nextChildKey, height, width, isLeaving, activeHeightTransition} = this.state
+ const {currentKey, currentChild, prevChildren, height} = this.state
const childrenToRender = []
const {
- overflowHidden, transitionName, changeWidth, component,
+ overflowHidden, transitionName, component, childComponent,
transitionAppear, transitionEnter, transitionLeave,
transitionAppearTimeout, transitionEnterTimeout, transitionLeaveTimeout,
...containerProps
} = this.props
- if (currentChild && !nextChild && !transitionLeave || currentChild && transitionLeave) {
- childrenToRender.push(
- React.createElement(
- 'span',
- {key: currentChildKey},
- this._wrapChild(
- typeof currentChild.type == 'string' ? currentChild : React.cloneElement(currentChild, {isLeaving}),
- {ref: 'curr'})
- )
- )
- }
-
-
if (height !== null) {
const heightClassName = (typeof transitionName == 'object' && transitionName !== null)
? transitionName.height || ''
: `${transitionName}-height`
- // Similarly to ReactCSSTransitionGroup, adding `-height-active` suffix to the
- // container when we are transitioning height.
- const activeHeightClassName = (nextChild && activeHeightTransition && heightClassName)
- ? `${heightClassName}-active`
- : ''
-
- containerProps.className = `${containerProps.className || ''} ${heightClassName} ${activeHeightClassName}`
-
+ containerProps.className = `${containerProps.className || ''} ${heightClassName}`
containerProps.style = {
...containerProps.style,
position: 'relative',
@@ -314,16 +257,13 @@ export default class ReactCSSTransitionReplace extends React.Component {
if (overflowHidden) {
containerProps.style.overflow = 'hidden'
}
-
- if (changeWidth) {
- containerProps.style.width = width
- }
}
- if (nextChild) {
+ Object.keys(prevChildren).forEach(key => {
childrenToRender.push(
- React.createElement('span',
+ React.createElement(childComponent,
{
+ key,
style: {
position: 'absolute',
top: 0,
@@ -331,9 +271,21 @@ export default class ReactCSSTransitionReplace extends React.Component {
right: 0,
bottom: 0,
},
- key: nextChildKey,
},
- this._wrapChild(nextChild, {ref: 'next'})
+ this.wrapChild(
+ typeof prevChildren[key].type == 'string'
+ ? prevChildren[key]
+ : React.cloneElement(prevChildren[key], {isLeaving: true}),
+ {ref: key})
+ )
+ )
+ })
+
+ if (currentChild) {
+ childrenToRender.push(
+ React.createElement(childComponent,
+ {key: currentKey},
+ this.wrapChild(currentChild, {ref: currentKey})
)
)
}
From 0249594d8fddb4b01df3b1179ba05c8d7da8fbc3 Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Mon, 27 Feb 2017 23:22:44 +0000
Subject: [PATCH 02/22] Add third image to the carousel animation.
---
demo/assets/transitions.css | 6 +++---
demo/components/ContentSwapper.jsx | 7 ++++---
demo/components/Demo.jsx | 7 ++++---
3 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/demo/assets/transitions.css b/demo/assets/transitions.css
index 2381a11..0fbdda9 100644
--- a/demo/assets/transitions.css
+++ b/demo/assets/transitions.css
@@ -49,7 +49,7 @@
/* Carousel-like transition */
.carousel-swap-leave {
- transition: transform .3s ease-in-out;
+ transition: transform 2s ease-in-out;
transform: translate(0, 0);
}
.carousel-swap-leave-active {
@@ -57,7 +57,7 @@
}
.carousel-swap-enter {
- transition: transform .3s ease-in-out;
+ transition: transform 2s ease-in-out;
transform: translate(100%, 0);
}
.carousel-swap-enter-active {
@@ -65,7 +65,7 @@
}
.carousel-swap-height {
- transition: height .3s ease-in-out;
+ transition: height 2s ease-in-out;
}
diff --git a/demo/components/ContentSwapper.jsx b/demo/components/ContentSwapper.jsx
index a71a2be..0324eb4 100644
--- a/demo/components/ContentSwapper.jsx
+++ b/demo/components/ContentSwapper.jsx
@@ -4,10 +4,11 @@ import ReactCSSTransitionReplace from '../../src/ReactCSSTransitionReplace.jsx'
class ContentSwapper extends React.Component {
- state = {swapped: false}
+ state = {index: 0}
handleClick = () => {
- this.setState({swapped: !this.state.swapped})
+ const index = this.state.index + 1
+ this.setState({index: index >= React.Children.count(this.props.children) ? 0 : index})
}
render() {
@@ -18,7 +19,7 @@ class ContentSwapper extends React.Component {
return (
- {this.state.swapped ? content[1] : content[0]}
+ {content[this.state.index]}
)
}
diff --git a/demo/components/Demo.jsx b/demo/components/Demo.jsx
index 050ab15..d0a179c 100644
--- a/demo/components/Demo.jsx
+++ b/demo/components/Demo.jsx
@@ -71,10 +71,11 @@ class Demo extends React.Component {
{''}
.
-
-
-
+
+
+
Add/Remove Content
From 4ee94c7c7a0bfc37fb0df97e75c2e1fbc3afbb9d Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Mon, 27 Feb 2017 23:23:06 +0000
Subject: [PATCH 03/22] Not opening the demo in a browser automatically.
---
gulpfile.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/gulpfile.js b/gulpfile.js
index 35acdfc..e92a351 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -54,6 +54,7 @@ gulp.task('demo:bundleAndWatch', function() {
gulp.task('demo', ['demo:bundleAndWatch'], function() {
browserSync.init({
browser: ['google chrome'],
+ open: false,
notify: false,
server: {
baseDir: "demo/assets"
From bb60952f4275b82168548c92f0c68683a4549ba1 Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Wed, 8 Mar 2017 21:49:19 +0000
Subject: [PATCH 04/22] Update docs to include childComponent.
---
README.md | 6 +++++-
package.json | 2 +-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index c890d37..26b3b31 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ Using `react-css-transition-replace` provides two distinct benefits:
Animations are fully configurable with CSS, including having the entering component wait to enter until
the leaving component's animation completes. Following suit with the
-[React.js API](https://facebook.github.io/react/docs/animation.html#getting-started) the one caveat is
+[React.js API](https://facebook.github.io/react/docs/animation.html) the one caveat is
that the transition duration must be specified in JavaScript as well as CSS.
[Live Examples](http://marnusw.github.io/react-css-transition-replace) |
@@ -56,6 +56,10 @@ It is also possible to remove the child component (i.e. leave `ReactCSSTransitio
which will animate the `height` going to zero along with the `leave` transition. Similarly, a single child
can be added to an empty `ReactCSSTransitionReplace`, triggering the inverse animation.
+By default a `span` is rendered as a wrapper of the child components. Each child is also wrapped in a `span`
+used in the positioning of the actual rendered child. These can be overridden with the `component` and
+`childComponent` props respectively.
+
### Cross-fading two components
The `ReactCSSTransitionReplace` component is used exactly like its `ReactCSSTransitionGroup` counterpart:
diff --git a/package.json b/package.json
index 003401f..4c3b11f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-css-transition-replace",
- "version": "2.2.1",
+ "version": "3.0.0-beta.1",
"description": "A React component to animate replacing one element with another.",
"main": "lib/ReactCSSTransitionReplace.js",
"repository": {
From ba38fe063214dc1b979f51689cacbb3a658d4fd9 Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sat, 29 Apr 2017 20:18:16 +0100
Subject: [PATCH 05/22] Define prop types using the util in
react-transition-group.
---
src/ReactCSSTransitionReplace.jsx | 45 +++++--------------------------
1 file changed, 6 insertions(+), 39 deletions(-)
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index 666ea1a..06da7cf 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -9,60 +9,27 @@ import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
import ReactCSSTransitionGroupChild from 'react-transition-group/CSSTransitionGroupChild'
+import { nameShape, transitionTimeout } from 'react-transition-group/utils/PropTypes'
+
const reactCSSTransitionGroupChild = React.createFactory(ReactCSSTransitionGroupChild)
const TICK = 17
-function createTransitionTimeoutPropValidator(transitionType) {
- const timeoutPropName = 'transition' + transitionType + 'Timeout'
- const enabledPropName = 'transition' + transitionType
-
- return function(props) {
- // If the transition is enabled
- if (props[enabledPropName]) {
- // If no timeout duration is provided
- if (!props[timeoutPropName]) {
- return new Error(timeoutPropName + ' wasn\'t supplied to ReactCSSTransitionReplace: '
- + 'this can cause unreliable animations and won\'t be supported in '
- + 'a future version of React. See '
- + 'https://fb.me/react-animation-transition-group-timeout for more ' + 'information.')
-
- // If the duration isn't a number
- } else if (typeof props[timeoutPropName] != 'number') {
- return new Error(timeoutPropName + ' must be a number (in milliseconds)')
- }
- }
- }
-}
-
export default class ReactCSSTransitionReplace extends React.Component {
static displayName = 'ReactCSSTransitionReplace'
static propTypes = {
- transitionName: PropTypes.oneOfType([PropTypes.string, PropTypes.shape({
- enter: PropTypes.string,
- leave: PropTypes.string,
- active: PropTypes.string,
- height: PropTypes.string,
- }), PropTypes.shape({
- enter: PropTypes.string,
- enterActive: PropTypes.string,
- leave: PropTypes.string,
- leaveActive: PropTypes.string,
- appear: PropTypes.string,
- appearActive: PropTypes.string,
- height: PropTypes.string,
- })]).isRequired,
+ transitionName: nameShape.isRequired,
transitionAppear: PropTypes.bool,
transitionEnter: PropTypes.bool,
transitionLeave: PropTypes.bool,
- transitionAppearTimeout: createTransitionTimeoutPropValidator('Appear'),
- transitionEnterTimeout: createTransitionTimeoutPropValidator('Enter'),
- transitionLeaveTimeout: createTransitionTimeoutPropValidator('Leave'),
+ transitionAppearTimeout: transitionTimeout('Appear'),
+ transitionEnterTimeout: transitionTimeout('Enter'),
+ transitionLeaveTimeout: transitionTimeout('Leave'),
overflowHidden: PropTypes.bool,
}
From 64ae270b79489737317d3c5d2e3c93457ed8b4a3 Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 30 Apr 2017 00:00:22 +0100
Subject: [PATCH 06/22] Maintain component callback refs by not overwriting
with string refs.
---
package.json | 4 +-
src/ReactCSSTransitionReplace.jsx | 73 ++++++++++++++++++++++---------
2 files changed, 56 insertions(+), 21 deletions(-)
diff --git a/package.json b/package.json
index 4c3b11f..59f1873 100644
--- a/package.json
+++ b/package.json
@@ -14,8 +14,10 @@
"prepublish": "npm run build"
},
"dependencies": {
+ "chain-function": "^1.0.0",
"prop-types": "^15.5.6",
- "react-transition-group": "^1.1.1"
+ "react-transition-group": "^1.1.1",
+ "warning": "^3.0.0"
},
"peerDependencies": {
"react": "^15.0.0",
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index 06da7cf..a59ed44 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -7,6 +7,8 @@
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
+import chain from 'chain-function'
+import warning from 'warning'
import ReactCSSTransitionGroupChild from 'react-transition-group/CSSTransitionGroupChild'
import { nameShape, transitionTimeout } from 'react-transition-group/utils/PropTypes'
@@ -42,11 +44,17 @@ export default class ReactCSSTransitionReplace extends React.Component {
childComponent: 'span',
}
- state = {
- currentKey: '1',
- currentChild: this.props.children ? React.Children.only(this.props.children) : undefined,
- prevChildren: {},
- height: null,
+ constructor(props, context) {
+ super(props, context)
+
+ this.childRefs = Object.create(null)
+
+ this.state = {
+ currentKey: '1',
+ currentChild: this.props.children ? React.Children.only(this.props.children) : undefined,
+ prevChildren: {},
+ height: null,
+ }
}
componentWillMount() {
@@ -73,8 +81,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
return
}
- const {state} = this
- const {currentKey} = state
+ const {currentKey, prevChildren} = this.state
const nextState = {
currentKey: String(Number(currentKey) + 1),
@@ -87,9 +94,9 @@ export default class ReactCSSTransitionReplace extends React.Component {
}
if (currentChild) {
- nextState.height = ReactDOM.findDOMNode(this.refs[currentKey]).offsetHeight
+ nextState.height = ReactDOM.findDOMNode(this.childRefs[currentKey]).offsetHeight
nextState.prevChildren = {
- ...state.prevChildren,
+ ...prevChildren,
[currentKey]: currentChild,
}
if (!this.transitioningKeys[currentKey]) {
@@ -113,7 +120,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
performAppear(key) {
this.transitioningKeys[key] = true
- this.refs[key].componentWillAppear(this.handleDoneAppearing.bind(this, key))
+ this.childRefs[key].componentWillAppear(this.handleDoneAppearing.bind(this, key))
}
handleDoneAppearing = (key) => {
@@ -126,7 +133,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
performEnter(key) {
this.transitioningKeys[key] = true
- this.refs[key].componentWillEnter(this.handleDoneEntering.bind(this, key))
+ this.childRefs[key].componentWillEnter(this.handleDoneEntering.bind(this, key))
this.enqueueHeightTransition()
}
@@ -143,7 +150,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
performLeave = (key) => {
this.transitioningKeys[key] = true
- this.refs[key].componentWillLeave(this.handleDoneLeaving.bind(this, key))
+ this.childRefs[key].componentWillLeave(this.handleDoneLeaving.bind(this, key))
if (!this.state.currentChild) {
// The enter transition dominates, but if there is no
// entering component the height is set to zero.
@@ -156,6 +163,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
const nextState = {prevChildren: {...this.state.prevChildren}}
delete nextState.prevChildren[key]
+ delete this.childRefs[key]
if (!this.state.currentChild) {
nextState.height = null
@@ -170,14 +178,14 @@ export default class ReactCSSTransitionReplace extends React.Component {
if (!state.currentChild) {
return this.setState({height: 0})
}
- this.setState({height: ReactDOM.findDOMNode(this.refs[state.currentKey]).offsetHeight})
+ this.setState({height: ReactDOM.findDOMNode(this.childRefs[state.currentKey]).offsetHeight})
}, TICK)
}
wrapChild(child, moreProps) {
let transitionName = this.props.transitionName
- if (typeof transitionName == 'object' && transitionName !== null) {
+ if (typeof transitionName === 'object' && transitionName !== null) {
transitionName = {...transitionName}
delete transitionName.height
}
@@ -209,7 +217,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
} = this.props
if (height !== null) {
- const heightClassName = (typeof transitionName == 'object' && transitionName !== null)
+ const heightClassName = (typeof transitionName === 'object' && transitionName !== null)
? transitionName.height || ''
: `${transitionName}-height`
@@ -227,6 +235,12 @@ export default class ReactCSSTransitionReplace extends React.Component {
}
Object.keys(prevChildren).forEach(key => {
+ const child = prevChildren[key]
+ const isCallbackRef = typeof child.ref !== 'string'
+ warning(isCallbackRef,
+ 'string refs are not supported on children of ReactCSSTransitionReplace and will be ignored. ' +
+ 'Please use a callback ref instead: https://facebook.github.io/react/docs/refs-and-the-dom.html#the-ref-callback-attribute')
+
childrenToRender.push(
React.createElement(childComponent,
{
@@ -240,19 +254,38 @@ export default class ReactCSSTransitionReplace extends React.Component {
},
},
this.wrapChild(
- typeof prevChildren[key].type == 'string'
- ? prevChildren[key]
- : React.cloneElement(prevChildren[key], {isLeaving: true}),
- {ref: key})
+ typeof child.type !== 'string'
+ ? React.cloneElement(child, {isLeaving: true})
+ : child,
+ {
+ ref: chain(
+ isCallbackRef ? child.ref : null,
+ (r) => {this.childRefs[key] = r}
+ ),
+ }
+ )
)
)
})
if (currentChild) {
+ const isCallbackRef = typeof currentChild.ref !== 'string'
+ warning(isCallbackRef,
+ 'string refs are not supported on children of ReactCSSTransitionReplace and will be ignored. ' +
+ 'Please use a callback ref instead: https://facebook.github.io/react/docs/refs-and-the-dom.html#the-ref-callback-attribute')
+
childrenToRender.push(
React.createElement(childComponent,
{key: currentKey},
- this.wrapChild(currentChild, {ref: currentKey})
+ this.wrapChild(
+ currentChild,
+ {
+ ref: chain(
+ isCallbackRef ? currentChild.ref : null,
+ (r) => {this.childRefs[currentKey] = r}
+ ),
+ }
+ )
)
)
}
From f8b0a294d4359795dce0f4e01116bee56e21cfd1 Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 30 Apr 2017 00:01:40 +0100
Subject: [PATCH 07/22] Only add the isLeaving prop to children if
notifyLeaving is true.
---
src/ReactCSSTransitionReplace.jsx | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index a59ed44..ecc52dc 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -33,6 +33,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
transitionEnterTimeout: transitionTimeout('Enter'),
transitionLeaveTimeout: transitionTimeout('Leave'),
overflowHidden: PropTypes.bool,
+ notifyLeaving: PropTypes.bool,
}
static defaultProps = {
@@ -40,6 +41,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
transitionEnter: true,
transitionLeave: true,
overflowHidden: true,
+ notifyLeaving: false,
component: 'span',
childComponent: 'span',
}
@@ -210,7 +212,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
const childrenToRender = []
const {
- overflowHidden, transitionName, component, childComponent,
+ overflowHidden, transitionName, component, childComponent, notifyLeaving,
transitionAppear, transitionEnter, transitionLeave,
transitionAppearTimeout, transitionEnterTimeout, transitionLeaveTimeout,
...containerProps
@@ -254,7 +256,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
},
},
this.wrapChild(
- typeof child.type !== 'string'
+ notifyLeaving && typeof child.type !== 'string'
? React.cloneElement(child, {isLeaving: true})
: child,
{
From 6b3938eca3aff2b64cfdc721895a05afeef7429e Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 30 Apr 2017 00:04:52 +0100
Subject: [PATCH 08/22] Support multiple children provided all but one child
renders null.
---
src/ReactCSSTransitionReplace.jsx | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index ecc52dc..cd98bbc 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -19,6 +19,16 @@ const reactCSSTransitionGroupChild = React.createFactory(ReactCSSTransitionGroup
const TICK = 17
+// Filter out nulls before looking for an only child
+function getChildMapping(children) {
+ if (!Array.isArray(children)) {
+ return children
+ }
+ const childArray = React.Children.toArray(children).filter(c => c)
+ return childArray.length === 1 ? childArray[0] : React.Children.only(childArray)
+}
+
+
export default class ReactCSSTransitionReplace extends React.Component {
static displayName = 'ReactCSSTransitionReplace'
@@ -53,7 +63,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
this.state = {
currentKey: '1',
- currentChild: this.props.children ? React.Children.only(this.props.children) : undefined,
+ currentChild: getChildMapping(this.props.children),
prevChildren: {},
height: null,
}
@@ -76,7 +86,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
}
componentWillReceiveProps(nextProps) {
- const nextChild = nextProps.children ? React.Children.only(nextProps.children) : null
+ const nextChild = getChildMapping(nextProps.children)
const {currentChild} = this.state
if ((!currentChild && !nextChild) || (currentChild && nextChild && currentChild.key === nextChild.key)) {
From 0000db149ab7c4ae46c559ad8dfb8ec65a4300a2 Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 30 Apr 2017 09:28:28 +0100
Subject: [PATCH 09/22] Use requestAnimationFrame to queue the height
transition rather than a timeout.
---
package.json | 1 +
src/ReactCSSTransitionReplace.jsx | 28 +++++++++++++++++-----------
2 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/package.json b/package.json
index 59f1873..fd88363 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
},
"dependencies": {
"chain-function": "^1.0.0",
+ "dom-helpers": "^3.2.0",
"prop-types": "^15.5.6",
"react-transition-group": "^1.1.1",
"warning": "^3.0.0"
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index cd98bbc..bea211e 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -7,17 +7,15 @@
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
+import raf from 'dom-helpers/util/requestAnimationFrame'
import chain from 'chain-function'
import warning from 'warning'
import ReactCSSTransitionGroupChild from 'react-transition-group/CSSTransitionGroupChild'
import { nameShape, transitionTimeout } from 'react-transition-group/utils/PropTypes'
-
const reactCSSTransitionGroupChild = React.createFactory(ReactCSSTransitionGroupChild)
-const TICK = 17
-
// Filter out nulls before looking for an only child
function getChildMapping(children) {
@@ -82,7 +80,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
}
componentWillUnmount() {
- clearTimeout(this.timeout)
+ this.unmounted = true
}
componentWillReceiveProps(nextProps) {
@@ -185,13 +183,21 @@ export default class ReactCSSTransitionReplace extends React.Component {
}
enqueueHeightTransition() {
- const {state} = this
- this.timeout = setTimeout(() => {
- if (!state.currentChild) {
- return this.setState({height: 0})
- }
- this.setState({height: ReactDOM.findDOMNode(this.childRefs[state.currentKey]).offsetHeight})
- }, TICK)
+ if (!this.rafHandle) {
+ this.rafHandle = raf(this.performHeightTransition)
+ }
+ }
+
+ performHeightTransition = () => {
+ if (!this.unmounted) {
+ const {state} = this
+ this.setState({
+ height: state.currentChild
+ ? ReactDOM.findDOMNode(this.childRefs[state.currentKey]).offsetHeight
+ : 0,
+ })
+ }
+ this.rafHandle = null
}
wrapChild(child, moreProps) {
From 058551a9675c094599910aa208a9e3fed75021cb Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 30 Apr 2017 11:54:36 +0100
Subject: [PATCH 10/22] The transition nameShape prop type includes the height
and heightActive props.
---
src/ReactCSSTransitionReplace.jsx | 3 ++-
src/utils/PropTypes.js | 21 +++++++++++++++++++++
2 files changed, 23 insertions(+), 1 deletion(-)
create mode 100644 src/utils/PropTypes.js
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index bea211e..9ce2eed 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -12,7 +12,8 @@ import chain from 'chain-function'
import warning from 'warning'
import ReactCSSTransitionGroupChild from 'react-transition-group/CSSTransitionGroupChild'
-import { nameShape, transitionTimeout } from 'react-transition-group/utils/PropTypes'
+import { transitionTimeout } from 'react-transition-group/utils/PropTypes'
+import { nameShape } from './utils/PropTypes'
const reactCSSTransitionGroupChild = React.createFactory(ReactCSSTransitionGroupChild)
diff --git a/src/utils/PropTypes.js b/src/utils/PropTypes.js
new file mode 100644
index 0000000..5d368fd
--- /dev/null
+++ b/src/utils/PropTypes.js
@@ -0,0 +1,21 @@
+import PropTypes from 'prop-types'
+
+export const nameShape = PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.shape({
+ enter: PropTypes.string,
+ leave: PropTypes.string,
+ active: PropTypes.string,
+ height: PropTypes.string,
+ }),
+ PropTypes.shape({
+ enter: PropTypes.string,
+ enterActive: PropTypes.string,
+ leave: PropTypes.string,
+ leaveActive: PropTypes.string,
+ appear: PropTypes.string,
+ appearActive: PropTypes.string,
+ height: PropTypes.string,
+ heightActive: PropTypes.string,
+ }),
+])
From a69c6fe18380bcf832933ae45b10561e1c5166a6 Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 30 Apr 2017 11:55:27 +0100
Subject: [PATCH 11/22] Clear the selection after transitions to avoid the
child being selected due to multiple clicks.
---
src/ReactCSSTransitionReplace.jsx | 9 ++++++++-
src/utils/dom-helpers.js | 9 +++++++++
2 files changed, 17 insertions(+), 1 deletion(-)
create mode 100644 src/utils/dom-helpers.js
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index 9ce2eed..e45a32a 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -7,10 +7,12 @@
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
-import raf from 'dom-helpers/util/requestAnimationFrame'
import chain from 'chain-function'
import warning from 'warning'
+import raf from 'dom-helpers/util/requestAnimationFrame'
+import { clearSelection } from './utils/dom-helpers'
+
import ReactCSSTransitionGroupChild from 'react-transition-group/CSSTransitionGroupChild'
import { transitionTimeout } from 'react-transition-group/utils/PropTypes'
import { nameShape } from './utils/PropTypes'
@@ -127,6 +129,11 @@ export default class ReactCSSTransitionReplace extends React.Component {
const keysToLeave = this.keysToLeave
this.keysToLeave = []
keysToLeave.forEach(this.performLeave)
+
+ // When the enter completes and the component switches to relative positioning the
+ // child often gets selected after multiple clicks (at least in Chrome). To compensate
+ // the current selection is cleared whenever the component updates.
+ clearSelection()
}
performAppear(key) {
diff --git a/src/utils/dom-helpers.js b/src/utils/dom-helpers.js
new file mode 100644
index 0000000..fa8cb8c
--- /dev/null
+++ b/src/utils/dom-helpers.js
@@ -0,0 +1,9 @@
+/* global document, window */
+
+export function clearSelection() {
+ if (document.selection) {
+ document.selection.empty()
+ } else if (window.getSelection) {
+ window.getSelection().removeAllRanges()
+ }
+}
From 6c3be56a10fed03550d4cd734073732c13e8e360 Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 30 Apr 2017 12:09:45 +0100
Subject: [PATCH 12/22] Entering child renders with absolute positioning since
switching from relative to abs on leave cancels active animations.
---
demo/assets/styles.css | 2 +-
src/ReactCSSTransitionReplace.jsx | 88 +++++++++++++++----------------
2 files changed, 45 insertions(+), 45 deletions(-)
diff --git a/demo/assets/styles.css b/demo/assets/styles.css
index eb85449..e8d9938 100644
--- a/demo/assets/styles.css
+++ b/demo/assets/styles.css
@@ -15,6 +15,6 @@ h3 {
margin-top: 50px;
}
-.examples p {
+.examples > p {
margin-bottom: 20px;
}
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index e45a32a..c0669f6 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -231,6 +231,18 @@ export default class ReactCSSTransitionReplace extends React.Component {
}, child)
}
+ storeChildRef(child, key) {
+ const isCallbackRef = typeof child.ref !== 'string'
+ warning(isCallbackRef,
+ 'string refs are not supported on children of ReactCSSTransitionReplace and will be ignored. ' +
+ 'Please use a callback ref instead: https://facebook.github.io/react/docs/refs-and-the-dom.html#the-ref-callback-attribute')
+
+ return chain(
+ isCallbackRef ? child.ref : null,
+ (r) => {this.childRefs[key] = r}
+ )
+ }
+
render() {
const {currentKey, currentChild, prevChildren, height} = this.state
const childrenToRender = []
@@ -242,75 +254,63 @@ export default class ReactCSSTransitionReplace extends React.Component {
...containerProps
} = this.props
- if (height !== null) {
- const heightClassName = (typeof transitionName === 'object' && transitionName !== null)
- ? transitionName.height || ''
- : `${transitionName}-height`
+ containerProps.style = {
+ ...containerProps.style,
+ }
- containerProps.className = `${containerProps.className || ''} ${heightClassName}`
- containerProps.style = {
- ...containerProps.style,
- position: 'relative',
- display: 'block',
- height,
- }
+ if (Object.keys(this.transitioningKeys).length) {
+ containerProps.style.position = 'relative'
+ containerProps.style.display = 'block'
if (overflowHidden) {
containerProps.style.overflow = 'hidden'
}
}
+ if (height !== null) {
+ const heightClassName = typeof transitionName === 'string'
+ ? `${transitionName}-height`
+ : (transitionName && transitionName.height) || ''
+
+ containerProps.className = `${containerProps.className || ''} ${heightClassName}`
+ containerProps.style.height = height
+ }
+
+ const positionAbsolute = {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ }
+
Object.keys(prevChildren).forEach(key => {
const child = prevChildren[key]
- const isCallbackRef = typeof child.ref !== 'string'
- warning(isCallbackRef,
- 'string refs are not supported on children of ReactCSSTransitionReplace and will be ignored. ' +
- 'Please use a callback ref instead: https://facebook.github.io/react/docs/refs-and-the-dom.html#the-ref-callback-attribute')
-
childrenToRender.push(
React.createElement(childComponent,
- {
- key,
- style: {
- position: 'absolute',
- top: 0,
- left: 0,
- right: 0,
- bottom: 0,
- },
- },
+ {key, style: positionAbsolute},
this.wrapChild(
notifyLeaving && typeof child.type !== 'string'
? React.cloneElement(child, {isLeaving: true})
: child,
- {
- ref: chain(
- isCallbackRef ? child.ref : null,
- (r) => {this.childRefs[key] = r}
- ),
- }
+ {ref: this.storeChildRef(child, key)}
)
)
)
})
if (currentChild) {
- const isCallbackRef = typeof currentChild.ref !== 'string'
- warning(isCallbackRef,
- 'string refs are not supported on children of ReactCSSTransitionReplace and will be ignored. ' +
- 'Please use a callback ref instead: https://facebook.github.io/react/docs/refs-and-the-dom.html#the-ref-callback-attribute')
-
childrenToRender.push(
React.createElement(childComponent,
- {key: currentKey},
+ {
+ key: currentKey,
+ // Positioning must always be specified to keep the
+ // current child on top of the leaving children
+ style: this.transitioningKeys[currentKey] ? positionAbsolute : {position: 'relative'},
+ },
this.wrapChild(
currentChild,
- {
- ref: chain(
- isCallbackRef ? currentChild.ref : null,
- (r) => {this.childRefs[currentKey] = r}
- ),
- }
+ {ref: this.storeChildRef(currentChild, currentKey)}
)
)
)
From 9ce33e97141a058b299eeb8ae1386dacc949276d Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 30 Apr 2017 22:13:07 +0100
Subject: [PATCH 13/22] Fix enter animation of absolutely positioned elements
in Chrome not working by skipping one animation frame.
---
src/ReactCSSTransitionReplace.jsx | 6 ++---
src/ReactCSSTransitionReplaceChild.js | 35 +++++++++++++++++++++++++++
2 files changed, 38 insertions(+), 3 deletions(-)
create mode 100644 src/ReactCSSTransitionReplaceChild.js
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index c0669f6..6cb758d 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -13,11 +13,11 @@ import warning from 'warning'
import raf from 'dom-helpers/util/requestAnimationFrame'
import { clearSelection } from './utils/dom-helpers'
-import ReactCSSTransitionGroupChild from 'react-transition-group/CSSTransitionGroupChild'
+import ReactCSSTransitionReplaceChild from './ReactCSSTransitionReplaceChild'
import { transitionTimeout } from 'react-transition-group/utils/PropTypes'
import { nameShape } from './utils/PropTypes'
-const reactCSSTransitionGroupChild = React.createFactory(ReactCSSTransitionGroupChild)
+const reactCSSTransitionReplaceChild = React.createFactory(ReactCSSTransitionReplaceChild)
// Filter out nulls before looking for an only child
@@ -219,7 +219,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
// We need to provide this childFactory so that
// ReactCSSTransitionReplaceChild can receive updates to name,
// enter, and leave while it is leaving.
- return reactCSSTransitionGroupChild({
+ return reactCSSTransitionReplaceChild({
name: transitionName,
appear: this.props.transitionAppear,
enter: this.props.transitionEnter,
diff --git a/src/ReactCSSTransitionReplaceChild.js b/src/ReactCSSTransitionReplaceChild.js
new file mode 100644
index 0000000..d69a355
--- /dev/null
+++ b/src/ReactCSSTransitionReplaceChild.js
@@ -0,0 +1,35 @@
+/**
+ * Uses react-transition-group/CSSTransitionGroupChild with the exception that
+ * the first animation frame is skipped when starting new transitions since
+ * entering absolutely positioned elements in Chrome does not animate otherwise.
+ */
+import ReactCSSTransitionReplaceChild from 'react-transition-group/CSSTransitionGroupChild'
+import raf from 'dom-helpers/util/requestAnimationFrame'
+
+
+ReactCSSTransitionReplaceChild.prototype.queueClassAndNode = function queueClassAndNode(className, node) {
+ const _this2 = this
+
+ this.classNameAndNodeQueue.push({
+ className: className,
+ node: node,
+ })
+
+ if (!this.rafHandle) {
+ this.rafHandle = raf(function() {
+ return _this2.flushClassNameAndNodeQueueOnNextFrame()
+ })
+ }
+}
+
+// In Chrome the absolutely positioned children would not animate on enter
+// if the immediate animation frame is used so this skips to the next one.
+ReactCSSTransitionReplaceChild.prototype.flushClassNameAndNodeQueueOnNextFrame = function flushClassNameAndNodeQueueOnNextFrame() {
+ const _this2 = this
+
+ this.rafHandle = raf(function() {
+ return _this2.flushClassNameAndNodeQueue()
+ })
+}
+
+export default ReactCSSTransitionReplaceChild
From 7b7a2b068d73e80f0fa7bc0f52a66655df683e20 Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 30 Apr 2017 22:14:02 +0100
Subject: [PATCH 14/22] Fix Edge glitch when render starts by always applying
container position, display and overflow styles.
---
src/ReactCSSTransitionReplace.jsx | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index 6cb758d..0a08fdd 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -53,7 +53,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
transitionLeave: true,
overflowHidden: true,
notifyLeaving: false,
- component: 'span',
+ component: 'div',
childComponent: 'span',
}
@@ -254,17 +254,15 @@ export default class ReactCSSTransitionReplace extends React.Component {
...containerProps
} = this.props
+ // In edge there is a glitch as the container switches from not positioned
+ // to a positioned element at the start of a transition which is solved
+ // by applying the position and overflow style rules at all times.
containerProps.style = {
...containerProps.style,
+ position: 'relative',
}
-
- if (Object.keys(this.transitioningKeys).length) {
- containerProps.style.position = 'relative'
- containerProps.style.display = 'block'
-
- if (overflowHidden) {
- containerProps.style.overflow = 'hidden'
- }
+ if (overflowHidden) {
+ containerProps.style.overflow = 'hidden'
}
if (height !== null) {
From 5e924c7c946c10dfd1d0321769c8e397ca2560af Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 30 Apr 2017 22:14:24 +0100
Subject: [PATCH 15/22] Remove demo animated paragraph margins.
---
demo/assets/styles.css | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/demo/assets/styles.css b/demo/assets/styles.css
index e8d9938..b694999 100644
--- a/demo/assets/styles.css
+++ b/demo/assets/styles.css
@@ -15,6 +15,10 @@ h3 {
margin-top: 50px;
}
+p {
+ margin-bottom: 0;
+}
+
.examples > p {
margin-bottom: 20px;
}
From cd8246065ef25115f9a47fb3dc928752c40b40e9 Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 30 Apr 2017 22:17:00 +0100
Subject: [PATCH 16/22] 3.0.0-beta.2
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index fd88363..a1361f7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-css-transition-replace",
- "version": "3.0.0-beta.1",
+ "version": "3.0.0-beta.2",
"description": "A React component to animate replacing one element with another.",
"main": "lib/ReactCSSTransitionReplace.js",
"repository": {
From 05069bda1d261aa1f3524656ce565873f2b4f37b Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 30 Apr 2017 22:31:40 +0100
Subject: [PATCH 17/22] Revert the getChildMapping change since detecting nulls
can't work.
---
src/ReactCSSTransitionReplace.jsx | 14 ++------------
1 file changed, 2 insertions(+), 12 deletions(-)
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index 0a08fdd..a1a9c47 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -20,16 +20,6 @@ import { nameShape } from './utils/PropTypes'
const reactCSSTransitionReplaceChild = React.createFactory(ReactCSSTransitionReplaceChild)
-// Filter out nulls before looking for an only child
-function getChildMapping(children) {
- if (!Array.isArray(children)) {
- return children
- }
- const childArray = React.Children.toArray(children).filter(c => c)
- return childArray.length === 1 ? childArray[0] : React.Children.only(childArray)
-}
-
-
export default class ReactCSSTransitionReplace extends React.Component {
static displayName = 'ReactCSSTransitionReplace'
@@ -64,7 +54,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
this.state = {
currentKey: '1',
- currentChild: getChildMapping(this.props.children),
+ currentChild: this.props.children ? React.Children.only(this.props.children) : undefined,
prevChildren: {},
height: null,
}
@@ -87,7 +77,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
}
componentWillReceiveProps(nextProps) {
- const nextChild = getChildMapping(nextProps.children)
+ const nextChild = nextProps.children ? React.Children.only(nextProps.children) : undefined
const {currentChild} = this.state
if ((!currentChild && !nextChild) || (currentChild && nextChild && currentChild.key === nextChild.key)) {
From 1b73b4f4de23f463f102ecc73536695a028b394b Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 30 Apr 2017 22:39:01 +0100
Subject: [PATCH 18/22] Update the changelog for v3.
---
CHANGELOG.md | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1098028..381a984 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,15 @@
+### v3.0.0 (x May 2017)
+
+* [ENHANCEMENT] Maintain component callback refs by not overwriting with string refs similar to `react-transition-group`.
+* [FEATURE] Only add the `isLeaving` prop to children if opted in with `notifyLeaving={true}` since it's
+ a departure from `react-transition-group` features.
+* [ENHANCEMENT] Use `requestAnimationFrame` to queue the height transition rather than a timeout.
+* [ENHANCEMENT] Handle the enter and leave animation of changes due to successive child updates before the current transition ends.
+ * Clear the selection after transitions to avoid the child being selected after multiple clicks.
+ * Entering child renders with absolute positioning since switching from relative to abs on a premature leave cancels the active enter animation.
+ * Fix enter animation of absolutely positioned elements in Chrome not working by skipping one animation frame in the Child component.
+ * Fix Edge glitch when render starts by always applying container `position`, `display` (use a `div`) and `overflow` styles.
+
### v2.2.1 (29 April 2017)
* [UPGRADE] Add a `yarn` lock file.
From 6513728639ae5e745e772294501c5ba72a498910 Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 27 Aug 2017 15:01:09 +0100
Subject: [PATCH 19/22] Add back support for changeWidth.
---
demo/assets/transitions.css | 29 +++++++++++++++++++++++++---
demo/components/ContentAddRemove.jsx | 2 +-
demo/components/Demo.jsx | 11 ++++++++++-
gulpfile.js | 1 +
src/ReactCSSTransitionReplace.jsx | 24 ++++++++++++++++-------
5 files changed, 55 insertions(+), 12 deletions(-)
diff --git a/demo/assets/transitions.css b/demo/assets/transitions.css
index 0fbdda9..a065cae 100644
--- a/demo/assets/transitions.css
+++ b/demo/assets/transitions.css
@@ -49,7 +49,7 @@
/* Carousel-like transition */
.carousel-swap-leave {
- transition: transform 2s ease-in-out;
+ transition: transform 1s ease-in-out;
transform: translate(0, 0);
}
.carousel-swap-leave-active {
@@ -57,7 +57,7 @@
}
.carousel-swap-enter {
- transition: transform 2s ease-in-out;
+ transition: transform 1s ease-in-out;
transform: translate(100%, 0);
}
.carousel-swap-enter-active {
@@ -65,7 +65,7 @@
}
.carousel-swap-height {
- transition: height 2s ease-in-out;
+ transition: height 1s ease-in-out;
}
@@ -94,3 +94,26 @@
.roll-up-height {
transition: height .8s ease-in-out;
}
+
+
+/* Fade with width transition */
+
+.fade-width-leave {
+ opacity: 1;
+}
+.fade-width-leave.fade-width-leave-active {
+ opacity: 0;
+ transition: opacity .5s ease-in-out;
+}
+
+.fade-width-enter {
+ opacity: 0;
+}
+.fade-width-enter.fade-width-enter-active {
+ opacity: 1;
+ transition: opacity .5s ease-in-out;
+}
+
+.fade-width-height {
+ transition: height .5s ease-in-out, width .5s ease-in-out;
+}
diff --git a/demo/components/ContentAddRemove.jsx b/demo/components/ContentAddRemove.jsx
index 5416554..05c0c24 100644
--- a/demo/components/ContentAddRemove.jsx
+++ b/demo/components/ContentAddRemove.jsx
@@ -19,7 +19,7 @@ class ContentAddRemove extends React.Component {
diff --git a/demo/components/Demo.jsx b/demo/components/Demo.jsx
index d0a179c..830128a 100644
--- a/demo/components/Demo.jsx
+++ b/demo/components/Demo.jsx
@@ -1,5 +1,6 @@
import React from 'react'
import { Navbar, Nav, NavItem, Grid } from 'react-bootstrap'
+
const NavbarBrand = Navbar.Brand
import PageHead from './PageHead.jsx'
@@ -36,7 +37,7 @@ class Demo extends React.Component {
-
+
Examples
@@ -89,6 +90,14 @@ class Demo extends React.Component {
+
Height and Width animation
+
+
+
+
+
diff --git a/gulpfile.js b/gulpfile.js
index e92a351..6e1942f 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -53,6 +53,7 @@ gulp.task('demo:bundleAndWatch', function() {
gulp.task('demo', ['demo:bundleAndWatch'], function() {
browserSync.init({
+ port: 3010,
browser: ['google chrome'],
open: false,
notify: false,
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index a1a9c47..155e157 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -5,7 +5,7 @@
*/
import React from 'react'
-import ReactDOM from 'react-dom'
+import { findDOMNode } from 'react-dom'
import PropTypes from 'prop-types'
import chain from 'chain-function'
import warning from 'warning'
@@ -34,6 +34,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
transitionEnterTimeout: transitionTimeout('Enter'),
transitionLeaveTimeout: transitionTimeout('Leave'),
overflowHidden: PropTypes.bool,
+ changeWidth: PropTypes.bool,
notifyLeaving: PropTypes.bool,
}
@@ -42,6 +43,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
transitionEnter: true,
transitionLeave: true,
overflowHidden: true,
+ changeWidth: false,
notifyLeaving: false,
component: 'div',
childComponent: 'span',
@@ -57,6 +59,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
currentChild: this.props.children ? React.Children.only(this.props.children) : undefined,
prevChildren: {},
height: null,
+ width: null,
}
}
@@ -90,6 +93,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
currentKey: String(Number(currentKey) + 1),
currentChild: nextChild,
height: 0,
+ width: this.props.changeWidth ? 0 : null,
}
if (nextChild) {
@@ -97,7 +101,8 @@ export default class ReactCSSTransitionReplace extends React.Component {
}
if (currentChild) {
- nextState.height = ReactDOM.findDOMNode(this.childRefs[currentKey]).offsetHeight
+ nextState.height = findDOMNode(this.childRefs[currentKey]).offsetHeight
+ nextState.width = this.props.changeWidth ? findDOMNode(this.childRefs[currentKey]).offsetWidth : null
nextState.prevChildren = {
...prevChildren,
[currentKey]: currentChild,
@@ -190,9 +195,10 @@ export default class ReactCSSTransitionReplace extends React.Component {
if (!this.unmounted) {
const {state} = this
this.setState({
- height: state.currentChild
- ? ReactDOM.findDOMNode(this.childRefs[state.currentKey]).offsetHeight
- : 0,
+ height: state.currentChild ? findDOMNode(this.childRefs[state.currentKey]).offsetHeight : 0,
+ width: this.props.changeWidth
+ ? (state.currentChild ? findDOMNode(this.childRefs[state.currentKey]).offsetWidth : 0)
+ : null,
})
}
this.rafHandle = null
@@ -234,12 +240,12 @@ export default class ReactCSSTransitionReplace extends React.Component {
}
render() {
- const {currentKey, currentChild, prevChildren, height} = this.state
+ const {currentKey, currentChild, prevChildren, height, width} = this.state
const childrenToRender = []
const {
overflowHidden, transitionName, component, childComponent, notifyLeaving,
- transitionAppear, transitionEnter, transitionLeave,
+ transitionAppear, transitionEnter, transitionLeave, changeWidth,
transitionAppearTimeout, transitionEnterTimeout, transitionLeaveTimeout,
...containerProps
} = this.props
@@ -264,6 +270,10 @@ export default class ReactCSSTransitionReplace extends React.Component {
containerProps.style.height = height
}
+ if (width !== null) {
+ containerProps.style.width = width
+ }
+
const positionAbsolute = {
position: 'absolute',
top: 0,
From 64b4693f1ed9126783fd9fe2a17f2143bcd7097a Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 27 Aug 2017 20:35:20 +0100
Subject: [PATCH 20/22] React-Router 4 demo and support children rendering
null.
---
demo/assets/styles.css | 4 ++
demo/assets/transitions.css | 10 ++---
demo/components/AnimatedRouter.jsx | 58 ++++++++++++++++++++++++
demo/components/Demo.jsx | 8 +++-
package.json | 1 +
src/ReactCSSTransitionReplace.jsx | 17 ++++---
yarn.lock | 71 +++++++++++++++++++++++-------
7 files changed, 143 insertions(+), 26 deletions(-)
create mode 100644 demo/components/AnimatedRouter.jsx
diff --git a/demo/assets/styles.css b/demo/assets/styles.css
index b694999..20d939a 100644
--- a/demo/assets/styles.css
+++ b/demo/assets/styles.css
@@ -22,3 +22,7 @@ p {
.examples > p {
margin-bottom: 20px;
}
+
+.router-example h2 {
+ margin-top: 0;
+}
diff --git a/demo/assets/transitions.css b/demo/assets/transitions.css
index a065cae..3c5494f 100644
--- a/demo/assets/transitions.css
+++ b/demo/assets/transitions.css
@@ -98,22 +98,22 @@
/* Fade with width transition */
-.fade-width-leave {
+.fade-fast-leave {
opacity: 1;
}
-.fade-width-leave.fade-width-leave-active {
+.fade-fast-leave.fade-fast-leave-active {
opacity: 0;
transition: opacity .5s ease-in-out;
}
-.fade-width-enter {
+.fade-fast-enter {
opacity: 0;
}
-.fade-width-enter.fade-width-enter-active {
+.fade-fast-enter.fade-fast-enter-active {
opacity: 1;
transition: opacity .5s ease-in-out;
}
-.fade-width-height {
+.fade-fast-height {
transition: height .5s ease-in-out, width .5s ease-in-out;
}
diff --git a/demo/components/AnimatedRouter.jsx b/demo/components/AnimatedRouter.jsx
new file mode 100644
index 0000000..e65e578
--- /dev/null
+++ b/demo/components/AnimatedRouter.jsx
@@ -0,0 +1,58 @@
+/* eslint-disable react/no-multi-comp */
+import React from 'react'
+import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'
+import ReactCSSTransitionReplace from '../../src/ReactCSSTransitionReplace.jsx'
+
+const Home = () => (
+
+
Home
+
+
+)
+
+const One = () => (
+
+
One
+
+
+)
+
+const Two = () => (
+
+
Two
+
+
+)
+
+const AnimatedRouter = () => (
+
+
+
+ Home
+ One
+ Two
+ Three (no match)
+
+
+
+
+
(
+
+
+
+
+
+
+
+ )}/>
+
+
+
+
+)
+
+export default AnimatedRouter
diff --git a/demo/components/Demo.jsx b/demo/components/Demo.jsx
index 830128a..f97561f 100644
--- a/demo/components/Demo.jsx
+++ b/demo/components/Demo.jsx
@@ -6,6 +6,7 @@ const NavbarBrand = Navbar.Brand
import PageHead from './PageHead.jsx'
import ContentSwapper from './ContentSwapper.jsx'
import ContentAddRemove from './ContentAddRemove.jsx'
+import AnimatedRouter from './AnimatedRouter.jsx'
import ContentLong from './ContentLong.jsx'
import ContentShort from './ContentShort.jsx'
@@ -94,10 +95,15 @@ class Demo extends React.Component {
+
+ React Router v4
+
+
+
diff --git a/package.json b/package.json
index a1361f7..972cf20 100644
--- a/package.json
+++ b/package.json
@@ -75,6 +75,7 @@
"react": "^15.3.2",
"react-bootstrap": "^0.31.0",
"react-dom": "^15.3.2",
+ "react-router-dom": "^4.1.1",
"rimraf": "^2.5.4",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index 155e157..be99e67 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -101,8 +101,11 @@ export default class ReactCSSTransitionReplace extends React.Component {
}
if (currentChild) {
- nextState.height = findDOMNode(this.childRefs[currentKey]).offsetHeight
- nextState.width = this.props.changeWidth ? findDOMNode(this.childRefs[currentKey]).offsetWidth : null
+ const currentChildNode = findDOMNode(this.childRefs[currentKey])
+ nextState.height = currentChildNode ? currentChildNode.offsetHeight : 0
+ nextState.width = this.props.changeWidth
+ ? (currentChildNode ? currentChildNode.offsetWidth : 0)
+ : null
nextState.prevChildren = {
...prevChildren,
[currentKey]: currentChild,
@@ -118,7 +121,10 @@ export default class ReactCSSTransitionReplace extends React.Component {
componentDidUpdate() {
if (this.shouldEnterCurrent) {
this.shouldEnterCurrent = false
- this.performEnter(this.state.currentKey)
+ // If the current child renders null there is nothing to enter
+ if (findDOMNode(this.childRefs[this.state.currentKey])) {
+ this.performEnter(this.state.currentKey)
+ }
}
const keysToLeave = this.keysToLeave
@@ -194,10 +200,11 @@ export default class ReactCSSTransitionReplace extends React.Component {
performHeightTransition = () => {
if (!this.unmounted) {
const {state} = this
+ const currentChildNode = state.currentChild ? findDOMNode(this.childRefs[state.currentKey]) : null
this.setState({
- height: state.currentChild ? findDOMNode(this.childRefs[state.currentKey]).offsetHeight : 0,
+ height: currentChildNode ? currentChildNode.offsetHeight : 0,
width: this.props.changeWidth
- ? (state.currentChild ? findDOMNode(this.childRefs[state.currentKey]).offsetWidth : 0)
+ ? (currentChildNode ? currentChildNode.offsetWidth : 0)
: null,
})
}
diff --git a/yarn.lock b/yarn.lock
index 2fd9ec8..adef11e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1254,15 +1254,7 @@ concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
-concat-stream@^1.5.2:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
- dependencies:
- inherits "^2.0.3"
- readable-stream "^2.2.2"
- typedarray "^0.0.6"
-
-concat-stream@~1.5.0, concat-stream@~1.5.1:
+concat-stream@^1.5.2, concat-stream@~1.5.0, concat-stream@~1.5.1:
version "1.5.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266"
dependencies:
@@ -2378,6 +2370,16 @@ hawk@~3.1.3:
hoek "2.x.x"
sntp "1.x.x"
+history@^4.5.1, history@^4.6.0:
+ version "4.6.1"
+ resolved "https://registry.yarnpkg.com/history/-/history-4.6.1.tgz#911cf8eb65728555a94f2b12780a0c531a14d2fd"
+ dependencies:
+ invariant "^2.2.1"
+ loose-envify "^1.2.0"
+ resolve-pathname "^2.0.0"
+ value-equal "^0.2.0"
+ warning "^3.0.0"
+
hmac-drbg@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@@ -2390,6 +2392,10 @@ hoek@2.x.x:
version "2.16.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
+hoist-non-react-statics@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb"
+
home-or-tmp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@@ -2477,7 +2483,7 @@ inherits@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b"
-inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1:
+inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
@@ -2530,7 +2536,7 @@ interpret@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90"
-invariant@^2.1.0, invariant@^2.2.0, invariant@^2.2.1:
+invariant@^2.1.0, invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
dependencies:
@@ -3068,7 +3074,7 @@ lodash@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551"
-loose-envify@^1.0.0, loose-envify@^1.1.0:
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
dependencies:
@@ -3573,6 +3579,12 @@ path-root@^0.1.1:
dependencies:
path-root-regex "^0.1.0"
+path-to-regexp@^1.5.3:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
+ dependencies:
+ isarray "0.0.1"
+
path-type@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
@@ -3650,7 +3662,7 @@ promise@^7.1.1:
dependencies:
asap "~2.0.3"
-prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@~15.5.7:
+prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@~15.5.7:
version "15.5.8"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.8.tgz#6b7b2e141083be38c8595aa51fc55775c7199394"
dependencies:
@@ -3762,6 +3774,27 @@ react-prop-types@^0.4.0:
dependencies:
warning "^3.0.0"
+react-router-dom@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.1.1.tgz#3021ade1f2c160af97cf94e25594c5f294583025"
+ dependencies:
+ history "^4.5.1"
+ loose-envify "^1.3.1"
+ prop-types "^15.5.4"
+ react-router "^4.1.1"
+
+react-router@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.1.1.tgz#d448f3b7c1b429a6fbb03395099949c606b1fe95"
+ dependencies:
+ history "^4.6.0"
+ hoist-non-react-statics "^1.2.0"
+ invariant "^2.2.2"
+ loose-envify "^1.3.1"
+ path-to-regexp "^1.5.3"
+ prop-types "^15.5.4"
+ warning "^3.0.0"
+
react-transition-group@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-1.1.2.tgz#374cd778070f74b0a658045fc532edfd471ad836"
@@ -3810,7 +3843,7 @@ read-pkg@^1.0.0:
isarray "0.0.1"
string_decoder "~0.10.x"
-readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6:
+readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.6:
version "2.2.9"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8"
dependencies:
@@ -4008,6 +4041,10 @@ resolve-from@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
+resolve-pathname@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.1.0.tgz#e8358801b86b83b17560d4e3c382d7aef2100944"
+
resolve@1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
@@ -4527,7 +4564,7 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
-typedarray@^0.0.6, typedarray@~0.0.5:
+typedarray@~0.0.5:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@@ -4617,6 +4654,10 @@ validate-npm-package-license@^3.0.1:
spdx-correct "~1.0.0"
spdx-expression-parse "~1.0.0"
+value-equal@^0.2.0:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.2.1.tgz#c220a304361fce6994dbbedaa3c7e1a1b895871d"
+
verror@1.3.6:
version "1.3.6"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c"
From 143ee628159cc9282e53118ac67cc4ce19f2e5dc Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 27 Aug 2017 21:34:14 +0100
Subject: [PATCH 21/22] Leave transition dominates for no current child or one
that renders null.
---
src/ReactCSSTransitionReplace.jsx | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/ReactCSSTransitionReplace.jsx b/src/ReactCSSTransitionReplace.jsx
index be99e67..b170966 100644
--- a/src/ReactCSSTransitionReplace.jsx
+++ b/src/ReactCSSTransitionReplace.jsx
@@ -170,9 +170,9 @@ export default class ReactCSSTransitionReplace extends React.Component {
performLeave = (key) => {
this.transitioningKeys[key] = true
this.childRefs[key].componentWillLeave(this.handleDoneLeaving.bind(this, key))
- if (!this.state.currentChild) {
- // The enter transition dominates, but if there is no
- // entering component the height is set to zero.
+ if (!this.state.currentChild || !findDOMNode(this.childRefs[this.state.currentKey])) {
+ // The enter transition dominates, but if there is no entering
+ // component or it renders null the height is set to zero.
this.enqueueHeightTransition()
}
}
@@ -184,7 +184,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
delete nextState.prevChildren[key]
delete this.childRefs[key]
- if (!this.state.currentChild) {
+ if (!this.state.currentChild || !findDOMNode(this.childRefs[this.state.currentKey])) {
nextState.height = null
}
From 3f9a4caeb3454bcaea79b8d1adffdcf37df6ab79 Mon Sep 17 00:00:00 2001
From: Marnus Weststrate
Date: Sun, 27 Aug 2017 22:14:04 +0100
Subject: [PATCH 22/22] Update demo and docs.
---
.eslintrc | 2 +-
CHANGELOG.md | 11 +++----
README.md | 46 ++++++++++++++++++++++++++++--
UPGRADE_GUIDE.md | 11 ++++---
demo/assets/styles.css | 4 +++
demo/components/AnimatedRouter.jsx | 12 ++++----
demo/components/Demo.jsx | 16 +++++++----
package.json | 2 +-
8 files changed, 81 insertions(+), 23 deletions(-)
diff --git a/.eslintrc b/.eslintrc
index 8f23b45..3d753d7 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -155,7 +155,7 @@
"no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines
"max": 2
}],
- "no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary
+ "no-nested-ternary": 0, // http://eslint.org/docs/rules/no-nested-ternary
"no-new-object": 2, // http://eslint.org/docs/rules/no-new-object
"no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func
"no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 381a984..bef416d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,14 +1,15 @@
-### v3.0.0 (x May 2017)
+### v3.0.0 (27 August 2017)
+* [ENHANCEMENT] Treat a child rendering `null` as if there is no child; specifically helps avoid errors with RR4.
* [ENHANCEMENT] Maintain component callback refs by not overwriting with string refs similar to `react-transition-group`.
* [FEATURE] Only add the `isLeaving` prop to children if opted in with `notifyLeaving={true}` since it's
a departure from `react-transition-group` features.
* [ENHANCEMENT] Use `requestAnimationFrame` to queue the height transition rather than a timeout.
* [ENHANCEMENT] Handle the enter and leave animation of changes due to successive child updates before the current transition ends.
- * Clear the selection after transitions to avoid the child being selected after multiple clicks.
- * Entering child renders with absolute positioning since switching from relative to abs on a premature leave cancels the active enter animation.
- * Fix enter animation of absolutely positioned elements in Chrome not working by skipping one animation frame in the Child component.
- * Fix Edge glitch when render starts by always applying container `position`, `display` (use a `div`) and `overflow` styles.
+* [ENHANCEMENT] Clear the selection after transitions to avoid the child being selected after multiple clicks.
+* [ENHANCEMENT] Entering child renders with absolute positioning since switching from relative to abs on a premature leave cancels the active enter animation.
+* [ENHANCEMENT] Fix enter animation of absolutely positioned elements in Chrome not working by skipping one animation frame in the Child component.
+* [ENHANCEMENT] Fix Edge glitch when render starts by always applying container `position`, `display` (use a `div`) and `overflow` styles.
### v2.2.1 (29 April 2017)
diff --git a/README.md b/README.md
index 26b3b31..8a87e07 100644
--- a/README.md
+++ b/README.md
@@ -39,8 +39,9 @@ follows the exact same API as `ReactCSSTransitionGroup`, with support for `trans
and `transitionAppear`. When the `key` of the child component changes, the previous component is animated out
and the new component animated in. During this process:
- - The leaving component continues to be rendered as usual with `static` positioning.
- - The entering component is positioned on top of the leaving component with `absolute` positioning.
+ - All leaving components continue to be rendered; if the animation is slow there may be multiple components
+ in the process of leaving.
+ - The entering component is positioned on top of the leaving component(s) with `absolute` positioning.
- The height of the container is set to that of the leaving component, and then immediately to that of the
entering component. If the `transitionName` is a `String` the `{animation-name}-height` class name is applied
to it, and if `transitionName` is an `Object` the `transitionName.height` class will be used if present.
@@ -145,6 +146,47 @@ the duration of the transition. In this case:
See the live example [here](http://marnusw.github.io/react-css-transition-replace#fade-wait).
+### React-Router v4
+
+Animated transitions of react-router v4 routes is supported with two caveats shown in the example below:
+
+1. The current `location` must be applied to the `Switch` to force it to maintain the previous matched route on
+ the leaving component.
+2. If the `Switch` might render `null`, i.e. there is no catch-all `"*"` route, the `Switch` must be wrapped in a
+ `div` or similar for the leave transition to work; if not the previous component will disappear instantaneously
+ when there is no match.
+
+```javascript
+
+
+
+ Home
+ One
+ Two
+ Three (no match)
+
+
(
+
+
+
+
+
+
+
+
+
+ )}/>
+
+
+```
+
+See the live example [here](http://marnusw.github.io/react-css-transition-replace#react-router-v4).
+
+
### Hardware acceleration for smoother transitions
For smoother transitions hardware acceleration, which is achieved by using translate3d instead of the 2D
diff --git a/UPGRADE_GUIDE.md b/UPGRADE_GUIDE.md
index 83d7b80..f184bcf 100644
--- a/UPGRADE_GUIDE.md
+++ b/UPGRADE_GUIDE.md
@@ -1,5 +1,11 @@
# Upgrade Guide
+## 2.2.1 -> 3.0.0
+
+Applying the `isLeaving` prop on leaving children is now an opt-in behaviour controlled
+by the `notifyLeaving` prop.
+
+
## 2.1.0 -> 2.2.0
The library has always had a bug causing subsequent changes while an animation is in
@@ -7,9 +13,6 @@ progress to be ignored. This has been fixed in v2.2.0. While the functioning of
library is now technically more correct, this but may have been a feature used to
smooth over multiple transitions by some which will no longer be the case.
-If this causes problems it might be worthwhile to introduce a flag that could direct
-the component to ignore changes during transitions to restore the previous behaviour.
-
## 1.3.0 -> 2.0.0
@@ -20,7 +23,7 @@ You can pass `transitionName={{ height: 'my-height-className' }}` now, if
you need to use a custom className (useful for `css-modules`).
The leaving component will receive `isLeaving={true}` prop during it's leaving transition.
-You can use it in your child components to prevent their rerendering during that period, for example.
+You can use it in your child components to prevent their re-rendering during that period, for example.
## 1.0.x -> 1.1.0
diff --git a/demo/assets/styles.css b/demo/assets/styles.css
index 20d939a..24c55d3 100644
--- a/demo/assets/styles.css
+++ b/demo/assets/styles.css
@@ -19,6 +19,10 @@ p {
margin-bottom: 0;
}
+hr {
+ border-color: #bbb;
+}
+
.examples > p {
margin-bottom: 20px;
}
diff --git a/demo/components/AnimatedRouter.jsx b/demo/components/AnimatedRouter.jsx
index e65e578..e929b7d 100644
--- a/demo/components/AnimatedRouter.jsx
+++ b/demo/components/AnimatedRouter.jsx
@@ -42,11 +42,13 @@ const AnimatedRouter = () => (
transitionEnterTimeout={500}
transitionLeaveTimeout={500}
>
-
-
-
-
-
+
+
+
+
+
+
+
)}/>
diff --git a/demo/components/Demo.jsx b/demo/components/Demo.jsx
index f97561f..a4ced32 100644
--- a/demo/components/Demo.jsx
+++ b/demo/components/Demo.jsx
@@ -91,8 +91,12 @@ class Demo extends React.Component {
- Height and Width animation
-
+ Height and Width animation
+ By setting the changeWidth
prop the container width is managed along with the height.
+ This example realizes the same effect as above using this property and just a fade CSS animation.
+ In this case the height
class should configure the transition of both the height
+ and width properties: transition: height .5s ease-in-out, width .5s ease-in-out;
+
- React Router v4
-
+ React Router v4
+ Animating React-Router v4 transitions is supported. See the example for details.
-
+
diff --git a/package.json b/package.json
index 972cf20..0388b08 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-css-transition-replace",
- "version": "3.0.0-beta.2",
+ "version": "3.0.0",
"description": "A React component to animate replacing one element with another.",
"main": "lib/ReactCSSTransitionReplace.js",
"repository": {