459 lines
15 KiB
JavaScript
459 lines
15 KiB
JavaScript
import _extends from 'babel-runtime/helpers/extends';
|
|
import React from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import { findDOMNode } from 'react-dom';
|
|
import createReactClass from 'create-react-class';
|
|
import contains from 'rc-util/es/Dom/contains';
|
|
import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
|
import Popup from './Popup';
|
|
import { getAlignFromPlacement, getPopupClassNameFromAlign as _getPopupClassNameFromAlign } from './utils';
|
|
import getContainerRenderMixin from 'rc-util/lib/getContainerRenderMixin';
|
|
|
|
function noop() {}
|
|
|
|
function returnEmptyString() {
|
|
return '';
|
|
}
|
|
|
|
function returnDocument() {
|
|
return window.document;
|
|
}
|
|
|
|
var isMobile = typeof navigator !== 'undefined' && !!navigator.userAgent.match(/(Android|iPhone|iPad|iPod|iOS|UCWEB)/i);
|
|
|
|
var ALL_HANDLERS = ['onClick', 'onMouseDown', 'onTouchStart', 'onMouseEnter', 'onMouseLeave', 'onFocus', 'onBlur'];
|
|
|
|
var Trigger = createReactClass({
|
|
displayName: 'Trigger',
|
|
propTypes: {
|
|
children: PropTypes.any,
|
|
action: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
|
|
showAction: PropTypes.any,
|
|
hideAction: PropTypes.any,
|
|
getPopupClassNameFromAlign: PropTypes.any,
|
|
onPopupVisibleChange: PropTypes.func,
|
|
afterPopupVisibleChange: PropTypes.func,
|
|
popup: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
|
|
popupStyle: PropTypes.object,
|
|
prefixCls: PropTypes.string,
|
|
popupClassName: PropTypes.string,
|
|
popupPlacement: PropTypes.string,
|
|
builtinPlacements: PropTypes.object,
|
|
popupTransitionName: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
|
popupAnimation: PropTypes.any,
|
|
mouseEnterDelay: PropTypes.number,
|
|
mouseLeaveDelay: PropTypes.number,
|
|
zIndex: PropTypes.number,
|
|
focusDelay: PropTypes.number,
|
|
blurDelay: PropTypes.number,
|
|
getPopupContainer: PropTypes.func,
|
|
getDocument: PropTypes.func,
|
|
destroyPopupOnHide: PropTypes.bool,
|
|
mask: PropTypes.bool,
|
|
maskClosable: PropTypes.bool,
|
|
onPopupAlign: PropTypes.func,
|
|
popupAlign: PropTypes.object,
|
|
popupVisible: PropTypes.bool,
|
|
maskTransitionName: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
|
maskAnimation: PropTypes.string
|
|
},
|
|
|
|
mixins: [getContainerRenderMixin({
|
|
autoMount: false,
|
|
|
|
isVisible: function isVisible(instance) {
|
|
return instance.state.popupVisible;
|
|
},
|
|
getContainer: function getContainer(instance) {
|
|
var props = instance.props;
|
|
|
|
var popupContainer = document.createElement('div');
|
|
popupContainer.style.position = 'absolute';
|
|
popupContainer.style.top = '0';
|
|
popupContainer.style.left = '0';
|
|
popupContainer.style.width = '100%';
|
|
var mountNode = props.getPopupContainer ? props.getPopupContainer(findDOMNode(instance)) : props.getDocument().body;
|
|
mountNode.appendChild(popupContainer);
|
|
return popupContainer;
|
|
}
|
|
})],
|
|
|
|
getDefaultProps: function getDefaultProps() {
|
|
return {
|
|
prefixCls: 'rc-trigger-popup',
|
|
getPopupClassNameFromAlign: returnEmptyString,
|
|
getDocument: returnDocument,
|
|
onPopupVisibleChange: noop,
|
|
afterPopupVisibleChange: noop,
|
|
onPopupAlign: noop,
|
|
popupClassName: '',
|
|
mouseEnterDelay: 0,
|
|
mouseLeaveDelay: 0.1,
|
|
focusDelay: 0,
|
|
blurDelay: 0.15,
|
|
popupStyle: {},
|
|
destroyPopupOnHide: false,
|
|
popupAlign: {},
|
|
defaultPopupVisible: false,
|
|
mask: false,
|
|
maskClosable: true,
|
|
action: [],
|
|
showAction: [],
|
|
hideAction: []
|
|
};
|
|
},
|
|
getInitialState: function getInitialState() {
|
|
var props = this.props;
|
|
var popupVisible = void 0;
|
|
if ('popupVisible' in props) {
|
|
popupVisible = !!props.popupVisible;
|
|
} else {
|
|
popupVisible = !!props.defaultPopupVisible;
|
|
}
|
|
return {
|
|
popupVisible: popupVisible
|
|
};
|
|
},
|
|
componentWillMount: function componentWillMount() {
|
|
var _this = this;
|
|
|
|
ALL_HANDLERS.forEach(function (h) {
|
|
_this['fire' + h] = function (e) {
|
|
_this.fireEvents(h, e);
|
|
};
|
|
});
|
|
},
|
|
componentDidMount: function componentDidMount() {
|
|
this.componentDidUpdate({}, {
|
|
popupVisible: this.state.popupVisible
|
|
});
|
|
},
|
|
componentWillReceiveProps: function componentWillReceiveProps(_ref) {
|
|
var popupVisible = _ref.popupVisible;
|
|
|
|
if (popupVisible !== undefined) {
|
|
this.setState({
|
|
popupVisible: popupVisible
|
|
});
|
|
}
|
|
},
|
|
componentDidUpdate: function componentDidUpdate(_, prevState) {
|
|
var props = this.props;
|
|
var state = this.state;
|
|
this.renderComponent(null, function () {
|
|
if (prevState.popupVisible !== state.popupVisible) {
|
|
props.afterPopupVisibleChange(state.popupVisible);
|
|
}
|
|
});
|
|
|
|
if (state.popupVisible) {
|
|
var currentDocument = void 0;
|
|
if (!this.clickOutsideHandler && this.isClickToHide()) {
|
|
currentDocument = props.getDocument();
|
|
this.clickOutsideHandler = addEventListener(currentDocument, 'mousedown', this.onDocumentClick);
|
|
}
|
|
if (!this.touchOutsideHandler && isMobile) {
|
|
currentDocument = currentDocument || props.getDocument();
|
|
this.touchOutsideHandler = addEventListener(currentDocument, 'click', this.onDocumentClick);
|
|
}
|
|
return;
|
|
}
|
|
|
|
this.clearOutsideHandler();
|
|
},
|
|
componentWillUnmount: function componentWillUnmount() {
|
|
this.clearDelayTimer();
|
|
this.clearOutsideHandler();
|
|
},
|
|
onMouseEnter: function onMouseEnter(e) {
|
|
this.fireEvents('onMouseEnter', e);
|
|
this.delaySetPopupVisible(true, this.props.mouseEnterDelay);
|
|
},
|
|
onMouseLeave: function onMouseLeave(e) {
|
|
this.fireEvents('onMouseLeave', e);
|
|
this.delaySetPopupVisible(false, this.props.mouseLeaveDelay);
|
|
},
|
|
onPopupMouseEnter: function onPopupMouseEnter() {
|
|
this.clearDelayTimer();
|
|
},
|
|
onPopupMouseLeave: function onPopupMouseLeave(e) {
|
|
if (e.relatedTarget && !e.relatedTarget.setTimeout && this._component && this._component.getPopupDomNode && contains(this._component.getPopupDomNode(), e.relatedTarget)) {
|
|
return;
|
|
}
|
|
this.delaySetPopupVisible(false, this.props.mouseLeaveDelay);
|
|
},
|
|
onFocus: function onFocus(e) {
|
|
this.fireEvents('onFocus', e);
|
|
this.clearDelayTimer();
|
|
if (this.isFocusToShow()) {
|
|
this.focusTime = Date.now();
|
|
this.delaySetPopupVisible(true, this.props.focusDelay);
|
|
}
|
|
},
|
|
onMouseDown: function onMouseDown(e) {
|
|
this.fireEvents('onMouseDown', e);
|
|
this.preClickTime = Date.now();
|
|
},
|
|
onTouchStart: function onTouchStart(e) {
|
|
this.fireEvents('onTouchStart', e);
|
|
this.preTouchTime = Date.now();
|
|
},
|
|
onBlur: function onBlur(e) {
|
|
this.fireEvents('onBlur', e);
|
|
this.clearDelayTimer();
|
|
if (this.isBlurToHide()) {
|
|
this.delaySetPopupVisible(false, this.props.blurDelay);
|
|
}
|
|
},
|
|
onClick: function onClick(event) {
|
|
this.fireEvents('onClick', event);
|
|
if (this.focusTime) {
|
|
var preTime = void 0;
|
|
if (this.preClickTime && this.preTouchTime) {
|
|
preTime = Math.min(this.preClickTime, this.preTouchTime);
|
|
} else if (this.preClickTime) {
|
|
preTime = this.preClickTime;
|
|
} else if (this.preTouchTime) {
|
|
preTime = this.preTouchTime;
|
|
}
|
|
if (Math.abs(preTime - this.focusTime) < 20) {
|
|
return;
|
|
}
|
|
this.focusTime = 0;
|
|
}
|
|
this.preClickTime = 0;
|
|
this.preTouchTime = 0;
|
|
event.preventDefault();
|
|
var nextVisible = !this.state.popupVisible;
|
|
if (this.isClickToHide() && !nextVisible || nextVisible && this.isClickToShow()) {
|
|
this.setPopupVisible(!this.state.popupVisible);
|
|
}
|
|
},
|
|
onDocumentClick: function onDocumentClick(event) {
|
|
if (this.props.mask && !this.props.maskClosable) {
|
|
return;
|
|
}
|
|
var target = event.target;
|
|
var root = findDOMNode(this);
|
|
var popupNode = this.getPopupDomNode();
|
|
if (!contains(root, target) && !contains(popupNode, target)) {
|
|
this.close();
|
|
}
|
|
},
|
|
getPopupDomNode: function getPopupDomNode() {
|
|
if (this._component && this._component.getPopupDomNode) {
|
|
return this._component.getPopupDomNode();
|
|
}
|
|
return null;
|
|
},
|
|
getRootDomNode: function getRootDomNode() {
|
|
return findDOMNode(this);
|
|
},
|
|
getPopupClassNameFromAlign: function getPopupClassNameFromAlign(align) {
|
|
var className = [];
|
|
var props = this.props;
|
|
var popupPlacement = props.popupPlacement,
|
|
builtinPlacements = props.builtinPlacements,
|
|
prefixCls = props.prefixCls;
|
|
|
|
if (popupPlacement && builtinPlacements) {
|
|
className.push(_getPopupClassNameFromAlign(builtinPlacements, prefixCls, align));
|
|
}
|
|
if (props.getPopupClassNameFromAlign) {
|
|
className.push(props.getPopupClassNameFromAlign(align));
|
|
}
|
|
return className.join(' ');
|
|
},
|
|
getPopupAlign: function getPopupAlign() {
|
|
var props = this.props;
|
|
var popupPlacement = props.popupPlacement,
|
|
popupAlign = props.popupAlign,
|
|
builtinPlacements = props.builtinPlacements;
|
|
|
|
if (popupPlacement && builtinPlacements) {
|
|
return getAlignFromPlacement(builtinPlacements, popupPlacement, popupAlign);
|
|
}
|
|
return popupAlign;
|
|
},
|
|
getComponent: function getComponent() {
|
|
var props = this.props,
|
|
state = this.state;
|
|
|
|
var mouseProps = {};
|
|
if (this.isMouseEnterToShow()) {
|
|
mouseProps.onMouseEnter = this.onPopupMouseEnter;
|
|
}
|
|
if (this.isMouseLeaveToHide()) {
|
|
mouseProps.onMouseLeave = this.onPopupMouseLeave;
|
|
}
|
|
return React.createElement(
|
|
Popup,
|
|
_extends({
|
|
prefixCls: props.prefixCls,
|
|
destroyPopupOnHide: props.destroyPopupOnHide,
|
|
visible: state.popupVisible,
|
|
className: props.popupClassName,
|
|
action: props.action,
|
|
align: this.getPopupAlign(),
|
|
onAlign: props.onPopupAlign,
|
|
animation: props.popupAnimation,
|
|
getClassNameFromAlign: this.getPopupClassNameFromAlign
|
|
}, mouseProps, {
|
|
getRootDomNode: this.getRootDomNode,
|
|
style: props.popupStyle,
|
|
mask: props.mask,
|
|
zIndex: props.zIndex,
|
|
transitionName: props.popupTransitionName,
|
|
maskAnimation: props.maskAnimation,
|
|
maskTransitionName: props.maskTransitionName
|
|
}),
|
|
typeof props.popup === 'function' ? props.popup() : props.popup
|
|
);
|
|
},
|
|
setPopupVisible: function setPopupVisible(popupVisible) {
|
|
this.clearDelayTimer();
|
|
if (this.state.popupVisible !== popupVisible) {
|
|
if (!('popupVisible' in this.props)) {
|
|
this.setState({
|
|
popupVisible: popupVisible
|
|
});
|
|
}
|
|
this.props.onPopupVisibleChange(popupVisible);
|
|
}
|
|
},
|
|
delaySetPopupVisible: function delaySetPopupVisible(visible, delayS) {
|
|
var _this2 = this;
|
|
|
|
var delay = delayS * 1000;
|
|
this.clearDelayTimer();
|
|
if (delay) {
|
|
this.delayTimer = setTimeout(function () {
|
|
_this2.setPopupVisible(visible);
|
|
_this2.clearDelayTimer();
|
|
}, delay);
|
|
} else {
|
|
this.setPopupVisible(visible);
|
|
}
|
|
},
|
|
clearDelayTimer: function clearDelayTimer() {
|
|
if (this.delayTimer) {
|
|
clearTimeout(this.delayTimer);
|
|
this.delayTimer = null;
|
|
}
|
|
},
|
|
clearOutsideHandler: function clearOutsideHandler() {
|
|
if (this.clickOutsideHandler) {
|
|
this.clickOutsideHandler.remove();
|
|
this.clickOutsideHandler = null;
|
|
}
|
|
|
|
if (this.touchOutsideHandler) {
|
|
this.touchOutsideHandler.remove();
|
|
this.touchOutsideHandler = null;
|
|
}
|
|
},
|
|
createTwoChains: function createTwoChains(event) {
|
|
var childPros = this.props.children.props;
|
|
var props = this.props;
|
|
if (childPros[event] && props[event]) {
|
|
return this['fire' + event];
|
|
}
|
|
return childPros[event] || props[event];
|
|
},
|
|
isClickToShow: function isClickToShow() {
|
|
var _props = this.props,
|
|
action = _props.action,
|
|
showAction = _props.showAction;
|
|
|
|
return action.indexOf('click') !== -1 || showAction.indexOf('click') !== -1;
|
|
},
|
|
isClickToHide: function isClickToHide() {
|
|
var _props2 = this.props,
|
|
action = _props2.action,
|
|
hideAction = _props2.hideAction;
|
|
|
|
return action.indexOf('click') !== -1 || hideAction.indexOf('click') !== -1;
|
|
},
|
|
isMouseEnterToShow: function isMouseEnterToShow() {
|
|
var _props3 = this.props,
|
|
action = _props3.action,
|
|
showAction = _props3.showAction;
|
|
|
|
return action.indexOf('hover') !== -1 || showAction.indexOf('mouseEnter') !== -1;
|
|
},
|
|
isMouseLeaveToHide: function isMouseLeaveToHide() {
|
|
var _props4 = this.props,
|
|
action = _props4.action,
|
|
hideAction = _props4.hideAction;
|
|
|
|
return action.indexOf('hover') !== -1 || hideAction.indexOf('mouseLeave') !== -1;
|
|
},
|
|
isFocusToShow: function isFocusToShow() {
|
|
var _props5 = this.props,
|
|
action = _props5.action,
|
|
showAction = _props5.showAction;
|
|
|
|
return action.indexOf('focus') !== -1 || showAction.indexOf('focus') !== -1;
|
|
},
|
|
isBlurToHide: function isBlurToHide() {
|
|
var _props6 = this.props,
|
|
action = _props6.action,
|
|
hideAction = _props6.hideAction;
|
|
|
|
return action.indexOf('focus') !== -1 || hideAction.indexOf('blur') !== -1;
|
|
},
|
|
forcePopupAlign: function forcePopupAlign() {
|
|
if (this.state.popupVisible && this._component && this._component.alignInstance) {
|
|
this._component.alignInstance.forceAlign();
|
|
}
|
|
},
|
|
fireEvents: function fireEvents(type, e) {
|
|
var childCallback = this.props.children.props[type];
|
|
if (childCallback) {
|
|
childCallback(e);
|
|
}
|
|
var callback = this.props[type];
|
|
if (callback) {
|
|
callback(e);
|
|
}
|
|
},
|
|
close: function close() {
|
|
this.setPopupVisible(false);
|
|
},
|
|
render: function render() {
|
|
var props = this.props;
|
|
var children = props.children;
|
|
var child = React.Children.only(children);
|
|
var newChildProps = {};
|
|
if (this.isClickToHide() || this.isClickToShow()) {
|
|
newChildProps.onClick = this.onClick;
|
|
newChildProps.onMouseDown = this.onMouseDown;
|
|
newChildProps.onTouchStart = this.onTouchStart;
|
|
} else {
|
|
newChildProps.onClick = this.createTwoChains('onClick');
|
|
newChildProps.onMouseDown = this.createTwoChains('onMouseDown');
|
|
newChildProps.onTouchStart = this.createTwoChains('onTouchStart');
|
|
}
|
|
if (this.isMouseEnterToShow()) {
|
|
newChildProps.onMouseEnter = this.onMouseEnter;
|
|
} else {
|
|
newChildProps.onMouseEnter = this.createTwoChains('onMouseEnter');
|
|
}
|
|
if (this.isMouseLeaveToHide()) {
|
|
newChildProps.onMouseLeave = this.onMouseLeave;
|
|
} else {
|
|
newChildProps.onMouseLeave = this.createTwoChains('onMouseLeave');
|
|
}
|
|
if (this.isFocusToShow() || this.isBlurToHide()) {
|
|
newChildProps.onFocus = this.onFocus;
|
|
newChildProps.onBlur = this.onBlur;
|
|
} else {
|
|
newChildProps.onFocus = this.createTwoChains('onFocus');
|
|
newChildProps.onBlur = this.createTwoChains('onBlur');
|
|
}
|
|
|
|
return React.cloneElement(child, newChildProps);
|
|
}
|
|
});
|
|
|
|
export default Trigger; |