@@ -11,7 +11,46 @@ import EventListener from './utils/EventListener';
1111// - Add `modal-body` div if only one child passed in that doesn't already have it
1212// - Tests
1313
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+
1452const Modal = React . createClass ( {
53+
1554 mixins : [ BootstrapMixin , FadeMixin ] ,
1655
1756 propTypes : {
@@ -35,8 +74,10 @@ const Modal = React.createClass({
3574 } ,
3675
3776 render ( ) {
38- let modalStyle = { display : 'block' } ;
77+ let state = this . state ;
78+ let modalStyle = { ...state . dialogStyles , display : 'block' } ;
3979 let dialogClasses = this . getBsClassSet ( ) ;
80+
4081 delete dialogClasses . modal ;
4182 dialogClasses [ 'modal-dialog' ] = true ;
4283
@@ -119,30 +160,47 @@ const Modal = React.createClass({
119160 } ,
120161
121162 componentDidMount ( ) {
163+ const doc = domUtils . ownerDocument ( this ) ;
164+ const win = domUtils . ownerWindow ( this ) ;
165+
122166 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 ) ;
124173
125- let container = ( this . props . container && React . findDOMNode ( this . props . container ) ) ||
126- domUtils . ownerDocument ( this ) . body ;
127174 container . className += container . className . length ? ' modal-open' : 'modal-open' ;
128175
129- this . focusModalContent ( ) ;
176+ this . _containerIsOverflowing = container . scrollHeight > containerClientHeight ( container , this ) ;
130177
131178 if ( this . props . backdrop ) {
132179 this . iosClickHack ( ) ;
133180 }
181+
182+ this . setState ( this . _getStyles ( ) //eslint-disable-line react/no-did-mount-set-state
183+ , ( ) => this . focusModalContent ( ) ) ;
134184 } ,
135185
136186 componentDidUpdate ( prevProps ) {
137187 if ( this . props . backdrop && this . props . backdrop !== prevProps . backdrop ) {
138188 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 ) ;
139195 }
140196 } ,
141197
142198 componentWillUnmount ( ) {
143199 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+
146204 container . className = container . className . replace ( / ? m o d a l - o p e n / , '' ) ;
147205
148206 this . restoreLastFocus ( ) ;
@@ -162,8 +220,12 @@ const Modal = React.createClass({
162220 }
163221 } ,
164222
223+ handleWindowResize ( ) {
224+ this . setState ( this . _getStyles ( ) ) ;
225+ } ,
226+
165227 focusModalContent ( ) {
166- this . lastFocus = domUtils . ownerDocument ( this ) . activeElement ;
228+ this . lastFocus = domUtils . activeElement ( this ) ;
167229 let modalContent = React . findDOMNode ( this . refs . modal ) ;
168230 modalContent . focus ( ) ;
169231 } ,
@@ -173,6 +235,23 @@ const Modal = React.createClass({
173235 this . lastFocus . focus ( ) ;
174236 this . lastFocus = null ;
175237 }
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+ } ;
176255 }
177256} ) ;
178257
0 commit comments