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;