@@ -11,7 +11,46 @@ import EventListener from './utils/EventListener';
11
11
// - Add `modal-body` div if only one child passed in that doesn't already have it
12
12
// - Tests
13
13
14
+ /**
15
+ * Gets the correct clientHeight of the modal container
16
+ * when the body/window/document you need to use the docElement clientHeight
17
+ * @param {HTMLElement } container
18
+ * @param {ReactElement|HTMLElement } context
19
+ * @return {Number }
20
+ */
21
+ function containerClientHeight ( container , context ) {
22
+ let doc = domUtils . ownerDocument ( context ) ;
23
+
24
+ return ( container === doc . body || container === doc . documentElement )
25
+ ? doc . documentElement . clientHeight
26
+ : container . clientHeight ;
27
+ }
28
+
29
+ function getContainer ( context ) {
30
+ return ( context . props . container && React . findDOMNode ( context . props . container ) ) ||
31
+ domUtils . ownerDocument ( context ) . body ;
32
+ }
33
+
34
+
35
+ if ( domUtils . canUseDom ) {
36
+ let scrollDiv = document . createElement ( 'div' ) ;
37
+
38
+ scrollDiv . style . position = 'absolute' ;
39
+ scrollDiv . style . top = '-9999px' ;
40
+ scrollDiv . style . width = '50px' ;
41
+ scrollDiv . style . height = '50px' ;
42
+ scrollDiv . style . overflow = 'scroll' ;
43
+
44
+ document . body . appendChild ( scrollDiv ) ;
45
+
46
+ scrollbarSize = scrollDiv . offsetWidth - scrollDiv . clientWidth ;
47
+
48
+ document . body . removeChild ( scrollDiv ) ;
49
+ scrollDiv = null ;
50
+ }
51
+
14
52
const Modal = React . createClass ( {
53
+
15
54
mixins : [ BootstrapMixin , FadeMixin ] ,
16
55
17
56
propTypes : {
@@ -35,8 +74,10 @@ const Modal = React.createClass({
35
74
} ,
36
75
37
76
render ( ) {
38
- let modalStyle = { display : 'block' } ;
77
+ let state = this . state ;
78
+ let modalStyle = { ...state . dialogStyles , display : 'block' } ;
39
79
let dialogClasses = this . getBsClassSet ( ) ;
80
+
40
81
delete dialogClasses . modal ;
41
82
dialogClasses [ 'modal-dialog' ] = true ;
42
83
@@ -119,30 +160,47 @@ const Modal = React.createClass({
119
160
} ,
120
161
121
162
componentDidMount ( ) {
163
+ const doc = domUtils . ownerDocument ( this ) ;
164
+ const win = domUtils . ownerWindow ( this ) ;
165
+
122
166
this . _onDocumentKeyupListener =
123
- EventListener . listen ( domUtils . ownerDocument ( this ) , 'keyup' , this . handleDocumentKeyUp ) ;
167
+ EventListener . listen ( doc , 'keyup' , this . handleDocumentKeyUp ) ;
168
+
169
+ this . _onWindowResizeListener =
170
+ EventListener . listen ( win , 'resize' , this . handleWindowResize ) ;
171
+
172
+ let container = getContainer ( this ) ;
124
173
125
- let container = ( this . props . container && React . findDOMNode ( this . props . container ) ) ||
126
- domUtils . ownerDocument ( this ) . body ;
127
174
container . className += container . className . length ? ' modal-open' : 'modal-open' ;
128
175
129
- this . focusModalContent ( ) ;
176
+ this . _containerIsOverflowing = container . scrollHeight > containerClientHeight ( container , this ) ;
130
177
131
178
if ( this . props . backdrop ) {
132
179
this . iosClickHack ( ) ;
133
180
}
181
+
182
+ this . setState ( this . _getStyles ( ) //eslint-disable-line react/no-did-mount-set-state
183
+ , ( ) => this . focusModalContent ( ) ) ;
134
184
} ,
135
185
136
186
componentDidUpdate ( prevProps ) {
137
187
if ( this . props . backdrop && this . props . backdrop !== prevProps . backdrop ) {
138
188
this . iosClickHack ( ) ;
189
+ this . setState ( this . _getStyles ( ) ) ; //eslint-disable-line react/no-did-update-set-state
190
+ }
191
+
192
+ if ( this . props . container !== prevProps . container ) {
193
+ let container = getContainer ( this ) ;
194
+ this . _containerIsOverflowing = container . scrollHeight > containerClientHeight ( container , this ) ;
139
195
}
140
196
} ,
141
197
142
198
componentWillUnmount ( ) {
143
199
this . _onDocumentKeyupListener . remove ( ) ;
144
- let container = ( this . props . container && React . findDOMNode ( this . props . container ) ) ||
145
- domUtils . ownerDocument ( this ) . body ;
200
+ this . _onWindowResizeListener . remove ( ) ;
201
+
202
+ let container = getContainer ( this ) ;
203
+
146
204
container . className = container . className . replace ( / ? m o d a l - o p e n / , '' ) ;
147
205
148
206
this . restoreLastFocus ( ) ;
@@ -162,8 +220,12 @@ const Modal = React.createClass({
162
220
}
163
221
} ,
164
222
223
+ handleWindowResize ( ) {
224
+ this . setState ( this . _getStyles ( ) ) ;
225
+ } ,
226
+
165
227
focusModalContent ( ) {
166
- this . lastFocus = domUtils . ownerDocument ( this ) . activeElement ;
228
+ this . lastFocus = domUtils . activeElement ( this ) ;
167
229
let modalContent = React . findDOMNode ( this . refs . modal ) ;
168
230
modalContent . focus ( ) ;
169
231
} ,
@@ -173,6 +235,23 @@ const Modal = React.createClass({
173
235
this . lastFocus . focus ( ) ;
174
236
this . lastFocus = null ;
175
237
}
238
+ } ,
239
+
240
+ _getStyles ( ) {
241
+ if ( ! domUtils . canUseDom ) { return { } ; }
242
+
243
+ let node = React . findDOMNode ( this . refs . modal )
244
+ , scrollHt = node . scrollHeight
245
+ , container = getContainer ( this )
246
+ , containerIsOverflowing = this . _containerIsOverflowing
247
+ , modalIsOverflowing = scrollHt > containerClientHeight ( container , this ) ;
248
+
249
+ return {
250
+ dialogStyles : {
251
+ paddingRight : containerIsOverflowing && ! modalIsOverflowing ? scrollbarSize : void 0 ,
252
+ paddingLeft : ! containerIsOverflowing && modalIsOverflowing ? scrollbarSize : void 0
253
+ }
254
+ } ;
176
255
}
177
256
} ) ;
178
257
0 commit comments