@@ -21,6 +21,8 @@ var React = require('react');
21
21
var ReactDOM = require ( 'react-dom' ) ;
22
22
import sdk from './index' ;
23
23
24
+ const DIALOG_CONTAINER_ID = "mx_Dialog_Container" ;
25
+
24
26
/**
25
27
* Wrap an asynchronous loader function with a react component which shows a
26
28
* spinner until the real component loads.
@@ -67,24 +69,37 @@ const AsyncWrapper = React.createClass({
67
69
} ,
68
70
} ) ;
69
71
70
- module . exports = {
71
- DialogContainerId : "mx_Dialog_Container" ,
72
+ class ModalManager {
73
+ constructor ( ) {
74
+ this . _counter = 0 ;
75
+
76
+ /** list of the modals we have stacked up, with the most recent at [0] */
77
+ this . _modals = [
78
+ /* {
79
+ elem: React component for this dialog
80
+ onFinished: caller-supplied onFinished callback
81
+ className: CSS class for the dialog wrapper div
82
+ } */
83
+ ] ;
84
+
85
+ this . closeAll = this . closeAll . bind ( this ) ;
86
+ }
72
87
73
- getOrCreateContainer : function ( ) {
74
- var container = document . getElementById ( this . DialogContainerId ) ;
88
+ getOrCreateContainer ( ) {
89
+ let container = document . getElementById ( DIALOG_CONTAINER_ID ) ;
75
90
76
91
if ( ! container ) {
77
92
container = document . createElement ( "div" ) ;
78
- container . id = this . DialogContainerId ;
93
+ container . id = DIALOG_CONTAINER_ID ;
79
94
document . body . appendChild ( container ) ;
80
95
}
81
96
82
97
return container ;
83
- } ,
98
+ }
84
99
85
- createDialog : function ( Element , props , className ) {
100
+ createDialog ( Element , props , className ) {
86
101
return this . createDialogAsync ( ( cb ) => { cb ( Element ) ; } , props , className ) ;
87
- } ,
102
+ }
88
103
89
104
/**
90
105
* Open a modal view.
@@ -105,27 +120,73 @@ module.exports = {
105
120
*
106
121
* @param {String } className CSS class to apply to the modal wrapper
107
122
*/
108
- createDialogAsync : function ( loader , props , className ) {
123
+ createDialogAsync ( loader , props , className ) {
109
124
var self = this ;
110
- // never call this via modal.close() from onFinished() otherwise it will loop
125
+ const modal = { } ;
126
+
127
+ // never call this from onFinished() otherwise it will loop
128
+ //
129
+ // nb explicit function() rather than arrow function, to get `arguments`
111
130
var closeDialog = function ( ) {
112
131
if ( props && props . onFinished ) props . onFinished . apply ( null , arguments ) ;
113
- ReactDOM . unmountComponentAtNode ( self . getOrCreateContainer ( ) ) ;
132
+ var i = self . _modals . indexOf ( modal ) ;
133
+ if ( i >= 0 ) {
134
+ self . _modals . splice ( i , 1 ) ;
135
+ }
136
+ self . _reRender ( ) ;
114
137
} ;
115
138
139
+ // don't attempt to reuse the same AsyncWrapper for different dialogs,
140
+ // otherwise we'll get confused.
141
+ const modalCount = this . _counter ++ ;
142
+
116
143
// FIXME: If a dialog uses getDefaultProps it clobbers the onFinished
117
144
// property set here so you can't close the dialog from a button click!
145
+ modal . elem = (
146
+ < AsyncWrapper key = { modalCount } loader = { loader } { ...props }
147
+ onFinished = { closeDialog } />
148
+ ) ;
149
+ modal . onFinished = props ? props . onFinished : null ;
150
+ modal . className = className ;
151
+
152
+ this . _modals . unshift ( modal ) ;
153
+
154
+ this . _reRender ( ) ;
155
+ return { close : closeDialog } ;
156
+ }
157
+
158
+ closeAll ( ) {
159
+ const modals = this . _modals ;
160
+ this . _modals = [ ] ;
161
+
162
+ for ( let i = 0 ; i < modals . length ; i ++ ) {
163
+ const m = modals [ i ] ;
164
+ if ( m . onFinished ) {
165
+ m . onFinished ( false ) ;
166
+ }
167
+ }
168
+
169
+ this . _reRender ( ) ;
170
+ }
171
+
172
+ _reRender ( ) {
173
+ if ( this . _modals . length == 0 ) {
174
+ ReactDOM . unmountComponentAtNode ( this . getOrCreateContainer ( ) ) ;
175
+ return ;
176
+ }
177
+
178
+ var modal = this . _modals [ 0 ] ;
118
179
var dialog = (
119
- < div className = { "mx_Dialog_wrapper " + className } >
180
+ < div className = { "mx_Dialog_wrapper " + modal . className } >
120
181
< div className = "mx_Dialog" >
121
- < AsyncWrapper loader = { loader } { ... props } onFinished = { closeDialog } />
182
+ { modal . elem }
122
183
</ div >
123
- < div className = "mx_Dialog_background" onClick = { closeDialog . bind ( this , false ) } > </ div >
184
+ < div className = "mx_Dialog_background" onClick = { this . closeAll } > </ div >
124
185
</ div >
125
186
) ;
126
187
127
188
ReactDOM . render ( dialog , this . getOrCreateContainer ( ) ) ;
189
+ }
190
+ }
128
191
129
- return { close : closeDialog } ;
130
- } ,
131
- } ;
192
+ export default new ModalManager ( ) ;
0 commit comments