@@ -53,26 +53,16 @@ function clickedScrollbar(evt) {
5353 return document . documentElement . clientWidth <= evt . clientX || document . documentElement . clientHeight <= evt . clientY ;
5454}
5555
56- /**
57- * Generate the event handler that checks whether a clicked DOM node
58- * is inside of, or lives outside of, our Component's node tree.
59- */
60- function generateOutsideCheck ( componentNode , eventHandler , ignoreClass , excludeScrollbar , preventDefault , stopPropagation ) {
61- return function ( evt ) {
62- if ( preventDefault ) {
63- evt . preventDefault ( ) ;
64- }
65- if ( stopPropagation ) {
66- evt . stopPropagation ( ) ;
67- }
68- var current = evt . target ;
69- if ( excludeScrollbar && clickedScrollbar ( evt ) || findHighest ( current , componentNode , ignoreClass ) !== document ) {
70- return ;
71- }
72- eventHandler ( evt ) ;
56+ function autoInc ( ) {
57+ var seed = arguments . length > 0 && arguments [ 0 ] !== undefined ? arguments [ 0 ] : 0 ;
58+
59+ return function ( ) {
60+ return ++ seed ;
7361 } ;
7462}
7563
64+ var uid = autoInc ( ) ;
65+
7666var classCallCheck = function ( instance , Constructor ) {
7767 if ( ! ( instance instanceof Constructor ) ) {
7868 throw new TypeError ( "Cannot call a class as a function" ) ;
@@ -123,11 +113,7 @@ var possibleConstructorReturn = function (self, call) {
123113 return call && ( typeof call === "object" || typeof call === "function" ) ? call : self ;
124114} ;
125115
126- /**
127- * A higher-order-component for handling onClickOutside for React components.
128- */
129- var registeredComponents = [ ] ;
130- var handlers = [ ] ;
116+ var handlersMap = { } ;
131117
132118var touchEvents = [ 'touchstart' , 'touchmove' ] ;
133119var IGNORE_CLASS_NAME = 'ignore-react-onclickoutside' ;
@@ -154,27 +140,68 @@ function onClickOutsideHOC(WrappedComponent, config) {
154140 args [ _key ] = arguments [ _key ] ;
155141 }
156142
157- return _ret = ( _temp = ( _this = possibleConstructorReturn ( this , _Component . call . apply ( _Component , [ this ] . concat ( args ) ) ) , _this ) , _this . __outsideClickHandler = null , _this . enableOnClickOutside = function ( ) {
158- var fn = _this . __outsideClickHandler ;
159- if ( fn && typeof document !== 'undefined' ) {
160- var events = _this . props . eventTypes ;
161- if ( ! events . forEach ) {
162- events = [ events ] ;
163- }
143+ return _ret = ( _temp = ( _this = possibleConstructorReturn ( this , _Component . call . apply ( _Component , [ this ] . concat ( args ) ) ) , _this ) , _this . __outsideClickHandler = function ( event ) {
144+ if ( typeof _this . __clickOutsideHandlerProp === 'function' ) {
145+ _this . __clickOutsideHandlerProp ( event ) ;
146+ return ;
147+ }
164148
165- events . forEach ( function ( eventName ) {
166- var handlerOptions = null ;
167- var isTouchEvent = touchEvents . indexOf ( eventName ) !== - 1 ;
149+ var instance = _this . getInstance ( ) ;
168150
169- if ( isTouchEvent ) {
170- handlerOptions = { passive : ! _this . props . preventDefault } ;
171- }
151+ if ( typeof instance . props . handleClickOutside === 'function' ) {
152+ instance . props . handleClickOutside ( event ) ;
153+ return ;
154+ }
172155
173- document . addEventListener ( eventName , fn , handlerOptions ) ;
174- } ) ;
156+ if ( typeof instance . handleClickOutside === 'function' ) {
157+ instance . handleClickOutside ( event ) ;
158+ return ;
175159 }
160+
161+ throw new Error ( 'WrappedComponent lacks a handleClickOutside(event) function for processing outside click events.' ) ;
162+ } , _this . enableOnClickOutside = function ( ) {
163+ if ( typeof document === 'undefined' ) return ;
164+
165+ var events = _this . props . eventTypes ;
166+ if ( ! events . forEach ) {
167+ events = [ events ] ;
168+ }
169+
170+ handlersMap [ _this . _uid ] = function ( event ) {
171+ if ( _this . props . disableOnClickOutside ) return ;
172+ if ( _this . componentNode === null ) return ;
173+
174+ if ( _this . props . preventDefault ) {
175+ event . preventDefault ( ) ;
176+ }
177+
178+ if ( _this . props . stopPropagation ) {
179+ event . stopPropagation ( ) ;
180+ }
181+
182+ if ( _this . props . excludeScrollbar && clickedScrollbar ( event ) ) return ;
183+
184+ var current = event . target ;
185+
186+ if ( findHighest ( current , _this . componentNode , _this . props . outsideClickIgnoreClass ) !== document ) {
187+ return ;
188+ }
189+
190+ _this . __outsideClickHandler ( event ) ;
191+ } ;
192+
193+ events . forEach ( function ( eventName ) {
194+ var handlerOptions = null ;
195+ var isTouchEvent = touchEvents . indexOf ( eventName ) !== - 1 ;
196+
197+ if ( isTouchEvent ) {
198+ handlerOptions = { passive : ! _this . props . preventDefault } ;
199+ }
200+
201+ document . addEventListener ( eventName , handlersMap [ _this . _uid ] , handlerOptions ) ;
202+ } ) ;
176203 } , _this . disableOnClickOutside = function ( ) {
177- var fn = _this . __outsideClickHandler ;
204+ var fn = handlersMap [ _this . _uid ] ;
178205 if ( fn && typeof document !== 'undefined' ) {
179206 var events = _this . props . eventTypes ;
180207 if ( ! events . forEach ) {
@@ -183,6 +210,7 @@ function onClickOutsideHOC(WrappedComponent, config) {
183210 events . forEach ( function ( eventName ) {
184211 return document . removeEventListener ( eventName , fn ) ;
185212 } ) ;
213+ delete handlersMap [ _this . _uid ] ;
186214 }
187215 } , _this . getRef = function ( ref ) {
188216 return _this . instanceRef = ref ;
@@ -200,14 +228,12 @@ function onClickOutsideHOC(WrappedComponent, config) {
200228 return ref . getInstance ? ref . getInstance ( ) : ref ;
201229 } ;
202230
203- // this is given meaning in componentDidMount/componentDidUpdate
204-
205-
206231 /**
207232 * Add click listeners to the current document,
208233 * linked to this component's state.
209234 */
210235 onClickOutside . prototype . componentDidMount = function componentDidMount ( ) {
236+ this . _uid = uid ( ) ;
211237 // If we are in an environment without a DOM such
212238 // as shallow rendering or snapshots then we exit
213239 // early to prevent any unhandled errors being thrown.
@@ -222,51 +248,14 @@ function onClickOutsideHOC(WrappedComponent, config) {
222248 if ( typeof this . __clickOutsideHandlerProp !== 'function' ) {
223249 throw new Error ( 'WrappedComponent lacks a function for processing outside click events specified by the handleClickOutside config option.' ) ;
224250 }
225- } else if ( typeof instance . handleClickOutside === 'function' ) {
226- if ( react . Component . prototype . isPrototypeOf ( instance ) ) {
227- this . __clickOutsideHandlerProp = instance . handleClickOutside . bind ( instance ) ;
228- } else {
229- this . __clickOutsideHandlerProp = instance . handleClickOutside ;
230- }
231- } else if ( typeof instance . props . handleClickOutside === 'function' ) {
232- this . __clickOutsideHandlerProp = instance . props . handleClickOutside ;
233- } else {
234- throw new Error ( 'WrappedComponent lacks a handleClickOutside(event) function for processing outside click events.' ) ;
235- }
236-
237- // TODO: try to get rid of this, could be done with function ref, might be problematic for SFC though, they do not expose refs
238- if ( reactDom . findDOMNode ( instance ) === null ) {
239- return ;
240251 }
241252
242- this . addOutsideClickHandler ( ) ;
243- } ;
244-
245- /**
246- * Track for disableOnClickOutside props changes and enable/disable click outside
247- */
248-
249-
250- onClickOutside . prototype . componentWillReceiveProps = function componentWillReceiveProps ( nextProps ) {
251- if ( this . props . disableOnClickOutside && ! nextProps . disableOnClickOutside ) {
252- this . enableOnClickOutside ( ) ;
253- } else if ( ! this . props . disableOnClickOutside && nextProps . disableOnClickOutside ) {
254- this . disableOnClickOutside ( ) ;
255- }
253+ this . componentNode = reactDom . findDOMNode ( this . getInstance ( ) ) ;
254+ this . enableOnClickOutside ( ) ;
256255 } ;
257256
258257 onClickOutside . prototype . componentDidUpdate = function componentDidUpdate ( ) {
259- var componentNode = reactDom . findDOMNode ( this . getInstance ( ) ) ;
260-
261- if ( componentNode === null && this . __outsideClickHandler ) {
262- this . removeOutsideClickHandler ( ) ;
263- return ;
264- }
265-
266- if ( componentNode !== null && ! this . __outsideClickHandler ) {
267- this . addOutsideClickHandler ( ) ;
268- return ;
269- }
258+ this . componentNode = reactDom . findDOMNode ( this . getInstance ( ) ) ;
270259 } ;
271260
272261 /**
@@ -275,7 +264,7 @@ function onClickOutsideHOC(WrappedComponent, config) {
275264
276265
277266 onClickOutside . prototype . componentWillUnmount = function componentWillUnmount ( ) {
278- this . removeOutsideClickHandler ( ) ;
267+ this . disableOnClickOutside ( ) ;
279268 } ;
280269
281270 /**
@@ -290,35 +279,6 @@ function onClickOutsideHOC(WrappedComponent, config) {
290279 */
291280
292281
293- onClickOutside . prototype . addOutsideClickHandler = function addOutsideClickHandler ( ) {
294- var fn = this . __outsideClickHandler = generateOutsideCheck ( reactDom . findDOMNode ( this . getInstance ( ) ) , this . __clickOutsideHandlerProp , this . props . outsideClickIgnoreClass , this . props . excludeScrollbar , this . props . preventDefault , this . props . stopPropagation ) ;
295-
296- var pos = registeredComponents . length ;
297- registeredComponents . push ( this ) ;
298- handlers [ pos ] = fn ;
299-
300- // If there is a truthy disableOnClickOutside property for this
301- // component, don't immediately start listening for outside events.
302- if ( ! this . props . disableOnClickOutside ) {
303- this . enableOnClickOutside ( ) ;
304- }
305- } ;
306-
307- onClickOutside . prototype . removeOutsideClickHandler = function removeOutsideClickHandler ( ) {
308- this . disableOnClickOutside ( ) ;
309- this . __outsideClickHandler = false ;
310-
311- var pos = registeredComponents . indexOf ( this ) ;
312-
313- if ( pos > - 1 ) {
314- // clean up so we don't leak memory
315- if ( handlers [ pos ] ) {
316- handlers . splice ( pos , 1 ) ;
317- }
318- registeredComponents . splice ( pos , 1 ) ;
319- }
320- } ;
321-
322282 /**
323283 * Pass-through render
324284 */
0 commit comments