Skip to content

Commit ae79e53

Browse files
committed
Support React 16
1 parent 402016e commit ae79e53

File tree

4 files changed

+116
-32
lines changed

4 files changed

+116
-32
lines changed

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,15 @@
4343
},
4444
"devDependencies": {
4545
"async": "~1.5.0",
46+
"core-js": "^2.5.1",
4647
"expect.js": "0.3.x",
4748
"jquery": "~1.11.3",
4849
"object-assign": "~4.0.1",
4950
"pre-commit": "1.x",
50-
"rc-tools": "6.x",
5151
"rc-test": "6.x",
52-
"react": "15.x",
53-
"react-dom": "15.x"
52+
"rc-tools": "6.x",
53+
"react": "^16.0.0-rc.3",
54+
"react-dom": "^16.0.0-rc.3"
5455
},
5556
"pre-commit": [
5657
"lint"

src/Portal.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { createPortal } from 'react-dom';
4+
5+
export default class Portal extends React.Component {
6+
static propTypes = {
7+
getComponent: PropTypes.func.isRequired,
8+
getContainer: PropTypes.func.isRequired,
9+
}
10+
11+
componentDidMount() {
12+
this.createContainer();
13+
}
14+
15+
componentWillUnmount() {
16+
this.removeContainer();
17+
}
18+
19+
createContainer() {
20+
this._container = this.props.getContainer();
21+
this.forceUpdate();
22+
}
23+
24+
removeContainer() {
25+
if (this._container) {
26+
this._container.parentNode.removeChild(this._container);
27+
}
28+
}
29+
30+
render() {
31+
if (this._container) {
32+
return createPortal(this.props.getComponent(), this._container);
33+
}
34+
return null;
35+
}
36+
}

src/index.js

Lines changed: 74 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3-
import { findDOMNode } from 'react-dom';
3+
import { findDOMNode, createPortal } from 'react-dom';
44
import createReactClass from 'create-react-class';
55
import contains from 'rc-util/lib/Dom/contains';
66
import addEventListener from 'rc-util/lib/Dom/addEventListener';
77
import Popup from './Popup';
88
import { getAlignFromPlacement, getPopupClassNameFromAlign } from './utils';
99
import getContainerRenderMixin from 'rc-util/lib/getContainerRenderMixin';
10+
import Portal from './Portal';
1011

1112
function noop() {
1213
}
@@ -22,6 +23,26 @@ function returnDocument() {
2223
const ALL_HANDLERS = ['onClick', 'onMouseDown', 'onTouchStart', 'onMouseEnter',
2324
'onMouseLeave', 'onFocus', 'onBlur'];
2425

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+
2546
const Trigger = createReactClass({
2647
displayName: 'Trigger',
2748
propTypes: {
@@ -66,30 +87,7 @@ const Trigger = createReactClass({
6687
maskAnimation: PropTypes.string,
6788
},
6889

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,
9391

9492
getDefaultProps() {
9593
return {
@@ -154,11 +152,16 @@ const Trigger = createReactClass({
154152
componentDidUpdate(_, prevState) {
155153
const props = this.props;
156154
const state = this.state;
157-
this.renderComponent(null, () => {
155+
const triggerAfterPopupVisibleChange = () => {
158156
if (prevState.popupVisible !== state.popupVisible) {
159157
props.afterPopupVisibleChange(state.popupVisible);
160158
}
161-
});
159+
};
160+
if (!IS_REACT_16) {
161+
this.renderComponent(null, triggerAfterPopupVisibleChange);
162+
} else {
163+
triggerAfterPopupVisibleChange();
164+
}
162165

163166
// We must listen to `mousedown` or `touchstart`, edge case:
164167
// https://github.com/ant-design/ant-design/issues/5804
@@ -341,12 +344,28 @@ const Trigger = createReactClass({
341344
transitionName={props.popupTransitionName}
342345
maskAnimation={props.maskAnimation}
343346
maskTransitionName={props.maskTransitionName}
347+
ref={this.savePopup}
344348
>
345349
{typeof props.popup === 'function' ? props.popup() : props.popup}
346350
</Popup>
347351
);
348352
},
349353

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+
350369
setPopupVisible(popupVisible) {
351370
this.clearDelayTimer();
352371
if (this.state.popupVisible !== popupVisible) {
@@ -450,11 +469,18 @@ const Trigger = createReactClass({
450469
this.setPopupVisible(false);
451470
},
452471

472+
savePopup(node) {
473+
if (IS_REACT_16) {
474+
this._component = node;
475+
}
476+
},
477+
453478
render() {
479+
const { popupVisible } = this.state;
454480
const props = this.props;
455481
const children = props.children;
456482
const child = React.Children.only(children);
457-
const newChildProps = {};
483+
const newChildProps = { key: 'target' };
458484
if (this.isClickToHide() || this.isClickToShow()) {
459485
newChildProps.onClick = this.onClick;
460486
newChildProps.onMouseDown = this.onMouseDown;
@@ -482,7 +508,26 @@ const Trigger = createReactClass({
482508
newChildProps.onBlur = this.createTwoChains('onBlur');
483509
}
484510

485-
return React.cloneElement(child, newChildProps);
511+
if (!IS_REACT_16) {
512+
return React.cloneElement(child, newChildProps);
513+
}
514+
515+
let portal;
516+
// prevent unmounting when visible change to false
517+
if (popupVisible || this._component) {
518+
portal = (
519+
<Portal
520+
key="portal"
521+
getComponent={this.getComponent}
522+
getContainer={this.getContainer}
523+
/>
524+
);
525+
}
526+
527+
return [
528+
React.cloneElement(child, newChildProps),
529+
portal,
530+
];
486531
},
487532
});
488533

tests/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
/* eslint no-console:0 */
22

3+
import 'core-js/es6/map';
4+
import 'core-js/es6/set';
35
import expect from 'expect.js';
46
import React from 'react';
57
import ReactDOM from 'react-dom';

0 commit comments

Comments
 (0)