380 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import _extends from 'babel-runtime/helpers/extends';
import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties';
import _classCallCheck from 'babel-runtime/helpers/classCallCheck';
import _possibleConstructorReturn from 'babel-runtime/helpers/possibleConstructorReturn';
import _inherits from 'babel-runtime/helpers/inherits';
var _class, _temp;
import React, { Component, Children } from 'react';
import { findDOMNode } from 'react-dom';
import PropTypes from 'prop-types';
import { func, KEYCODE } from '../util';
import Overlay from './overlay';
var noop = func.noop,
makeChain = func.makeChain,
bindCtx = func.bindCtx;
/**
* Overlay.Popup
* @description 继承 Overlay 的 API除非特别说明
* */
var Popup = (_temp = _class = function (_Component) {
_inherits(Popup, _Component);
function Popup(props) {
_classCallCheck(this, Popup);
var _this = _possibleConstructorReturn(this, _Component.call(this, props));
_this.state = {
visible: typeof props.visible === 'undefined' ? props.defaultVisible : props.visible
};
bindCtx(_this, ['handleTriggerClick', 'handleTriggerKeyDown', 'handleTriggerMouseEnter', 'handleTriggerMouseLeave', 'handleTriggerFocus', 'handleTriggerBlur', 'handleContentMouseEnter', 'handleContentMouseLeave', 'handleContentMouseDown', 'handleRequestClose', 'handleMaskMouseEnter', 'handleMaskMouseLeave']);
return _this;
}
Popup.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
if ('visible' in nextProps) {
this.setState({
visible: nextProps.visible
});
}
};
Popup.prototype.componentWillUnmount = function componentWillUnmount() {
var _this2 = this;
['_timer', '_hideTimer', '_showTimer'].forEach(function (time) {
_this2[time] && clearTimeout(_this2[time]);
});
};
Popup.prototype.handleVisibleChange = function handleVisibleChange(visible, type, e) {
if (!('visible' in this.props)) {
this.setState({
visible: visible
});
}
this.props.onVisibleChange(visible, type, e);
};
Popup.prototype.handleTriggerClick = function handleTriggerClick(e) {
if (this.state.visible && !this.props.canCloseByTrigger) {
return;
}
this.handleVisibleChange(!this.state.visible, 'fromTrigger', e);
};
Popup.prototype.handleTriggerKeyDown = function handleTriggerKeyDown(e) {
var triggerClickKeycode = this.props.triggerClickKeycode;
var keycodes = Array.isArray(triggerClickKeycode) ? triggerClickKeycode : [triggerClickKeycode];
if (keycodes.includes(e.keyCode)) {
e.preventDefault();
this.handleTriggerClick(e);
}
};
Popup.prototype.handleTriggerMouseEnter = function handleTriggerMouseEnter(e) {
var _this3 = this;
this._mouseNotFirstOnMask = false;
if (this._hideTimer) {
clearTimeout(this._hideTimer);
this._hideTimer = null;
}
if (this._showTimer) {
clearTimeout(this._showTimer);
this._showTimer = null;
}
if (!this.state.visible) {
this._showTimer = setTimeout(function () {
_this3.handleVisibleChange(true, 'fromTrigger', e);
}, this.props.delay);
}
};
Popup.prototype.handleTriggerMouseLeave = function handleTriggerMouseLeave(e, type) {
var _this4 = this;
if (this._showTimer) {
clearTimeout(this._showTimer);
this._showTimer = null;
}
if (this.state.visible) {
this._hideTimer = setTimeout(function () {
_this4.handleVisibleChange(false, type || 'fromTrigger', e);
}, this.props.delay);
}
};
Popup.prototype.handleTriggerFocus = function handleTriggerFocus(e) {
this.handleVisibleChange(true, 'fromTrigger', e);
};
Popup.prototype.handleTriggerBlur = function handleTriggerBlur(e) {
if (!this._isForwardContent) {
this.handleVisibleChange(false, 'fromTrigger', e);
}
this._isForwardContent = false;
};
Popup.prototype.handleContentMouseDown = function handleContentMouseDown() {
this._isForwardContent = true;
};
Popup.prototype.handleContentMouseEnter = function handleContentMouseEnter() {
clearTimeout(this._hideTimer);
};
Popup.prototype.handleContentMouseLeave = function handleContentMouseLeave(e) {
this.handleTriggerMouseLeave(e, 'fromContent');
};
Popup.prototype.handleMaskMouseEnter = function handleMaskMouseEnter() {
if (!this._mouseNotFirstOnMask) {
clearTimeout(this._hideTimer);
this._hideTimer = null;
this._mouseNotFirstOnMask = false;
}
};
Popup.prototype.handleMaskMouseLeave = function handleMaskMouseLeave() {
this._mouseNotFirstOnMask = true;
};
Popup.prototype.handleRequestClose = function handleRequestClose(type, e) {
this.handleVisibleChange(false, type, e);
};
Popup.prototype.renderTrigger = function renderTrigger() {
var _this5 = this;
var _props = this.props,
trigger = _props.trigger,
disabled = _props.disabled;
var props = {
key: 'trigger',
'aria-haspopup': true,
'aria-expanded': this.state.visible
};
if (!this.state.visible) {
props['aria-describedby'] = undefined;
}
if (!disabled) {
var triggerType = this.props.triggerType;
var triggerTypes = Array.isArray(triggerType) ? triggerType : [triggerType];
var _ref = trigger && trigger.props || {},
onClick = _ref.onClick,
onKeyDown = _ref.onKeyDown,
onMouseEnter = _ref.onMouseEnter,
onMouseLeave = _ref.onMouseLeave,
onFocus = _ref.onFocus,
onBlur = _ref.onBlur;
triggerTypes.forEach(function (triggerType) {
switch (triggerType) {
case 'click':
props.onClick = makeChain(_this5.handleTriggerClick, onClick);
props.onKeyDown = makeChain(_this5.handleTriggerKeyDown, onKeyDown);
break;
case 'hover':
props.onMouseEnter = makeChain(_this5.handleTriggerMouseEnter, onMouseEnter);
props.onMouseLeave = makeChain(_this5.handleTriggerMouseLeave, onMouseLeave);
break;
case 'focus':
props.onFocus = makeChain(_this5.handleTriggerFocus, onFocus);
props.onBlur = makeChain(_this5.handleTriggerBlur, onBlur);
break;
default:
break;
}
});
}
return trigger && React.cloneElement(trigger, props);
};
Popup.prototype.renderContent = function renderContent() {
var _this6 = this;
var _props2 = this.props,
children = _props2.children,
triggerType = _props2.triggerType;
var triggerTypes = Array.isArray(triggerType) ? triggerType : [triggerType];
var content = Children.only(children);
var _content$props = content.props,
onMouseDown = _content$props.onMouseDown,
onMouseEnter = _content$props.onMouseEnter,
onMouseLeave = _content$props.onMouseLeave;
var props = {
key: 'portal'
};
triggerTypes.forEach(function (triggerType) {
switch (triggerType) {
case 'focus':
props.onMouseDown = makeChain(_this6.handleContentMouseDown, onMouseDown);
break;
case 'hover':
props.onMouseEnter = makeChain(_this6.handleContentMouseEnter, onMouseEnter);
props.onMouseLeave = makeChain(_this6.handleContentMouseLeave, onMouseLeave);
break;
default:
break;
}
});
return React.cloneElement(content, props);
};
Popup.prototype.renderPortal = function renderPortal() {
var _this7 = this;
var _props3 = this.props,
target = _props3.target,
safeNode = _props3.safeNode,
followTrigger = _props3.followTrigger,
triggerType = _props3.triggerType,
hasMask = _props3.hasMask,
wrapperStyle = _props3.wrapperStyle,
others = _objectWithoutProperties(_props3, ['target', 'safeNode', 'followTrigger', 'triggerType', 'hasMask', 'wrapperStyle']);
var container = this.props.container;
var findTriggerNode = function findTriggerNode() {
return findDOMNode(_this7) || {};
};
var safeNodes = Array.isArray(safeNode) ? [].concat(safeNode) : [safeNode];
safeNodes.unshift(findTriggerNode);
var newWrapperStyle = wrapperStyle || {};
if (followTrigger) {
container = function container(trigger) {
return trigger && trigger.parentNode || trigger;
};
newWrapperStyle.position = 'relative';
}
if (triggerType === 'hover' && hasMask) {
others.onMaskMouseEnter = this.handleMaskMouseEnter;
others.onMaskMouseLeave = this.handleMaskMouseLeave;
}
return React.createElement(
Overlay,
_extends({}, others, {
key: 'overlay',
ref: function ref(overlay) {
return _this7.overlay = overlay;
},
visible: this.state.visible,
target: target || findTriggerNode,
container: container,
safeNode: safeNodes,
wrapperStyle: newWrapperStyle,
triggerType: triggerType,
hasMask: hasMask,
onRequestClose: this.handleRequestClose
}),
this.renderContent()
);
};
Popup.prototype.render = function render() {
return [this.renderTrigger(), this.renderPortal()];
};
return Popup;
}(Component), _class.propTypes = {
/**
* 弹层内容
*/
children: PropTypes.node,
/**
* 触发弹层显示或隐藏的元素
*/
trigger: PropTypes.element,
/**
* 触发弹层显示或隐藏的操作类型,可以是 'click''hover''focus',或者它们组成的数组,如 ['hover', 'focus']
*/
triggerType: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
/**
* 当 triggerType 为 click 时才生效,可自定义触发弹层显示的键盘码
*/
triggerClickKeycode: PropTypes.oneOfType([PropTypes.number, PropTypes.array]),
/**
* 弹层当前是否显示
*/
visible: PropTypes.bool,
/**
* 弹层默认是否显示
*/
defaultVisible: PropTypes.bool,
/**
* 弹层显示或隐藏时触发的回调函数
* @param {Boolean} visible 弹层是否显示
* @param {String} type 触发弹层显示或隐藏的来源 fromTrigger 表示由trigger的点击触发 docClick 表示由document的点击触发
* @param {Object} e DOM事件
*/
onVisibleChange: PropTypes.func,
/**
* 设置此属性,弹层无法显示或隐藏
*/
disabled: PropTypes.bool,
autoFit: PropTypes.bool,
/**
* 弹层显示或隐藏的延时时间(以毫秒为单位),在 triggerType 被设置为 hover 时生效
*/
delay: PropTypes.number,
/**
* trigger 是否可以关闭弹层
*/
canCloseByTrigger: PropTypes.bool,
/**
* 弹层定位的参照元素
* @default target 属性,即触发元素
*/
target: PropTypes.any,
safeNode: PropTypes.any,
/**
* 是否跟随trigger滚动
*/
followTrigger: PropTypes.bool,
container: PropTypes.any,
hasMask: PropTypes.bool,
wrapperStyle: PropTypes.object,
rtl: PropTypes.bool
}, _class.defaultProps = {
triggerType: 'hover',
triggerClickKeycode: [KEYCODE.SPACE, KEYCODE.ENTER],
defaultVisible: false,
onVisibleChange: noop,
disabled: false,
autoFit: false,
delay: 200,
canCloseByTrigger: true,
followTrigger: false,
container: function container() {
return document.body;
},
rtl: false
}, _temp);
Popup.displayName = 'Popup';
export { Popup as default };