1
1
import React from 'react' ;
2
2
import PropTypes from 'prop-types' ;
3
- import { findDOMNode } from 'react-dom' ;
3
+ import { findDOMNode , createPortal } from 'react-dom' ;
4
4
import createReactClass from 'create-react-class' ;
5
5
import contains from 'rc-util/lib/Dom/contains' ;
6
6
import addEventListener from 'rc-util/lib/Dom/addEventListener' ;
7
7
import Popup from './Popup' ;
8
8
import { getAlignFromPlacement , getPopupClassNameFromAlign } from './utils' ;
9
9
import getContainerRenderMixin from 'rc-util/lib/getContainerRenderMixin' ;
10
+ import Portal from './Portal' ;
10
11
11
12
function noop ( ) {
12
13
}
@@ -22,6 +23,26 @@ function returnDocument() {
22
23
const ALL_HANDLERS = [ 'onClick' , 'onMouseDown' , 'onTouchStart' , 'onMouseEnter' ,
23
24
'onMouseLeave' , 'onFocus' , 'onBlur' ] ;
24
25
26
+ const IS_REACT_16 = ! ! createPortal ;
27
+
28
+ const mixins = [ ] ;
29
+
30
+ if ( ! IS_REACT_16 ) {
31
+ mixins . push (
32
+ getContainerRenderMixin ( {
33
+ autoMount : false ,
34
+
35
+ isVisible ( instance ) {
36
+ return instance . state . popupVisible ;
37
+ } ,
38
+
39
+ getContainer ( instance ) {
40
+ return instance . getContainer ( ) ;
41
+ } ,
42
+ } )
43
+ ) ;
44
+ }
45
+
25
46
const Trigger = createReactClass ( {
26
47
displayName : 'Trigger' ,
27
48
propTypes : {
@@ -66,30 +87,7 @@ const Trigger = createReactClass({
66
87
maskAnimation : PropTypes . string ,
67
88
} ,
68
89
69
- mixins : [
70
- getContainerRenderMixin ( {
71
- autoMount : false ,
72
-
73
- isVisible ( instance ) {
74
- return instance . state . popupVisible ;
75
- } ,
76
-
77
- getContainer ( instance ) {
78
- const { props } = instance ;
79
- const popupContainer = document . createElement ( 'div' ) ;
80
- // Make sure default popup container will never cause scrollbar appearing
81
- // https://github.com/react-component/trigger/issues/41
82
- popupContainer . style . position = 'absolute' ;
83
- popupContainer . style . top = '0' ;
84
- popupContainer . style . left = '0' ;
85
- popupContainer . style . width = '100%' ;
86
- const mountNode = props . getPopupContainer ?
87
- props . getPopupContainer ( findDOMNode ( instance ) ) : props . getDocument ( ) . body ;
88
- mountNode . appendChild ( popupContainer ) ;
89
- return popupContainer ;
90
- } ,
91
- } ) ,
92
- ] ,
90
+ mixins,
93
91
94
92
getDefaultProps ( ) {
95
93
return {
@@ -154,11 +152,16 @@ const Trigger = createReactClass({
154
152
componentDidUpdate ( _ , prevState ) {
155
153
const props = this . props ;
156
154
const state = this . state ;
157
- this . renderComponent ( null , ( ) => {
155
+ const triggerAfterPopupVisibleChange = ( ) => {
158
156
if ( prevState . popupVisible !== state . popupVisible ) {
159
157
props . afterPopupVisibleChange ( state . popupVisible ) ;
160
158
}
161
- } ) ;
159
+ } ;
160
+ if ( ! IS_REACT_16 ) {
161
+ this . renderComponent ( null , triggerAfterPopupVisibleChange ) ;
162
+ } else {
163
+ triggerAfterPopupVisibleChange ( ) ;
164
+ }
162
165
163
166
// We must listen to `mousedown` or `touchstart`, edge case:
164
167
// https://github.com/ant-design/ant-design/issues/5804
@@ -342,12 +345,28 @@ const Trigger = createReactClass({
342
345
transitionName = { props . popupTransitionName }
343
346
maskAnimation = { props . maskAnimation }
344
347
maskTransitionName = { props . maskTransitionName }
348
+ ref = { this . savePopup }
345
349
>
346
350
{ typeof props . popup === 'function' ? props . popup ( ) : props . popup }
347
351
</ Popup >
348
352
) ;
349
353
} ,
350
354
355
+ getContainer ( ) {
356
+ const { props } = this ;
357
+ const popupContainer = document . createElement ( 'div' ) ;
358
+ // Make sure default popup container will never cause scrollbar appearing
359
+ // https://github.com/react-component/trigger/issues/41
360
+ popupContainer . style . position = 'absolute' ;
361
+ popupContainer . style . top = '0' ;
362
+ popupContainer . style . left = '0' ;
363
+ popupContainer . style . width = '100%' ;
364
+ const mountNode = props . getPopupContainer ?
365
+ props . getPopupContainer ( findDOMNode ( this ) ) : props . getDocument ( ) . body ;
366
+ mountNode . appendChild ( popupContainer ) ;
367
+ return popupContainer ;
368
+ } ,
369
+
351
370
setPopupVisible ( popupVisible ) {
352
371
this . clearDelayTimer ( ) ;
353
372
if ( this . state . popupVisible !== popupVisible ) {
@@ -451,11 +470,18 @@ const Trigger = createReactClass({
451
470
this . setPopupVisible ( false ) ;
452
471
} ,
453
472
473
+ savePopup ( node ) {
474
+ if ( IS_REACT_16 ) {
475
+ this . _component = node ;
476
+ }
477
+ } ,
478
+
454
479
render ( ) {
480
+ const { popupVisible } = this . state ;
455
481
const props = this . props ;
456
482
const children = props . children ;
457
483
const child = React . Children . only ( children ) ;
458
- const newChildProps = { } ;
484
+ const newChildProps = { key : 'trigger' } ;
459
485
if ( this . isClickToHide ( ) || this . isClickToShow ( ) ) {
460
486
newChildProps . onClick = this . onClick ;
461
487
newChildProps . onMouseDown = this . onMouseDown ;
@@ -483,7 +509,29 @@ const Trigger = createReactClass({
483
509
newChildProps . onBlur = this . createTwoChains ( 'onBlur' ) ;
484
510
}
485
511
486
- return React . cloneElement ( child , newChildProps ) ;
512
+ const trigger = React . cloneElement ( child , newChildProps ) ;
513
+
514
+ if ( ! IS_REACT_16 ) {
515
+ return trigger ;
516
+ }
517
+
518
+ let portal ;
519
+ // prevent unmounting after it's rendered
520
+ if ( popupVisible || this . _component ) {
521
+ portal = (
522
+ < Portal
523
+ key = "portal"
524
+ getContainer = { this . getContainer }
525
+ >
526
+ { this . getComponent ( ) }
527
+ </ Portal >
528
+ ) ;
529
+ }
530
+
531
+ return [
532
+ trigger ,
533
+ portal ,
534
+ ] ;
487
535
} ,
488
536
} ) ;
489
537
0 commit comments