react 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +0 -8
  2. package/addons.js +0 -3
  3. package/lib/AutoFocusMixin.js +32 -0
  4. package/lib/CSSCore.js +23 -22
  5. package/lib/CSSProperty.js +32 -1
  6. package/lib/CSSPropertyOperations.js +1 -1
  7. package/lib/ChangeEventPlugin.js +27 -5
  8. package/lib/ClientReactRootIndex.js +30 -0
  9. package/lib/CompositionEventPlugin.js +58 -10
  10. package/lib/DOMChildrenOperations.js +39 -3
  11. package/lib/DOMProperty.js +9 -5
  12. package/lib/DOMPropertyOperations.js +21 -8
  13. package/lib/Danger.js +9 -8
  14. package/lib/DefaultDOMPropertyConfig.js +23 -14
  15. package/lib/DefaultEventPluginOrder.js +1 -1
  16. package/lib/EnterLeaveEventPlugin.js +38 -5
  17. package/lib/EventConstants.js +4 -1
  18. package/lib/EventListener.js +42 -34
  19. package/lib/EventPluginHub.js +118 -13
  20. package/lib/EventPluginRegistry.js +62 -18
  21. package/lib/EventPluginUtils.js +33 -4
  22. package/lib/EventPropagators.js +7 -43
  23. package/lib/ExecutionEnvironment.js +4 -1
  24. package/lib/LinkedStateMixin.js +1 -1
  25. package/lib/LinkedValueUtils.js +160 -0
  26. package/lib/MobileSafariClickEventPlugin.js +1 -1
  27. package/lib/PooledClass.js +7 -1
  28. package/lib/React.js +30 -4
  29. package/lib/ReactBrowserComponentMixin.js +42 -0
  30. package/lib/ReactCSSTransitionGroup.js +65 -0
  31. package/lib/{ReactTransitionableChild.js → ReactCSSTransitionGroupChild.js} +22 -36
  32. package/lib/ReactChildren.js +4 -4
  33. package/lib/ReactComponent.js +163 -83
  34. package/lib/ReactComponentBrowserEnvironment.js +55 -71
  35. package/lib/ReactCompositeComponent.js +686 -119
  36. package/lib/ReactContext.js +67 -0
  37. package/lib/ReactCurrentOwner.js +1 -1
  38. package/lib/ReactDOM.js +19 -6
  39. package/lib/ReactDOMButton.js +6 -1
  40. package/lib/ReactDOMComponent.js +66 -24
  41. package/lib/ReactDOMForm.js +13 -3
  42. package/lib/ReactDOMIDOperations.js +106 -61
  43. package/lib/ReactDOMImg.js +61 -0
  44. package/lib/ReactDOMInput.js +28 -15
  45. package/lib/ReactDOMOption.js +13 -8
  46. package/lib/ReactDOMSelect.js +38 -18
  47. package/lib/ReactDOMSelection.js +1 -1
  48. package/lib/ReactDOMTextarea.js +19 -11
  49. package/lib/ReactDefaultBatchingStrategy.js +1 -1
  50. package/lib/ReactDefaultInjection.js +60 -26
  51. package/lib/ReactDefaultPerf.js +208 -371
  52. package/lib/ReactDefaultPerfAnalysis.js +199 -0
  53. package/lib/ReactErrorUtils.js +6 -15
  54. package/lib/ReactEventEmitter.js +144 -146
  55. package/lib/ReactEventEmitterMixin.js +1 -33
  56. package/lib/ReactEventTopLevelCallback.js +75 -15
  57. package/lib/ReactInjection.js +43 -0
  58. package/lib/ReactInputSelection.js +3 -2
  59. package/lib/ReactInstanceHandles.js +36 -20
  60. package/lib/ReactLink.js +2 -2
  61. package/lib/ReactMarkupChecksum.js +1 -1
  62. package/lib/ReactMount.js +136 -104
  63. package/lib/ReactMountReady.js +2 -2
  64. package/lib/ReactMultiChild.js +40 -49
  65. package/lib/ReactMultiChildUpdateTypes.js +3 -1
  66. package/lib/ReactOwner.js +17 -4
  67. package/lib/ReactPerf.js +6 -9
  68. package/lib/ReactPropTransferer.js +41 -22
  69. package/lib/ReactPropTypeLocationNames.js +31 -0
  70. package/lib/{ReactComponentEnvironment.js → ReactPropTypeLocations.js} +11 -6
  71. package/lib/ReactPropTypes.js +249 -48
  72. package/lib/ReactPutListenerQueue.js +61 -0
  73. package/lib/ReactReconcileTransaction.js +28 -7
  74. package/lib/ReactRootIndex.js +36 -0
  75. package/lib/ReactServerRendering.js +46 -19
  76. package/lib/ReactServerRenderingTransaction.js +116 -0
  77. package/lib/ReactStateSetters.js +1 -1
  78. package/lib/ReactTestUtils.js +394 -0
  79. package/lib/ReactTextComponent.js +33 -6
  80. package/lib/{ReactTransitionKeySet.js → ReactTransitionChildMapping.js} +43 -48
  81. package/lib/ReactTransitionEvents.js +1 -1
  82. package/lib/ReactTransitionGroup.js +133 -58
  83. package/lib/ReactUpdates.js +15 -12
  84. package/lib/ReactWithAddons.js +15 -3
  85. package/lib/SelectEventPlugin.js +23 -40
  86. package/lib/ServerReactRootIndex.js +36 -0
  87. package/lib/SimpleEventPlugin.js +55 -7
  88. package/lib/SyntheticClipboardEvent.js +8 -2
  89. package/lib/SyntheticCompositionEvent.js +1 -1
  90. package/lib/SyntheticDragEvent.js +44 -0
  91. package/lib/SyntheticEvent.js +3 -2
  92. package/lib/SyntheticFocusEvent.js +1 -1
  93. package/lib/SyntheticKeyboardEvent.js +5 -3
  94. package/lib/SyntheticMouseEvent.js +1 -1
  95. package/lib/SyntheticTouchEvent.js +1 -1
  96. package/lib/SyntheticUIEvent.js +1 -1
  97. package/lib/SyntheticWheelEvent.js +11 -8
  98. package/lib/Transaction.js +62 -37
  99. package/lib/ViewportMetrics.js +1 -1
  100. package/lib/accumulate.js +1 -1
  101. package/lib/adler32.js +1 -1
  102. package/lib/cloneWithProps.js +59 -0
  103. package/lib/containsNode.js +1 -1
  104. package/lib/copyProperties.js +1 -1
  105. package/lib/createArrayFrom.js +11 -14
  106. package/lib/createFullPageComponent.js +63 -0
  107. package/lib/createNodesFromMarkup.js +1 -1
  108. package/lib/createObjectFrom.js +1 -1
  109. package/lib/cx.js +3 -3
  110. package/lib/dangerousStyleValue.js +1 -1
  111. package/lib/emptyFunction.js +1 -1
  112. package/lib/emptyObject.js +27 -0
  113. package/lib/escapeTextForBrowser.js +1 -1
  114. package/lib/flattenChildren.js +6 -3
  115. package/lib/focusNode.js +33 -0
  116. package/lib/forEachAccumulated.js +1 -1
  117. package/lib/getActiveElement.js +5 -4
  118. package/lib/getEventKey.js +85 -0
  119. package/lib/getEventTarget.js +1 -1
  120. package/lib/getMarkupWrap.js +11 -1
  121. package/lib/getNodeForCharacterOffset.js +1 -1
  122. package/lib/getReactRootElementInContainer.js +1 -1
  123. package/lib/getTextContentAccessor.js +6 -4
  124. package/lib/getUnboundedScrollPosition.js +3 -3
  125. package/lib/hyphenate.js +1 -1
  126. package/lib/instantiateReactComponent.js +70 -0
  127. package/lib/invariant.js +20 -12
  128. package/lib/isEventSupported.js +8 -12
  129. package/lib/isNode.js +2 -2
  130. package/lib/isTextInputElement.js +1 -1
  131. package/lib/isTextNode.js +1 -1
  132. package/lib/joinClasses.js +1 -1
  133. package/lib/keyMirror.js +1 -1
  134. package/lib/keyOf.js +1 -1
  135. package/lib/memoizeStringOnly.js +1 -1
  136. package/lib/merge.js +1 -1
  137. package/lib/mergeHelpers.js +6 -7
  138. package/lib/mergeInto.js +1 -1
  139. package/lib/mixInto.js +1 -1
  140. package/lib/monitorCodeUse.js +37 -0
  141. package/lib/objMap.js +1 -1
  142. package/lib/objMapKeyVal.js +1 -1
  143. package/lib/onlyChild.js +43 -0
  144. package/lib/performanceNow.js +1 -1
  145. package/lib/shallowEqual.js +1 -1
  146. package/lib/shouldUpdateReactComponent.js +61 -0
  147. package/lib/toArray.js +75 -0
  148. package/lib/traverseAllChildren.js +72 -9
  149. package/lib/update.js +159 -0
  150. package/lib/warning.js +48 -0
  151. package/package.json +3 -3
  152. package/react.js +0 -3
  153. package/ReactJSErrors.js +0 -40
  154. package/lib/$.js +0 -46
  155. package/lib/CallbackRegistry.js +0 -91
  156. package/lib/LinkedValueMixin.js +0 -68
  157. package/lib/ex.js +0 -49
  158. package/lib/filterAttributes.js +0 -45
  159. package/lib/ge.js +0 -76
  160. package/lib/mutateHTMLNodeWithMarkup.js +0 -100
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2013 Facebook, Inc.
2
+ * Copyright 2013-2014 Facebook, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -19,8 +19,9 @@
19
19
 
20
20
  "use strict";
21
21
 
22
+ var DOMPropertyOperations = require("./DOMPropertyOperations");
23
+ var ReactBrowserComponentMixin = require("./ReactBrowserComponentMixin");
22
24
  var ReactComponent = require("./ReactComponent");
23
- var ReactMount = require("./ReactMount");
24
25
 
25
26
  var escapeTextForBrowser = require("./escapeTextForBrowser");
26
27
  var mixInto = require("./mixInto");
@@ -44,7 +45,18 @@ var ReactTextComponent = function(initialText) {
44
45
  this.construct({text: initialText});
45
46
  };
46
47
 
48
+ /**
49
+ * Used to clone the text descriptor object before it's mounted.
50
+ *
51
+ * @param {object} props
52
+ * @return {object} A new ReactTextComponent instance
53
+ */
54
+ ReactTextComponent.ConvenienceConstructor = function(props) {
55
+ return new ReactTextComponent(props.text);
56
+ };
57
+
47
58
  mixInto(ReactTextComponent, ReactComponent.Mixin);
59
+ mixInto(ReactTextComponent, ReactBrowserComponentMixin);
48
60
  mixInto(ReactTextComponent, {
49
61
 
50
62
  /**
@@ -52,7 +64,7 @@ mixInto(ReactTextComponent, {
52
64
  * any features besides containing text content.
53
65
  *
54
66
  * @param {string} rootID DOM ID of the root node.
55
- * @param {ReactReconcileTransaction} transaction
67
+ * @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
56
68
  * @param {number} mountDepth number of components in the owner hierarchy
57
69
  * @return {string} Markup for this text node.
58
70
  * @internal
@@ -64,9 +76,19 @@ mixInto(ReactTextComponent, {
64
76
  transaction,
65
77
  mountDepth
66
78
  );
79
+
80
+ var escapedText = escapeTextForBrowser(this.props.text);
81
+
82
+ if (transaction.renderToStaticMarkup) {
83
+ // Normally we'd wrap this in a `span` for the reasons stated above, but
84
+ // since this is a situation where React won't take over (static pages),
85
+ // we can simply return the text as it is.
86
+ return escapedText;
87
+ }
88
+
67
89
  return (
68
- '<span ' + ReactMount.ATTR_NAME + '="' + escapeTextForBrowser(rootID) + '">' +
69
- escapeTextForBrowser(this.props.text) +
90
+ '<span ' + DOMPropertyOperations.createMarkupForID(rootID) + '>' +
91
+ escapedText +
70
92
  '</span>'
71
93
  );
72
94
  },
@@ -82,7 +104,7 @@ mixInto(ReactTextComponent, {
82
104
  var nextProps = nextComponent.props;
83
105
  if (nextProps.text !== this.props.text) {
84
106
  this.props.text = nextProps.text;
85
- ReactComponent.DOMIDOperations.updateTextContentByID(
107
+ ReactComponent.BackendIDOperations.updateTextContentByID(
86
108
  this._rootNodeID,
87
109
  nextProps.text
88
110
  );
@@ -91,4 +113,9 @@ mixInto(ReactTextComponent, {
91
113
 
92
114
  });
93
115
 
116
+ // Expose the constructor on itself and the prototype for consistency with other
117
+ // descriptors.
118
+ ReactTextComponent.type = ReactTextComponent;
119
+ ReactTextComponent.prototype.type = ReactTextComponent;
120
+
94
121
  module.exports = ReactTextComponent;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2013 Facebook, Inc.
2
+ * Copyright 2013-2014 Facebook, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -14,16 +14,14 @@
14
14
  * limitations under the License.
15
15
  *
16
16
  * @typechecks static-only
17
- * @providesModule ReactTransitionKeySet
17
+ * @providesModule ReactTransitionChildMapping
18
18
  */
19
19
 
20
20
  "use strict";
21
21
 
22
22
  var ReactChildren = require("./ReactChildren");
23
23
 
24
- var MERGE_KEY_SETS_TAIL_SENTINEL = {};
25
-
26
- var ReactTransitionKeySet = {
24
+ var ReactTransitionChildMapping = {
27
25
  /**
28
26
  * Given `this.props.children`, return an object mapping key to child. Just
29
27
  * simple syntactic sugar around ReactChildren.map().
@@ -37,19 +35,6 @@ var ReactTransitionKeySet = {
37
35
  });
38
36
  },
39
37
 
40
- /**
41
- * Simple syntactic sugar to get an object with keys of all of `children`.
42
- * Does not have references to the children themselves.
43
- *
44
- * @param {*} children `this.props.children`
45
- * @return {object} Mapping of key to the value "true"
46
- */
47
- getKeySet: function(children) {
48
- return ReactChildren.map(children, function() {
49
- return true;
50
- });
51
- },
52
-
53
38
  /**
54
39
  * When you're adding or removing children some may be added or removed in the
55
40
  * same render pass. We want ot show *both* since we want to simultaneously
@@ -60,52 +45,62 @@ var ReactTransitionKeySet = {
60
45
  * directly have this concept of the union of prevChildren and nextChildren
61
46
  * so we implement it here.
62
47
  *
63
- * @param {object} prev prev child keys as returned from
64
- * `ReactTransitionKeySet.getKeySet()`.
65
- * @param {object} next next child keys as returned from
66
- * `ReactTransitionKeySet.getKeySet()`.
48
+ * @param {object} prev prev children as returned from
49
+ * `ReactTransitionChildMapping.getChildMapping()`.
50
+ * @param {object} next next children as returned from
51
+ * `ReactTransitionChildMapping.getChildMapping()`.
67
52
  * @return {object} a key set that contains all keys in `prev` and all keys
68
53
  * in `next` in a reasonable order.
69
54
  */
70
- mergeKeySets: function(prev, next) {
55
+ mergeChildMappings: function(prev, next) {
71
56
  prev = prev || {};
72
57
  next = next || {};
73
58
 
74
- var keySet = {};
75
- var prevKeys = Object.keys(prev).concat([MERGE_KEY_SETS_TAIL_SENTINEL]);
76
- var nextKeys = Object.keys(next).concat([MERGE_KEY_SETS_TAIL_SENTINEL]);
77
- var i;
78
- for (i = 0; i < prevKeys.length - 1; i++) {
79
- var prevKey = prevKeys[i];
80
- if (next[prevKey]) {
81
- continue;
59
+ function getValueForKey(key) {
60
+ if (next.hasOwnProperty(key)) {
61
+ return next[key];
62
+ } else {
63
+ return prev[key];
82
64
  }
65
+ }
83
66
 
84
- // This key is not in the new set. Place it in our
85
- // best guess where it should go. We do this by searching
86
- // for a key after the current one in prevKeys that is
87
- // still in nextKeys, and inserting right before it.
88
- // I know this is O(n^2), but this is not a particularly
89
- // hot code path.
90
- var insertPos = -1;
67
+ // For each key of `next`, the list of keys to insert before that key in
68
+ // the combined list
69
+ var nextKeysPending = {};
91
70
 
92
- for (var j = i + 1; j < prevKeys.length; j++) {
93
- insertPos = nextKeys.indexOf(prevKeys[j]);
94
- if (insertPos >= 0) {
95
- break;
71
+ var pendingKeys = [];
72
+ for (var prevKey in prev) {
73
+ if (next[prevKey]) {
74
+ if (pendingKeys.length) {
75
+ nextKeysPending[prevKey] = pendingKeys;
76
+ pendingKeys = [];
96
77
  }
78
+ } else {
79
+ pendingKeys.push(prevKey);
97
80
  }
81
+ }
98
82
 
99
- // Insert before insertPos
100
- nextKeys.splice(insertPos, 0, prevKey);
83
+ var i;
84
+ var childMapping = {};
85
+ for (var nextKey in next) {
86
+ if (nextKeysPending[nextKey]) {
87
+ for (i = 0; i < nextKeysPending[nextKey].length; i++) {
88
+ var pendingNextKey = nextKeysPending[nextKey][i];
89
+ childMapping[nextKeysPending[nextKey][i]] = getValueForKey(
90
+ pendingNextKey
91
+ );
92
+ }
93
+ }
94
+ childMapping[nextKey] = getValueForKey(nextKey);
101
95
  }
102
96
 
103
- for (i = 0; i < nextKeys.length - 1; i++) {
104
- keySet[nextKeys[i]] = true;
97
+ // Finally, add the keys which didn't appear before any key in `next`
98
+ for (i = 0; i < pendingKeys.length; i++) {
99
+ childMapping[pendingKeys[i]] = getValueForKey(pendingKeys[i]);
105
100
  }
106
101
 
107
- return keySet;
102
+ return childMapping;
108
103
  }
109
104
  };
110
105
 
111
- module.exports = ReactTransitionKeySet;
106
+ module.exports = ReactTransitionChildMapping;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2013 Facebook, Inc.
2
+ * Copyright 2013-2014 Facebook, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2013 Facebook, Inc.
2
+ * Copyright 2013-2014 Facebook, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -19,93 +19,168 @@
19
19
  "use strict";
20
20
 
21
21
  var React = require("./React");
22
- var ReactTransitionableChild = require("./ReactTransitionableChild");
23
- var ReactTransitionKeySet = require("./ReactTransitionKeySet");
22
+ var ReactTransitionChildMapping = require("./ReactTransitionChildMapping");
23
+
24
+ var cloneWithProps = require("./cloneWithProps");
25
+ var emptyFunction = require("./emptyFunction");
26
+ var merge = require("./merge");
24
27
 
25
28
  var ReactTransitionGroup = React.createClass({
26
29
 
27
30
  propTypes: {
28
- transitionName: React.PropTypes.string.isRequired,
29
- transitionEnter: React.PropTypes.bool,
30
- transitionLeave: React.PropTypes.bool,
31
- onTransition: React.PropTypes.func,
32
- component: React.PropTypes.func
31
+ component: React.PropTypes.func,
32
+ childFactory: React.PropTypes.func
33
33
  },
34
34
 
35
35
  getDefaultProps: function() {
36
36
  return {
37
- transitionEnter: true,
38
- transitionLeave: true,
39
- component: React.DOM.span
37
+ component: React.DOM.span,
38
+ childFactory: emptyFunction.thatReturnsArgument
39
+ };
40
+ },
41
+
42
+ getInitialState: function() {
43
+ return {
44
+ children: ReactTransitionChildMapping.getChildMapping(this.props.children)
40
45
  };
41
46
  },
42
47
 
48
+ componentWillReceiveProps: function(nextProps) {
49
+ var nextChildMapping = ReactTransitionChildMapping.getChildMapping(
50
+ nextProps.children
51
+ );
52
+ var prevChildMapping = this.state.children;
53
+
54
+ this.setState({
55
+ children: ReactTransitionChildMapping.mergeChildMappings(
56
+ prevChildMapping,
57
+ nextChildMapping
58
+ )
59
+ });
60
+
61
+ var key;
62
+
63
+ for (key in nextChildMapping) {
64
+ if (!prevChildMapping.hasOwnProperty(key) &&
65
+ !this.currentlyTransitioningKeys[key]) {
66
+ this.keysToEnter.push(key);
67
+ }
68
+ }
69
+
70
+ for (key in prevChildMapping) {
71
+ if (!nextChildMapping.hasOwnProperty(key) &&
72
+ !this.currentlyTransitioningKeys[key]) {
73
+ this.keysToLeave.push(key);
74
+ }
75
+ }
76
+
77
+ // If we want to someday check for reordering, we could do it here.
78
+ },
79
+
43
80
  componentWillMount: function() {
44
- // _transitionGroupCurrentKeys stores the union of previous *and* next keys.
45
- // If this were a component we'd store it as state, however, since this must
46
- // be a mixin, we need to keep the result of the union of keys in each
47
- // call to animateChildren() which happens in render(), so we can't
48
- // call setState() in there.
49
- this._transitionGroupCurrentKeys = {};
81
+ this.currentlyTransitioningKeys = {};
82
+ this.keysToEnter = [];
83
+ this.keysToLeave = [];
50
84
  },
51
85
 
52
86
  componentDidUpdate: function() {
53
- if (this.props.onTransition) {
54
- this.props.onTransition();
87
+ var keysToEnter = this.keysToEnter;
88
+ this.keysToEnter = [];
89
+ keysToEnter.forEach(this.performEnter);
90
+
91
+ var keysToLeave = this.keysToLeave;
92
+ this.keysToLeave = [];
93
+ keysToLeave.forEach(this.performLeave);
94
+ },
95
+
96
+ performEnter: function(key) {
97
+ this.currentlyTransitioningKeys[key] = true;
98
+
99
+ var component = this.refs[key];
100
+
101
+ if (component.componentWillEnter) {
102
+ component.componentWillEnter(
103
+ this._handleDoneEntering.bind(this, key)
104
+ );
105
+ } else {
106
+ this._handleDoneEntering(key);
55
107
  }
56
108
  },
57
109
 
58
- /**
59
- * Render some children in a transitionable way.
60
- */
61
- renderTransitionableChildren: function(sourceChildren) {
62
- var children = {};
63
- var childMapping = ReactTransitionKeySet.getChildMapping(sourceChildren);
110
+ _handleDoneEntering: function(key) {
111
+ var component = this.refs[key];
112
+ if (component.componentDidEnter) {
113
+ component.componentDidEnter();
114
+ }
115
+
116
+ delete this.currentlyTransitioningKeys[key];
64
117
 
65
- var currentKeys = ReactTransitionKeySet.mergeKeySets(
66
- this._transitionGroupCurrentKeys,
67
- ReactTransitionKeySet.getKeySet(sourceChildren)
118
+ var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
119
+ this.props.children
68
120
  );
69
121
 
70
- for (var key in currentKeys) {
71
- // Here is how we keep the nodes in the DOM. ReactTransitionableChild
72
- // knows how to hold onto its child if it changes to undefined. Here, we
73
- // may look up an old key in the new children, and it may switch to
74
- // undefined. React's reconciler will keep the ReactTransitionableChild
75
- // instance alive such that we can animate it.
76
- if (childMapping[key] || this.props.transitionLeave) {
77
- children[key] = ReactTransitionableChild({
78
- name: this.props.transitionName,
79
- enter: this.props.transitionEnter,
80
- onDoneLeaving: this._handleDoneLeaving.bind(this, key)
81
- }, childMapping[key]);
82
- }
122
+ if (!currentChildMapping.hasOwnProperty(key)) {
123
+ // This was removed before it had fully entered. Remove it.
124
+ this.performLeave(key);
83
125
  }
126
+ },
84
127
 
85
- this._transitionGroupCurrentKeys = currentKeys;
128
+ performLeave: function(key) {
129
+ this.currentlyTransitioningKeys[key] = true;
86
130
 
87
- return children;
131
+ var component = this.refs[key];
132
+ if (component.componentWillLeave) {
133
+ component.componentWillLeave(this._handleDoneLeaving.bind(this, key));
134
+ } else {
135
+ // Note that this is somewhat dangerous b/c it calls setState()
136
+ // again, effectively mutating the component before all the work
137
+ // is done.
138
+ this._handleDoneLeaving(key);
139
+ }
88
140
  },
89
141
 
90
142
  _handleDoneLeaving: function(key) {
91
- // When the leave animation finishes, we should blow away the actual DOM
92
- // node.
93
- delete this._transitionGroupCurrentKeys[key];
94
- this.forceUpdate();
143
+ var component = this.refs[key];
144
+
145
+ if (component.componentDidLeave) {
146
+ component.componentDidLeave();
147
+ }
148
+
149
+ delete this.currentlyTransitioningKeys[key];
150
+
151
+ var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
152
+ this.props.children
153
+ );
154
+
155
+ if (currentChildMapping.hasOwnProperty(key)) {
156
+ // This entered again before it fully left. Add it again.
157
+ this.performEnter(key);
158
+ } else {
159
+ var newChildren = merge(this.state.children);
160
+ delete newChildren[key];
161
+ this.setState({children: newChildren});
162
+ }
95
163
  },
96
164
 
97
165
  render: function() {
98
- return this.transferPropsTo(
99
- this.props.component(
100
- {
101
- transitionName: null,
102
- transitionEnter: null,
103
- transitionLeave: null,
104
- component: null
105
- },
106
- this.renderTransitionableChildren(this.props.children)
107
- )
108
- );
166
+ // TODO: we could get rid of the need for the wrapper node
167
+ // by cloning a single child
168
+ var childrenToRender = {};
169
+ for (var key in this.state.children) {
170
+ var child = this.state.children[key];
171
+ if (child) {
172
+ // You may need to apply reactive updates to a child as it is leaving.
173
+ // The normal React way to do it won't work since the child will have
174
+ // already been removed. In case you need this behavior you can provide
175
+ // a childFactory function to wrap every child, even the ones that are
176
+ // leaving.
177
+ childrenToRender[key] = cloneWithProps(
178
+ this.props.childFactory(child),
179
+ {ref: key}
180
+ );
181
+ }
182
+ }
183
+ return this.transferPropsTo(this.props.component(null, childrenToRender));
109
184
  }
110
185
  });
111
186