Skip to content

Commit c837d8d

Browse files
author
Jimmy Jia
committed
[fixed] Only calculate overlay position on display
Fixes react-bootstrap#1018
1 parent 45909a2 commit c837d8d

File tree

6 files changed

+349
-382
lines changed

6 files changed

+349
-382
lines changed

src/Collapse.js

+35-37
Original file line numberDiff line numberDiff line change
@@ -115,84 +115,82 @@ class Collapse extends React.Component {
115115

116116
Collapse.propTypes = {
117117
/**
118-
* Collapse the Component in or out.
118+
* Whether the component is entered; triggers the enter or exit animation
119119
*/
120-
in: React.PropTypes.bool,
120+
in: React.PropTypes.bool,
121121

122122
/**
123-
* Provide the duration of the animation in milliseconds, used to ensure that finishing callbacks are fired even if the
124-
* original browser transition end events are canceled.
123+
* Whether the component should be unmounted (removed from DOM) when exited
125124
*/
126-
duration: React.PropTypes.number,
125+
unmountOnExit: React.PropTypes.bool,
127126

128127
/**
129-
* Specifies the dimension used when collapsing.
130-
*
131-
* _Note: Bootstrap only partially supports 'width'!
132-
* You will need to supply your own css animation for the `.width` css class._
128+
* Whether transition in should run when the Transition component mounts, if
129+
* the component is initially entered
133130
*/
134-
dimension: React.PropTypes.oneOfType([
135-
React.PropTypes.oneOf(['height', 'width']),
136-
React.PropTypes.func
137-
]),
131+
transitionAppear: React.PropTypes.bool,
138132

139133
/**
140-
* A function that returns the height or width of the animating DOM node. Allows for providing some custom logic how much
141-
* Collapse component should animate in its specified dimension.
142-
*
143-
* `getDimensionValue` is called with the current dimension prop value and the DOM node.
134+
* Duration of the animation in milliseconds, to ensure that finishing
135+
* callbacks are fired even if the original browser transition end events are
136+
* canceled
144137
*/
145-
getDimensionValue: React.PropTypes.func,
138+
duration: React.PropTypes.number,
146139

147140
/**
148-
* A Callback fired before the component starts to expand.
141+
* Callback fired before the "entering" classes are applied
149142
*/
150143
onEnter: React.PropTypes.func,
151-
152144
/**
153-
* A Callback fired immediately after the component starts to expand.
145+
* Callback fired after the "entering" classes are applied
154146
*/
155147
onEntering: React.PropTypes.func,
156-
157148
/**
158-
* A Callback fired after the component has expanded.
149+
* Callback fired after the "enter" classes are applied
159150
*/
160151
onEntered: React.PropTypes.func,
161-
162152
/**
163-
* A Callback fired before the component starts to collapse.
153+
* Callback fired before the "exiting" classes are applied
164154
*/
165155
onExit: React.PropTypes.func,
166-
167156
/**
168-
* A Callback fired immediately after the component starts to collapse.
157+
* Callback fired after the "exiting" classes are applied
169158
*/
170159
onExiting: React.PropTypes.func,
171-
172160
/**
173-
* A Callback fired after the component has collapsed.
161+
* Callback fired after the "exited" classes are applied
174162
*/
175163
onExited: React.PropTypes.func,
176164

177165
/**
178-
* Specify whether the transitioning component should be unmounted (removed from the DOM) once the exit animation finishes.
166+
* The dimension used when collapsing
167+
*
168+
* _Note: Bootstrap only partially supports 'width'!
169+
* You will need to supply your own CSS animation for the `.width` CSS class._
179170
*/
180-
unmountOnExit: React.PropTypes.bool,
171+
dimension: React.PropTypes.oneOfType([
172+
React.PropTypes.oneOf(['height', 'width']),
173+
React.PropTypes.func
174+
]),
181175

182176
/**
183-
* Specify whether the component should collapse or expand when it mounts.
177+
* Function that returns the height or width of the animating DOM node
178+
*
179+
* Allows for providing some custom logic for how much the Collapse component
180+
* should animate in its specified dimension. Called with the current
181+
* dimension prop value and the DOM node.
184182
*/
185-
transitionAppear: React.PropTypes.bool
183+
getDimensionValue: React.PropTypes.func
186184
};
187185

188186
Collapse.defaultProps = {
189-
in: false,
187+
in: false,
190188
duration: 300,
191-
dimension: 'height',
192-
transitionAppear: false,
193189
unmountOnExit: false,
190+
transitionAppear: false,
191+
192+
dimension: 'height',
194193
getDimensionValue
195194
};
196195

197196
export default Collapse;
198-

src/Fade.js

+29-39
Original file line numberDiff line numberDiff line change
@@ -2,87 +2,77 @@ import React from 'react';
22
import Transition from './Transition';
33

44
class Fade extends React.Component {
5-
6-
constructor(props, context){
7-
super(props, context);
8-
}
9-
105
render() {
116
return (
127
<Transition
138
{...this.props}
14-
in={this.props.in}
159
className='fade'
1610
enteredClassName='in'
1711
enteringClassName='in'
1812
>
19-
{ this.props.children }
13+
{this.props.children}
2014
</Transition>
2115
);
2216
}
2317
}
2418

19+
// Explicitly copied from Transition for doc generation.
20+
2521
Fade.propTypes = {
2622
/**
27-
* Fade the Component in or out.
23+
* Whether the component is entered; triggers the enter or exit animation
2824
*/
29-
in: React.PropTypes.bool,
25+
in: React.PropTypes.bool,
3026

3127
/**
32-
* Provide the duration of the animation in milliseconds, used to ensure that finishing callbacks are fired even if the
33-
* original browser transition end events are canceled.
28+
* Whether the component should be unmounted (removed from DOM) when exited
3429
*/
35-
duration: React.PropTypes.number,
30+
unmountOnExit: React.PropTypes.bool,
3631

3732
/**
38-
* A Callback fired before the component starts to fade in.
33+
* Whether transition in should run when the Transition component mounts, if
34+
* the component is initially entered
3935
*/
40-
onEnter: React.PropTypes.func,
36+
transitionAppear: React.PropTypes.bool,
4137

4238
/**
43-
* A Callback fired immediately after the component has started to faded in.
39+
* Duration of the animation in milliseconds, to ensure that finishing
40+
* callbacks are fired even if the original browser transition end events are
41+
* canceled
4442
*/
45-
onEntering: React.PropTypes.func,
43+
duration: React.PropTypes.number,
4644

4745
/**
48-
* A Callback fired after the component has faded in.
46+
* Callback fired before the "entering" classes are applied
4947
*/
50-
onEntered: React.PropTypes.func,
51-
48+
onEnter: React.PropTypes.func,
5249
/**
53-
* A Callback fired before the component starts to fade out.
50+
* Callback fired after the "entering" classes are applied
5451
*/
55-
onExit: React.PropTypes.func,
56-
52+
onEntering: React.PropTypes.func,
5753
/**
58-
* A Callback fired immediately after the component has started to faded out.
54+
* Callback fired after the "enter" classes are applied
5955
*/
60-
onExiting: React.PropTypes.func,
61-
56+
onEntered: React.PropTypes.func,
6257
/**
63-
* A Callback fired after the component has faded out.
58+
* Callback fired before the "exiting" classes are applied
6459
*/
65-
onExited: React.PropTypes.func,
66-
67-
60+
onExit: React.PropTypes.func,
6861
/**
69-
* Specify whether the transitioning component should be unmounted (removed from the DOM) once the exit animation finishes.
62+
* Callback fired after the "exiting" classes are applied
7063
*/
71-
unmountOnExit: React.PropTypes.bool,
72-
64+
onExiting: React.PropTypes.func,
7365
/**
74-
* Specify whether the component should fade in or out when it mounts.
66+
* Callback fired after the "exited" classes are applied
7567
*/
76-
transitionAppear: React.PropTypes.bool
77-
68+
onExited: React.PropTypes.func
7869
};
7970

8071
Fade.defaultProps = {
81-
in: false,
72+
in: false,
8273
duration: 300,
83-
dimension: 'height',
84-
transitionAppear: false,
85-
unmountOnExit: false
74+
unmountOnExit: false,
75+
transitionAppear: false
8676
};
8777

8878
export default Fade;

src/Overlay.js

+48-48
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,24 @@ import CustomPropTypes from './utils/CustomPropTypes';
77
import Fade from './Fade';
88
import classNames from 'classnames';
99

10-
1110
class Overlay extends React.Component {
12-
13-
constructor(props, context){
11+
constructor(props, context) {
1412
super(props, context);
1513

16-
this.state = { exited: false };
14+
this.state = {exited: !props.show};
1715
this.onHiddenListener = this.handleHidden.bind(this);
1816
}
1917

2018
componentWillReceiveProps(nextProps) {
21-
let state = {};
22-
23-
if ( !nextProps.show && this.props.show ){
24-
state.exiting = true;
25-
}
26-
2719
if (nextProps.show) {
28-
state = { exited: false, exiting: false };
20+
this.setState({exited: false});
21+
} else if (!nextProps.animation) {
22+
// Otherwise let handleHidden take care of marking exited.
23+
this.setState({exited: true});
2924
}
30-
31-
this.setState(state);
3225
}
3326

34-
render(){
27+
render() {
3528
let {
3629
container
3730
, containerPadding
@@ -42,56 +35,63 @@ class Overlay extends React.Component {
4235
, animation: Transition
4336
, ...props } = this.props;
4437

45-
let child = null;
46-
47-
if ( Transition === true ){
38+
if (Transition === true) {
4839
Transition = Fade;
4940
}
5041

51-
if (props.show || (Transition && this.state.exiting && !this.state.exited)) {
42+
// Don't un-render the overlay while it's transitioning out.
43+
const mountOverlay = props.show || (Transition && !this.state.exited);
44+
45+
if (!mountOverlay) {
46+
// Don't bother showing anything if we don't have to.
47+
return null;
48+
}
5249

53-
child = children;
50+
let child = children;
5451

55-
// Position the child before the animation to avoid `null` DOM nodes
52+
if (Transition) {
53+
// This animates the child by injecting props, so it must be inner-most.
5654
child = (
57-
<Position {...{ container, containerPadding, target, placement }}>
58-
{ child }
59-
</Position>
55+
<Transition
56+
in={props.show}
57+
transitionAppear
58+
onExited={this.onHiddenListener}
59+
>
60+
{child}
61+
</Transition>
62+
);
63+
} else {
64+
child = cloneElement(
65+
child,
66+
{className: classNames('in', child.className)}
6067
);
61-
62-
child = Transition
63-
? (
64-
<Transition
65-
unmountOnExit
66-
in={props.show}
67-
transitionAppear={props.show}
68-
onExited={this.onHiddenListener}
69-
>
70-
{ child }
71-
</Transition>
72-
)
73-
: cloneElement(child, { className: classNames('in', child.className) });
74-
75-
//Adds a wrapping div so it cannot be before Transition
76-
if (rootClose) {
77-
child = (
78-
<RootCloseWrapper onRootClose={props.onHide}>
79-
{ child }
80-
</RootCloseWrapper>
81-
);
82-
}
8368
}
8469

70+
// This must wrap the transition to avoid position recalculations.
71+
child = (
72+
<Position {...{container, containerPadding, target, placement}}>
73+
{child}
74+
</Position>
75+
);
76+
77+
// This goes after everything else because it adds a wrapping div.
78+
if (rootClose) {
79+
child = (
80+
<RootCloseWrapper onRootClose={props.onHide}>
81+
{child}
82+
</RootCloseWrapper>
83+
);
84+
}
8585

8686
return (
8787
<Portal container={container}>
88-
{ child }
88+
{child}
8989
</Portal>
9090
);
9191
}
9292

93-
handleHidden(){
94-
this.setState({ exited: true, exiting: false });
93+
handleHidden() {
94+
this.setState({exited: true});
9595
}
9696
}
9797

0 commit comments

Comments
 (0)