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
@@ -341,12 +344,28 @@ const Trigger = createReactClass({
341
344
transitionName = { props . popupTransitionName }
342
345
maskAnimation = { props . maskAnimation }
343
346
maskTransitionName = { props . maskTransitionName }
347
+ ref = { this . savePopup }
344
348
>
345
349
{ typeof props . popup === 'function' ? props . popup ( ) : props . popup }
346
350
</ Popup >
347
351
) ;
348
352
} ,
349
353
354
+ getContainer ( ) {
355
+ const { props } = this ;
356
+ const popupContainer = document . createElement ( 'div' ) ;
357
+ // Make sure default popup container will never cause scrollbar appearing
358
+ // https://github.com/react-component/trigger/issues/41
359
+ popupContainer . style . position = 'absolute' ;
360
+ popupContainer . style . top = '0' ;
361
+ popupContainer . style . left = '0' ;
362
+ popupContainer . style . width = '100%' ;
363
+ const mountNode = props . getPopupContainer ?
364
+ props . getPopupContainer ( findDOMNode ( this ) ) : props . getDocument ( ) . body ;
365
+ mountNode . appendChild ( popupContainer ) ;
366
+ return popupContainer ;
367
+ } ,
368
+
350
369
setPopupVisible ( popupVisible ) {
351
370
this . clearDelayTimer ( ) ;
352
371
if ( this . state . popupVisible !== popupVisible ) {
@@ -450,11 +469,18 @@ const Trigger = createReactClass({
450
469
this . setPopupVisible ( false ) ;
451
470
} ,
452
471
472
+ savePopup ( node ) {
473
+ if ( IS_REACT_16 ) {
474
+ this . _component = node ;
475
+ }
476
+ } ,
477
+
453
478
render ( ) {
479
+ const { popupVisible } = this . state ;
454
480
const props = this . props ;
455
481
const children = props . children ;
456
482
const child = React . Children . only ( children ) ;
457
- const newChildProps = { } ;
483
+ const newChildProps = { key : 'trigger' } ;
458
484
if ( this . isClickToHide ( ) || this . isClickToShow ( ) ) {
459
485
newChildProps . onClick = this . onClick ;
460
486
newChildProps . onMouseDown = this . onMouseDown ;
@@ -482,7 +508,28 @@ const Trigger = createReactClass({
482
508
newChildProps . onBlur = this . createTwoChains ( 'onBlur' ) ;
483
509
}
484
510
485
- return React . cloneElement ( child , newChildProps ) ;
511
+ const trigger = React . cloneElement ( child , newChildProps ) ;
512
+
513
+ if ( ! IS_REACT_16 ) {
514
+ return trigger ;
515
+ }
516
+
517
+ let portal ;
518
+ // prevent unmounting when visible change to false
519
+ if ( popupVisible || this . _component ) {
520
+ portal = (
521
+ < Portal
522
+ key = "portal"
523
+ getComponent = { this . getComponent }
524
+ getContainer = { this . getContainer }
525
+ />
526
+ ) ;
527
+ }
528
+
529
+ return [
530
+ trigger ,
531
+ portal ,
532
+ ] ;
486
533
} ,
487
534
} ) ;
488
535
0 commit comments