Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81155 views
1
/**
2
* Copyright 2013-2014, Facebook, Inc.
3
* All rights reserved.
4
*
5
* This source code is licensed under the BSD-style license found in the
6
* LICENSE file in the root directory of this source tree. An additional grant
7
* of patent rights can be found in the PATENTS file in the same directory.
8
*
9
* @providesModule ReactTransitionGroup
10
*/
11
12
"use strict";
13
14
var React = require('React');
15
var ReactTransitionChildMapping = require('ReactTransitionChildMapping');
16
17
var assign = require('Object.assign');
18
var cloneWithProps = require('cloneWithProps');
19
var emptyFunction = require('emptyFunction');
20
21
var ReactTransitionGroup = React.createClass({
22
displayName: 'ReactTransitionGroup',
23
24
propTypes: {
25
component: React.PropTypes.any,
26
childFactory: React.PropTypes.func
27
},
28
29
getDefaultProps: function() {
30
return {
31
component: 'span',
32
childFactory: emptyFunction.thatReturnsArgument
33
};
34
},
35
36
getInitialState: function() {
37
return {
38
children: ReactTransitionChildMapping.getChildMapping(this.props.children)
39
};
40
},
41
42
componentWillReceiveProps: function(nextProps) {
43
var nextChildMapping = ReactTransitionChildMapping.getChildMapping(
44
nextProps.children
45
);
46
var prevChildMapping = this.state.children;
47
48
this.setState({
49
children: ReactTransitionChildMapping.mergeChildMappings(
50
prevChildMapping,
51
nextChildMapping
52
)
53
});
54
55
var key;
56
57
for (key in nextChildMapping) {
58
var hasPrev = prevChildMapping && prevChildMapping.hasOwnProperty(key);
59
if (nextChildMapping[key] && !hasPrev &&
60
!this.currentlyTransitioningKeys[key]) {
61
this.keysToEnter.push(key);
62
}
63
}
64
65
for (key in prevChildMapping) {
66
var hasNext = nextChildMapping && nextChildMapping.hasOwnProperty(key);
67
if (prevChildMapping[key] && !hasNext &&
68
!this.currentlyTransitioningKeys[key]) {
69
this.keysToLeave.push(key);
70
}
71
}
72
73
// If we want to someday check for reordering, we could do it here.
74
},
75
76
componentWillMount: function() {
77
this.currentlyTransitioningKeys = {};
78
this.keysToEnter = [];
79
this.keysToLeave = [];
80
},
81
82
componentDidUpdate: function() {
83
var keysToEnter = this.keysToEnter;
84
this.keysToEnter = [];
85
keysToEnter.forEach(this.performEnter);
86
87
var keysToLeave = this.keysToLeave;
88
this.keysToLeave = [];
89
keysToLeave.forEach(this.performLeave);
90
},
91
92
performEnter: function(key) {
93
this.currentlyTransitioningKeys[key] = true;
94
95
var component = this.refs[key];
96
97
if (component.componentWillEnter) {
98
component.componentWillEnter(
99
this._handleDoneEntering.bind(this, key)
100
);
101
} else {
102
this._handleDoneEntering(key);
103
}
104
},
105
106
_handleDoneEntering: function(key) {
107
var component = this.refs[key];
108
if (component.componentDidEnter) {
109
component.componentDidEnter();
110
}
111
112
delete this.currentlyTransitioningKeys[key];
113
114
var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
115
this.props.children
116
);
117
118
if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {
119
// This was removed before it had fully entered. Remove it.
120
this.performLeave(key);
121
}
122
},
123
124
performLeave: function(key) {
125
this.currentlyTransitioningKeys[key] = true;
126
127
var component = this.refs[key];
128
if (component.componentWillLeave) {
129
component.componentWillLeave(this._handleDoneLeaving.bind(this, key));
130
} else {
131
// Note that this is somewhat dangerous b/c it calls setState()
132
// again, effectively mutating the component before all the work
133
// is done.
134
this._handleDoneLeaving(key);
135
}
136
},
137
138
_handleDoneLeaving: function(key) {
139
var component = this.refs[key];
140
141
if (component.componentDidLeave) {
142
component.componentDidLeave();
143
}
144
145
delete this.currentlyTransitioningKeys[key];
146
147
var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
148
this.props.children
149
);
150
151
if (currentChildMapping && currentChildMapping.hasOwnProperty(key)) {
152
// This entered again before it fully left. Add it again.
153
this.performEnter(key);
154
} else {
155
var newChildren = assign({}, this.state.children);
156
delete newChildren[key];
157
this.setState({children: newChildren});
158
}
159
},
160
161
render: function() {
162
// TODO: we could get rid of the need for the wrapper node
163
// by cloning a single child
164
var childrenToRender = {};
165
for (var key in this.state.children) {
166
var child = this.state.children[key];
167
if (child) {
168
// You may need to apply reactive updates to a child as it is leaving.
169
// The normal React way to do it won't work since the child will have
170
// already been removed. In case you need this behavior you can provide
171
// a childFactory function to wrap every child, even the ones that are
172
// leaving.
173
childrenToRender[key] = cloneWithProps(
174
this.props.childFactory(child),
175
{ref: key}
176
);
177
}
178
}
179
return React.createElement(
180
this.props.component,
181
this.props,
182
childrenToRender
183
);
184
}
185
});
186
187
module.exports = ReactTransitionGroup;
188
189