|
1 | 1 | var React = require('react');
|
2 |
| -var TransitionEvents = require('./utils/TransitionEvents'); |
| 2 | +var TransitionEvents = require('react/lib/ReactTransitionEvents'); |
3 | 3 |
|
4 | 4 | var CollapsableMixin = {
|
5 | 5 |
|
6 | 6 | propTypes: {
|
7 |
| - collapsable: React.PropTypes.bool, |
8 | 7 | defaultExpanded: React.PropTypes.bool,
|
9 | 8 | expanded: React.PropTypes.bool
|
10 | 9 | },
|
11 | 10 |
|
12 |
| - getInitialState: function () { |
| 11 | + getInitialState: function(){ |
| 12 | + var defaultExpanded = this.props.defaultExpanded != null ? |
| 13 | + this.props.defaultExpanded : |
| 14 | + this.props.expanded != null ? |
| 15 | + this.props.expanded : |
| 16 | + false; |
| 17 | + |
13 | 18 | return {
|
14 |
| - expanded: this.props.defaultExpanded != null ? this.props.defaultExpanded : null, |
| 19 | + expanded: defaultExpanded, |
15 | 20 | collapsing: false
|
16 | 21 | };
|
17 | 22 | },
|
18 | 23 |
|
19 |
| - handleTransitionEnd: function () { |
20 |
| - this._collapseEnd = true; |
21 |
| - this.setState({ |
22 |
| - collapsing: false |
23 |
| - }); |
24 |
| - }, |
25 |
| - |
26 |
| - componentWillReceiveProps: function (newProps) { |
27 |
| - if (this.props.collapsable && newProps.expanded !== this.props.expanded) { |
28 |
| - this._collapseEnd = false; |
29 |
| - this.setState({ |
30 |
| - collapsing: true |
31 |
| - }); |
| 24 | + componentWillUpdate: function(nextProps, nextState){ |
| 25 | + var willExpanded = nextProps.expanded != null ? nextProps.expanded : nextState.expanded; |
| 26 | + if (willExpanded === this.isExpanded()) { |
| 27 | + return; |
32 | 28 | }
|
33 |
| - }, |
34 | 29 |
|
35 |
| - _addEndTransitionListener: function () { |
| 30 | + // if the expanded state is being toggled, ensure node has a dimension value |
| 31 | + // this is needed for the animation to work and needs to be set before |
| 32 | + // the collapsing class is applied (after collapsing is applied the in class |
| 33 | + // is removed and the node's dimension will be wrong) |
| 34 | + |
36 | 35 | var node = this.getCollapsableDOMNode();
|
| 36 | + var dimension = this.dimension(); |
| 37 | + var value = '0'; |
37 | 38 |
|
38 |
| - if (node) { |
39 |
| - TransitionEvents.addEndEventListener( |
40 |
| - node, |
41 |
| - this.handleTransitionEnd |
42 |
| - ); |
| 39 | + if(!willExpanded){ |
| 40 | + value = this.getCollapsableDimensionValue(); |
43 | 41 | }
|
| 42 | + |
| 43 | + node.style[dimension] = value + 'px'; |
| 44 | + |
| 45 | + this._afterWillUpdate(); |
44 | 46 | },
|
45 | 47 |
|
46 |
| - _removeEndTransitionListener: function () { |
47 |
| - var node = this.getCollapsableDOMNode(); |
| 48 | + componentDidUpdate: function(prevProps, prevState){ |
| 49 | + // check if expanded is being toggled; if so, set collapsing |
| 50 | + this._checkToggleCollapsing(prevProps, prevState); |
48 | 51 |
|
49 |
| - if (node) { |
50 |
| - TransitionEvents.removeEndEventListener( |
51 |
| - node, |
52 |
| - this.handleTransitionEnd |
53 |
| - ); |
54 |
| - } |
| 52 | + // check if collapsing was turned on; if so, start animation |
| 53 | + this._checkStartAnimation(); |
| 54 | + }, |
| 55 | + |
| 56 | + // helps enable test stubs |
| 57 | + _afterWillUpdate: function(){ |
55 | 58 | },
|
56 | 59 |
|
57 |
| - componentDidMount: function () { |
58 |
| - this._afterRender(); |
| 60 | + _checkStartAnimation: function(){ |
| 61 | + if(!this.state.collapsing) { |
| 62 | + return; |
| 63 | + } |
| 64 | + |
| 65 | + var node = this.getCollapsableDOMNode(); |
| 66 | + var dimension = this.dimension(); |
| 67 | + var value = this.getCollapsableDimensionValue(); |
| 68 | + |
| 69 | + // setting the dimension here starts the transition animation |
| 70 | + var result; |
| 71 | + if(this.isExpanded()) { |
| 72 | + result = value + 'px'; |
| 73 | + } else { |
| 74 | + result = '0px'; |
| 75 | + } |
| 76 | + node.style[dimension] = result; |
59 | 77 | },
|
60 | 78 |
|
61 |
| - componentWillUnmount: function () { |
62 |
| - this._removeEndTransitionListener(); |
| 79 | + _checkToggleCollapsing: function(prevProps, prevState){ |
| 80 | + var wasExpanded = prevProps.expanded != null ? prevProps.expanded : prevState.expanded; |
| 81 | + var isExpanded = this.isExpanded(); |
| 82 | + if(wasExpanded !== isExpanded){ |
| 83 | + if(wasExpanded) { |
| 84 | + this._handleCollapse(); |
| 85 | + } else { |
| 86 | + this._handleExpand(); |
| 87 | + } |
| 88 | + } |
63 | 89 | },
|
64 | 90 |
|
65 |
| - componentWillUpdate: function (nextProps) { |
66 |
| - var dimension = (typeof this.getCollapsableDimension === 'function') ? |
67 |
| - this.getCollapsableDimension() : 'height'; |
| 91 | + _handleExpand: function(){ |
68 | 92 | var node = this.getCollapsableDOMNode();
|
| 93 | + var dimension = this.dimension(); |
| 94 | + |
| 95 | + var complete = (function (){ |
| 96 | + this._removeEndEventListener(node, complete); |
| 97 | + // remove dimension value - this ensures the collapsable item can grow |
| 98 | + // in dimension after initial display (such as an image loading) |
| 99 | + node.style[dimension] = ''; |
| 100 | + this.setState({ |
| 101 | + collapsing:false |
| 102 | + }); |
| 103 | + }).bind(this); |
| 104 | + |
| 105 | + this._addEndEventListener(node, complete); |
69 | 106 |
|
70 |
| - this._removeEndTransitionListener(); |
| 107 | + this.setState({ |
| 108 | + collapsing: true |
| 109 | + }); |
71 | 110 | },
|
72 | 111 |
|
73 |
| - componentDidUpdate: function (prevProps, prevState) { |
74 |
| - this._afterRender(); |
| 112 | + _handleCollapse: function(){ |
| 113 | + var node = this.getCollapsableDOMNode(); |
| 114 | + |
| 115 | + var complete = (function (){ |
| 116 | + this._removeEndEventListener(node, complete); |
| 117 | + this.setState({ |
| 118 | + collapsing: false |
| 119 | + }); |
| 120 | + }).bind(this); |
| 121 | + |
| 122 | + this._addEndEventListener(node, complete); |
| 123 | + |
| 124 | + this.setState({ |
| 125 | + collapsing: true |
| 126 | + }); |
75 | 127 | },
|
76 | 128 |
|
77 |
| - _afterRender: function () { |
78 |
| - if (!this.props.collapsable) { |
79 |
| - return; |
80 |
| - } |
| 129 | + // helps enable test stubs |
| 130 | + _addEndEventListener: function(node, complete){ |
| 131 | + TransitionEvents.addEndEventListener(node, complete); |
| 132 | + }, |
81 | 133 |
|
82 |
| - this._addEndTransitionListener(); |
83 |
| - setTimeout(this._updateDimensionAfterRender, 0); |
| 134 | + // helps enable test stubs |
| 135 | + _removeEndEventListener: function(node, complete){ |
| 136 | + TransitionEvents.removeEndEventListener(node, complete); |
84 | 137 | },
|
85 | 138 |
|
86 |
| - _updateDimensionAfterRender: function () { |
87 |
| - var node = this.getCollapsableDOMNode(); |
88 |
| - if (node) { |
89 |
| - var dimension = (typeof this.getCollapsableDimension === 'function') ? |
90 |
| - this.getCollapsableDimension() : 'height'; |
91 |
| - node.style[dimension] = this.isExpanded() ? |
92 |
| - this.getCollapsableDimensionValue() + 'px' : '0px'; |
93 |
| - } |
| 139 | + dimension: function(){ |
| 140 | + return (typeof this.getCollapsableDimension === 'function') ? |
| 141 | + this.getCollapsableDimension() : |
| 142 | + 'height'; |
94 | 143 | },
|
95 | 144 |
|
96 |
| - isExpanded: function () { |
97 |
| - return (this.props.expanded != null) ? |
98 |
| - this.props.expanded : this.state.expanded; |
| 145 | + isExpanded: function(){ |
| 146 | + return this.props.expanded != null ? this.props.expanded : this.state.expanded; |
99 | 147 | },
|
100 | 148 |
|
101 | 149 | getCollapsableClassSet: function (className) {
|
|
0 commit comments