UNPKG

8.69 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2013-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 */
8
9'use strict';
10
11var _assign = require('object-assign');
12
13function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
14
15function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
16
17function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
18
19var React = require('./React');
20var ReactTransitionChildMapping = require('./ReactTransitionChildMapping');
21
22var propTypesFactory = require('prop-types/factory');
23var PropTypes = propTypesFactory(React.isValidElement);
24
25var emptyFunction = require('fbjs/lib/emptyFunction');
26
27/**
28 * A basis for animations. When children are declaratively added or removed,
29 * special lifecycle hooks are called.
30 * See https://facebook.github.io/react/docs/animation.html#low-level-api-reacttransitiongroup
31 */
32
33var ReactTransitionGroup = function (_React$Component) {
34 _inherits(ReactTransitionGroup, _React$Component);
35
36 function ReactTransitionGroup() {
37 var _temp, _this, _ret;
38
39 _classCallCheck(this, ReactTransitionGroup);
40
41 for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
42 args[_key] = arguments[_key];
43 }
44
45 return _ret = (_temp = (_this = _possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.state = {
46 // TODO: can we get useful debug information to show at this point?
47 children: ReactTransitionChildMapping.getChildMapping(_this.props.children)
48 }, _this.performAppear = function (key) {
49 _this.currentlyTransitioningKeys[key] = true;
50
51 var component = _this.refs[key];
52
53 if (component.componentWillAppear) {
54 component.componentWillAppear(_this._handleDoneAppearing.bind(_this, key));
55 } else {
56 _this._handleDoneAppearing(key);
57 }
58 }, _this._handleDoneAppearing = function (key) {
59 var component = _this.refs[key];
60 if (component.componentDidAppear) {
61 component.componentDidAppear();
62 }
63
64 delete _this.currentlyTransitioningKeys[key];
65
66 var currentChildMapping = ReactTransitionChildMapping.getChildMapping(_this.props.children);
67
68 if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {
69 // This was removed before it had fully appeared. Remove it.
70 _this.performLeave(key);
71 }
72 }, _this.performEnter = function (key) {
73 _this.currentlyTransitioningKeys[key] = true;
74
75 var component = _this.refs[key];
76
77 if (component.componentWillEnter) {
78 component.componentWillEnter(_this._handleDoneEntering.bind(_this, key));
79 } else {
80 _this._handleDoneEntering(key);
81 }
82 }, _this._handleDoneEntering = function (key) {
83 var component = _this.refs[key];
84 if (component.componentDidEnter) {
85 component.componentDidEnter();
86 }
87
88 delete _this.currentlyTransitioningKeys[key];
89
90 var currentChildMapping = ReactTransitionChildMapping.getChildMapping(_this.props.children);
91
92 if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {
93 // This was removed before it had fully entered. Remove it.
94 _this.performLeave(key);
95 }
96 }, _this.performLeave = function (key) {
97 _this.currentlyTransitioningKeys[key] = true;
98
99 var component = _this.refs[key];
100 if (component.componentWillLeave) {
101 component.componentWillLeave(_this._handleDoneLeaving.bind(_this, key));
102 } else {
103 // Note that this is somewhat dangerous b/c it calls setState()
104 // again, effectively mutating the component before all the work
105 // is done.
106 _this._handleDoneLeaving(key);
107 }
108 }, _this._handleDoneLeaving = function (key) {
109 var component = _this.refs[key];
110
111 if (component.componentDidLeave) {
112 component.componentDidLeave();
113 }
114
115 delete _this.currentlyTransitioningKeys[key];
116
117 var currentChildMapping = ReactTransitionChildMapping.getChildMapping(_this.props.children);
118
119 if (currentChildMapping && currentChildMapping.hasOwnProperty(key)) {
120 // This entered again before it fully left. Add it again.
121 _this.performEnter(key);
122 } else {
123 _this.setState(function (state) {
124 var newChildren = _assign({}, state.children);
125 delete newChildren[key];
126 return { children: newChildren };
127 });
128 }
129 }, _temp), _possibleConstructorReturn(_this, _ret);
130 }
131
132 ReactTransitionGroup.prototype.componentWillMount = function componentWillMount() {
133 this.currentlyTransitioningKeys = {};
134 this.keysToEnter = [];
135 this.keysToLeave = [];
136 };
137
138 ReactTransitionGroup.prototype.componentDidMount = function componentDidMount() {
139 var initialChildMapping = this.state.children;
140 for (var key in initialChildMapping) {
141 if (initialChildMapping[key]) {
142 this.performAppear(key);
143 }
144 }
145 };
146
147 ReactTransitionGroup.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
148 var nextChildMapping = ReactTransitionChildMapping.getChildMapping(nextProps.children);
149 var prevChildMapping = this.state.children;
150
151 this.setState({
152 children: ReactTransitionChildMapping.mergeChildMappings(prevChildMapping, nextChildMapping)
153 });
154
155 var key;
156
157 for (key in nextChildMapping) {
158 var hasPrev = prevChildMapping && prevChildMapping.hasOwnProperty(key);
159 if (nextChildMapping[key] && !hasPrev && !this.currentlyTransitioningKeys[key]) {
160 this.keysToEnter.push(key);
161 }
162 }
163
164 for (key in prevChildMapping) {
165 var hasNext = nextChildMapping && nextChildMapping.hasOwnProperty(key);
166 if (prevChildMapping[key] && !hasNext && !this.currentlyTransitioningKeys[key]) {
167 this.keysToLeave.push(key);
168 }
169 }
170
171 // If we want to someday check for reordering, we could do it here.
172 };
173
174 ReactTransitionGroup.prototype.componentDidUpdate = function componentDidUpdate() {
175 var keysToEnter = this.keysToEnter;
176 this.keysToEnter = [];
177 keysToEnter.forEach(this.performEnter);
178
179 var keysToLeave = this.keysToLeave;
180 this.keysToLeave = [];
181 keysToLeave.forEach(this.performLeave);
182 };
183
184 ReactTransitionGroup.prototype.render = function render() {
185 // TODO: we could get rid of the need for the wrapper node
186 // by cloning a single child
187 var childrenToRender = [];
188 for (var key in this.state.children) {
189 var child = this.state.children[key];
190 if (child) {
191 // You may need to apply reactive updates to a child as it is leaving.
192 // The normal React way to do it won't work since the child will have
193 // already been removed. In case you need this behavior you can provide
194 // a childFactory function to wrap every child, even the ones that are
195 // leaving.
196 childrenToRender.push(React.cloneElement(this.props.childFactory(child), {
197 ref: key,
198 key: key
199 }));
200 }
201 }
202
203 // Do not forward ReactTransitionGroup props to primitive DOM nodes
204 var props = _assign({}, this.props);
205 delete props.transitionLeave;
206 delete props.transitionName;
207 delete props.transitionAppear;
208 delete props.transitionEnter;
209 delete props.childFactory;
210 delete props.transitionLeaveTimeout;
211 delete props.transitionEnterTimeout;
212 delete props.transitionAppearTimeout;
213 delete props.component;
214
215 return React.createElement(this.props.component, props, childrenToRender);
216 };
217
218 return ReactTransitionGroup;
219}(React.Component);
220
221ReactTransitionGroup.displayName = 'ReactTransitionGroup';
222ReactTransitionGroup.propTypes = {
223 component: PropTypes.any,
224 childFactory: PropTypes.func
225};
226ReactTransitionGroup.defaultProps = {
227 component: 'span',
228 childFactory: emptyFunction.thatReturnsArgument
229};
230
231
232module.exports = ReactTransitionGroup;
\No newline at end of file