UNPKG

zent

Version:

一套前端设计语言和基于React的实现

472 lines (370 loc) 14.3 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.PopoverContextType = undefined; var _extends2 = require('babel-runtime/helpers/extends'); var _extends3 = _interopRequireDefault(_extends2); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _inherits2 = require('babel-runtime/helpers/inherits'); var _inherits3 = _interopRequireDefault(_inherits2); var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _reactDom = require('react-dom'); var _reactDom2 = _interopRequireDefault(_reactDom); var _classnames = require('classnames'); var _classnames2 = _interopRequireDefault(_classnames); var _noop = require('lodash/noop'); var _noop2 = _interopRequireDefault(_noop); var _uniqueId = require('lodash/uniqueId'); var _uniqueId2 = _interopRequireDefault(_uniqueId); var _isFunction = require('lodash/isFunction'); var _isFunction2 = _interopRequireDefault(_isFunction); var _isBoolean = require('lodash/isBoolean'); var _isBoolean2 = _interopRequireDefault(_isBoolean); var _isPromise = require('../utils/isPromise'); var _isPromise2 = _interopRequireDefault(_isPromise); var _propTypes = require('prop-types'); var _propTypes2 = _interopRequireDefault(_propTypes); var _kindOf = require('../utils/kindOf'); var _kindOf2 = _interopRequireDefault(_kindOf); var _getWidth = require('../utils/getWidth'); var _getWidth2 = _interopRequireDefault(_getWidth); var _Content = require('./Content'); var _Content2 = _interopRequireDefault(_Content); var _Trigger = require('./trigger/Trigger'); var _Trigger2 = _interopRequireDefault(_Trigger); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var SKIPPED = function SKIPPED() {}; /** * 设计: * * Popover组件只是一个壳子,负责组装Trigger和Content。 * * 弹层实际的打开/关闭都是Content完成的,而什么情况打开弹层是Trigger控制的。 * * Popover 组件是一个递归的组件,支持嵌套。 * * * context context * ------> ------> * Popover Popover child Popover grand-child ...... * <------ <------ * isOutsideStacked isOutsideStacked * */ function handleBeforeHook(beforeFn, arity, continuation, escape) { // 有参数,传入continuation,由外部去控制何时调用 // escapse 用来终止 onChange 操作 if (arity === 1) { return beforeFn(continuation); } else if (arity >= 2) { return beforeFn(continuation, escape); } // 无参数,如果返回Promise那么resolve后调用continuation, reject 的话调用 escape; // 如果返回不是Promise,直接调用Promise var mayBePromise = beforeFn(); if (!(0, _isPromise2['default'])(mayBePromise) && mayBePromise !== SKIPPED) { return continuation(); } mayBePromise.then(continuation, escape); } var PopoverContextType = exports.PopoverContextType = { _zentPopover: _propTypes2['default'].shape({ close: _propTypes2['default'].func.isRequired, open: _propTypes2['default'].func.isRequired, getContentNode: _propTypes2['default'].func.isRequired, getTriggerNode: _propTypes2['default'].func.isRequired, // 用于维护 Popover 栈,处理嵌套的问题 registerDescendant: _propTypes2['default'].func, unregisterDescendant: _propTypes2['default'].func }) }; var Popover = function (_ref) { (0, _inherits3['default'])(Popover, _ref); (0, _createClass3['default'])(Popover, [{ key: 'getChildContext', value: function getChildContext() { return { _zentPopover: { close: this.close, open: this.open, getContentNode: this.getPopoverNode, getTriggerNode: this.getTriggerNode, registerDescendant: this.registerDescendant, unregisterDescendant: this.unregisterDescendant } }; } }]); function Popover(props) { (0, _classCallCheck3['default'])(this, Popover); // id用来唯一标识popover实例 var _this = (0, _possibleConstructorReturn3['default'])(this, (Popover.__proto__ || Object.getPrototypeOf(Popover)).call(this, props)); _initialiseProps.call(_this); _this.id = (0, _uniqueId2['default'])(props.prefix + '-popover-internal-id-'); // 记录 Popover 子孙 _this.descendants = []; if (!_this.isVisibilityControlled(props)) { _this.state = { visible: false }; } _this.isUnmounted = false; return _this; } (0, _createClass3['default'])(Popover, [{ key: 'isVisibilityControlled', value: function isVisibilityControlled(props) { var _ref2 = props || this.props, visible = _ref2.visible, onVisibleChange = _ref2.onVisibleChange; var hasOnChange = (0, _isFunction2['default'])(onVisibleChange); var hasVisible = (0, _isBoolean2['default'])(visible); if (hasVisible && !hasOnChange || hasOnChange && !hasVisible) { throw new Error('visible and onVisibleChange must be used together'); } return hasVisible && hasOnChange; } // Popover up in the tree will call this method to see if the node lies outside }, { key: 'validateChildren', value: function validateChildren() { var children = this.props.children; var childArray = _react.Children.toArray(children); if (childArray.length !== 2) { throw new Error('There must be one and only one trigger and content in Popover'); } var _childArray$reduce = childArray.reduce(function (state, c) { var type = c.type; if ((0, _kindOf2['default'])(type, _Trigger2['default'])) { state.trigger = c; } else if ((0, _kindOf2['default'])(type, _Content2['default'])) { state.content = c; } return state; }, { trigger: null, content: null }), trigger = _childArray$reduce.trigger, content = _childArray$reduce.content; if (!trigger) { throw new Error('Missing trigger in Popover'); } if (!content) { throw new Error('Missing content in Popover'); } return { trigger: trigger, content: content }; } }, { key: 'safeSetState', value: function safeSetState(updater, callback) { if (!this.isUnmounted) { return this.setState(updater, callback); } } }, { key: 'componentDidMount', value: function componentDidMount() { var _ref3 = this.context || {}, popover = _ref3._zentPopover; if (popover && popover.registerDescendant) { popover.registerDescendant(this); } if (this.isVisibilityControlled() && this.props.visible) { this.props.onShow(); } } }, { key: 'componentDidUpdate', value: function componentDidUpdate(prevProps, prevState) { var visible = this.getVisible(); if (visible !== this.getVisible(prevProps, prevState)) { var afterHook = visible ? this.props.onShow : this.props.onClose; afterHook(); } } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { var _ref4 = this.context || {}, popover = _ref4._zentPopover; if (popover && popover.unregisterDescendant) { popover.unregisterDescendant(this); } this.isUnmounted = true; } }, { key: 'render', value: function render() { var _validateChildren = this.validateChildren(), trigger = _validateChildren.trigger, content = _validateChildren.content; var _props = this.props, display = _props.display, prefix = _props.prefix, className = _props.className, wrapperClassName = _props.wrapperClassName, containerSelector = _props.containerSelector, position = _props.position, cushion = _props.cushion, width = _props.width; var visible = this.getVisible(); return _react2['default'].createElement( 'div', { style: (0, _extends3['default'])({ display: display }, (0, _getWidth2['default'])(width)), className: (0, _classnames2['default'])(prefix + '-popover-wrapper', wrapperClassName) }, _react2['default'].cloneElement(trigger, { prefix: prefix, contentVisible: visible, onTriggerRefChange: this.onTriggerRefChange, getTriggerNode: this.getTriggerNode, getContentNode: this.getPopoverNode, open: this.open, close: this.close, isOutsideStacked: this.isOutsideStacked, injectIsOutsideSelf: this.injectIsOutsideSelf }), _react2['default'].cloneElement(content, { prefix: prefix, className: className, id: this.id, getContentNode: this.getPopoverNode, getAnchor: this.getTriggerNode, visible: visible, cushion: cushion, containerSelector: containerSelector, placement: position }) ); } }]); return Popover; }(_react.PureComponent || _react.Component); Popover.propTypes = { prefix: _propTypes2['default'].string, className: _propTypes2['default'].string, // custom classname for trigger wrapper wrapperClassName: _propTypes2['default'].string, // container的display属性 display: _propTypes2['default'].string, // position strategy position: _propTypes2['default'].func.isRequired, // 定位时的偏移量 cushion: _propTypes2['default'].number, // 只有用户触发的打开/关闭才会触发这两个毁掉 onBeforeClose: _propTypes2['default'].func, onBeforeShow: _propTypes2['default'].func, // 不管打开/关闭时如何触发的都会被调用 onClose: _propTypes2['default'].func, onShow: _propTypes2['default'].func, // defaults to body containerSelector: _propTypes2['default'].string, children: _propTypes2['default'].node.isRequired, // 两个必须一起出现 visible: _propTypes2['default'].bool, onVisibleChange: _propTypes2['default'].func }; Popover.defaultProps = { prefix: 'zent', className: '', wrapperClassName: '', display: 'block', onBeforeClose: _noop2['default'], onBeforeShow: _noop2['default'], onClose: _noop2['default'], onShow: _noop2['default'], cushion: 0, containerSelector: 'body' }; Popover.contextTypes = PopoverContextType; Popover.childContextTypes = PopoverContextType; var _initialiseProps = function _initialiseProps() { var _this2 = this; this.registerDescendant = function (popover) { _this2.descendants.push(popover); }; this.unregisterDescendant = function (popover) { var idx = _this2.descendants.indexOf(popover); _this2.descendants.splice(idx, 1); }; this.getVisible = function (props, state) { if (_this2.isVisibilityControlled(props)) { props = props || _this2.props; return props.visible; } state = state || _this2.state; return state.visible; }; this.setVisible = function (visible, props, state) { props = props || _this2.props; state = state || _this2.state; var beforeHook = visible ? props.onBeforeShow : props.onBeforeClose; var onBefore = function onBefore() { // 确保pending的时候不会触发多次beforeHook if (_this2.pendingOnBeforeHook) { return SKIPPED; } _this2.pendingOnBeforeHook = true; return beforeHook.apply(undefined, arguments); }; var escapse = function escapse() { _this2.pendingOnBeforeHook = false; }; if (_this2.isVisibilityControlled(props)) { if (_this2.pendingOnBeforeHook || props.visible === visible) { return; } handleBeforeHook(onBefore, beforeHook.length, function () { props.onVisibleChange(visible); _this2.pendingOnBeforeHook = false; }, escapse); } else { if (_this2.pendingOnBeforeHook || state.visible === visible) { return; } handleBeforeHook(onBefore, beforeHook.length, function () { _this2.safeSetState({ visible: visible }); _this2.pendingOnBeforeHook = false; }, escapse); } }; this.getPopoverNode = function () { return document.querySelector('.' + _this2.id); }; this.onTriggerRefChange = function (triggerInstance) { _this2.triggerNode = _reactDom2['default'].findDOMNode(triggerInstance); }; this.getTriggerNode = function () { return _this2.triggerNode; }; this.open = function () { _this2.setVisible(true); }; this.close = function () { _this2.setVisible(false); }; this.injectIsOutsideSelf = function (impl) { _this2.isOutsideSelf = impl; }; this.isOutsideStacked = function (node) { if (_this2.isOutsideSelf) { // 在自身内部,肯定不在外面 if (!_this2.isOutsideSelf(node)) { return false; } } // 问下面的 Popover 是否在外面 if (_this2.descendants.some(function (popover) { return !popover.isOutsideStacked(node); })) { return false; } return true; }; }; exports['default'] = Popover;