yqcloud-ui
Version:
An enterprise-class UI design language and React-based implementation
627 lines (512 loc) • 19.5 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SubMenu = undefined;
var _typeof2 = require('babel-runtime/helpers/typeof');
var _typeof3 = _interopRequireDefault(_typeof2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _defineProperty2 = require('babel-runtime/helpers/defineProperty');
var _defineProperty3 = _interopRequireDefault(_defineProperty2);
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _reactDom = require('react-dom');
var _reactDom2 = _interopRequireDefault(_reactDom);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _trigger = require('../trigger');
var _trigger2 = _interopRequireDefault(_trigger);
var _KeyCode = require('../util/KeyCode');
var _KeyCode2 = _interopRequireDefault(_KeyCode);
var _classnames = require('classnames');
var _classnames2 = _interopRequireDefault(_classnames);
var _miniStore = require('mini-store');
var _SubPopupMenu = require('./SubPopupMenu');
var _SubPopupMenu2 = _interopRequireDefault(_SubPopupMenu);
var _placements = require('./placements');
var _placements2 = _interopRequireDefault(_placements);
var _animate = require('../animate');
var _animate2 = _interopRequireDefault(_animate);
var _util = require('./util');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
var guid = 0;
var popupPlacementMap = {
horizontal: 'bottomLeft',
vertical: 'rightTop',
'vertical-left': 'rightTop',
'vertical-right': 'leftTop'
};
var updateDefaultActiveFirst = function updateDefaultActiveFirst(store, eventKey, defaultActiveFirst) {
var menuId = (0, _util.getMenuIdFromSubMenuEventKey)(eventKey);
var state = store.getState();
store.setState({
defaultActiveFirst: (0, _extends3['default'])({}, state.defaultActiveFirst, (0, _defineProperty3['default'])({}, menuId, defaultActiveFirst))
});
};
var SubMenu = exports.SubMenu = function (_React$Component) {
(0, _inherits3['default'])(SubMenu, _React$Component);
function SubMenu(props) {
(0, _classCallCheck3['default'])(this, SubMenu);
var _this = (0, _possibleConstructorReturn3['default'])(this, _React$Component.call(this, props));
_initialiseProps.call(_this);
var store = props.store;
var eventKey = props.eventKey;
var defaultActiveFirst = store.getState().defaultActiveFirst;
_this.isRootMenu = false;
var value = false;
if (defaultActiveFirst) {
value = defaultActiveFirst[eventKey];
}
updateDefaultActiveFirst(store, eventKey, value);
return _this;
}
SubMenu.prototype.componentDidMount = function componentDidMount() {
this.componentDidUpdate();
};
SubMenu.prototype.componentDidUpdate = function componentDidUpdate() {
var _this2 = this;
var _props = this.props,
mode = _props.mode,
parentMenu = _props.parentMenu,
manualRef = _props.manualRef;
// invoke customized ref to expose component to mixin
if (manualRef) {
manualRef(this);
}
if (mode !== 'horizontal' || !parentMenu.isRootMenu || !this.props.isOpen) {
return;
}
this.minWidthTimeout = setTimeout(function () {
return _this2.adjustWidth();
}, 0);
};
SubMenu.prototype.componentWillUnmount = function componentWillUnmount() {
var _props2 = this.props,
onDestroy = _props2.onDestroy,
eventKey = _props2.eventKey;
if (onDestroy) {
onDestroy(eventKey);
}
/* istanbul ignore if */
if (this.minWidthTimeout) {
clearTimeout(this.minWidthTimeout);
}
/* istanbul ignore if */
if (this.mouseenterTimeout) {
clearTimeout(this.mouseenterTimeout);
}
};
SubMenu.prototype.renderChildren = function renderChildren(children) {
var props = this.props;
var baseProps = {
mode: props.mode === 'horizontal' ? 'vertical' : props.mode,
visible: this.props.isOpen,
level: props.level + 1,
inlineIndent: props.inlineIndent,
focusable: false,
onClick: this.onSubMenuClick,
onSelect: this.onSelect,
onDeselect: this.onDeselect,
onDestroy: this.onDestroy,
selectedKeys: props.selectedKeys,
eventKey: props.eventKey + '-menu-',
openKeys: props.openKeys,
openTransitionName: props.openTransitionName,
openAnimation: props.openAnimation,
onOpenChange: this.onOpenChange,
subMenuOpenDelay: props.subMenuOpenDelay,
parentMenu: this,
subMenuCloseDelay: props.subMenuCloseDelay,
forceSubMenuRender: props.forceSubMenuRender,
triggerSubMenuAction: props.triggerSubMenuAction,
builtinPlacements: props.builtinPlacements,
defaultActiveFirst: props.store.getState().defaultActiveFirst[(0, _util.getMenuIdFromSubMenuEventKey)(props.eventKey)],
multiple: props.multiple,
prefixCls: props.rootPrefixCls,
id: this._menuId,
manualRef: this.saveMenuInstance,
itemIcon: props.itemIcon,
expandIcon: props.expandIcon
};
var haveRendered = this.haveRendered;
this.haveRendered = true;
this.haveOpened = this.haveOpened || baseProps.visible || baseProps.forceSubMenuRender;
// never rendered not planning to, don't render
if (!this.haveOpened) {
return _react2['default'].createElement('div', null);
}
// don't show transition on first rendering (no animation for opened menu)
// show appear transition if it's not visible (not sure why)
// show appear transition if it's not inline mode
var transitionAppear = haveRendered || !baseProps.visible || !baseProps.mode === 'inline';
baseProps.className = ' ' + baseProps.prefixCls + '-sub';
var animProps = {};
if (baseProps.openTransitionName) {
animProps.transitionName = baseProps.openTransitionName;
} else if ((0, _typeof3['default'])(baseProps.openAnimation) === 'object') {
animProps.animation = (0, _extends3['default'])({}, baseProps.openAnimation);
if (!transitionAppear) {
delete animProps.animation.appear;
}
}
return _react2['default'].createElement(_animate2['default'], (0, _extends3['default'])({}, animProps, {
showProp: 'visible',
component: '',
transitionAppear: transitionAppear
}), _react2['default'].createElement(_SubPopupMenu2['default'], (0, _extends3['default'])({}, baseProps, { id: this._menuId }), children));
};
SubMenu.prototype.render = function render() {
var _classNames;
var props = (0, _extends3['default'])({}, this.props);
var isOpen = props.isOpen;
var prefixCls = this.getPrefixCls();
var isInlineMode = props.mode === 'inline';
var className = (0, _classnames2['default'])(prefixCls, prefixCls + '-' + props.mode, (_classNames = {}, (0, _defineProperty3['default'])(_classNames, props.className, !!props.className), (0, _defineProperty3['default'])(_classNames, this.getOpenClassName(), isOpen), (0, _defineProperty3['default'])(_classNames, this.getActiveClassName(), props.active || isOpen && !isInlineMode), (0, _defineProperty3['default'])(_classNames, this.getDisabledClassName(), props.disabled), (0, _defineProperty3['default'])(_classNames, this.getSelectedClassName(), this.isChildrenSelected()), _classNames));
if (!this._menuId) {
if (props.eventKey) {
this._menuId = props.eventKey + '$Menu';
} else {
this._menuId = '$__$' + ++guid + '$Menu';
}
}
var mouseEvents = {};
var titleClickEvents = {};
var titleMouseEvents = {};
if (!props.disabled) {
mouseEvents = {
onMouseLeave: this.onMouseLeave,
onMouseEnter: this.onMouseEnter
};
// only works in title, not outer li
titleClickEvents = {
onClick: this.onTitleClick
};
titleMouseEvents = {
onMouseEnter: this.onTitleMouseEnter,
onMouseLeave: this.onTitleMouseLeave
};
}
var style = {};
if (isInlineMode) {
style.paddingLeft = props.inlineIndent * props.level;
}
var ariaOwns = {};
// only set aria-owns when menu is open
// otherwise it would be an invalid aria-owns value
// since corresponding node cannot be found
if (this.props.isOpen) {
ariaOwns = {
'aria-owns': this._menuId
};
}
// expand custom icon should NOT be displayed in menu with horizontal mode.
var icon = null;
if (props.mode !== 'horizontal') {
icon = this.props.expandIcon; // ReactNode
if (typeof this.props.expandIcon === 'function') {
icon = _react2['default'].createElement(this.props.expandIcon, (0, _extends3['default'])({}, this.props));
}
}
var title = _react2['default'].createElement('div', (0, _extends3['default'])({
ref: this.saveSubMenuTitle,
style: style,
className: prefixCls + '-title'
}, titleMouseEvents, titleClickEvents, {
'aria-expanded': isOpen
}, ariaOwns, {
'aria-haspopup': 'true',
title: typeof props.title === 'string' ? props.title : undefined
}), props.title, icon || _react2['default'].createElement('i', { className: prefixCls + '-arrow' }));
var children = this.renderChildren(props.children);
var getPopupContainer = props.parentMenu.isRootMenu ? props.parentMenu.props.getPopupContainer : function (triggerNode) {
return triggerNode.parentNode;
};
var popupPlacement = popupPlacementMap[props.mode];
var popupAlign = props.popupOffset ? { offset: props.popupOffset } : {};
var popupClassName = props.mode === 'inline' ? '' : props.popupClassName;
var disabled = props.disabled,
triggerSubMenuAction = props.triggerSubMenuAction,
subMenuOpenDelay = props.subMenuOpenDelay,
forceSubMenuRender = props.forceSubMenuRender,
subMenuCloseDelay = props.subMenuCloseDelay,
builtinPlacements = props.builtinPlacements;
_util.menuAllProps.forEach(function (key) {
return delete props[key];
});
// Set onClick to null, to ignore propagated onClick event
delete props.onClick;
return _react2['default'].createElement('li', (0, _extends3['default'])({}, props, mouseEvents, {
className: className,
role: 'menuitem'
}), isInlineMode && title, isInlineMode && children, !isInlineMode && _react2['default'].createElement(_trigger2['default'], {
prefixCls: prefixCls,
popupClassName: prefixCls + '-popup ' + popupClassName,
getPopupContainer: getPopupContainer,
builtinPlacements: (0, _extends3['default'])({}, _placements2['default'], builtinPlacements),
popupPlacement: popupPlacement,
popupVisible: isOpen,
popupAlign: popupAlign,
popup: children,
action: disabled ? [] : [triggerSubMenuAction],
mouseEnterDelay: subMenuOpenDelay,
mouseLeaveDelay: subMenuCloseDelay,
onPopupVisibleChange: this.onPopupVisibleChange,
forceRender: forceSubMenuRender
}, title));
};
return SubMenu;
}(_react2['default'].Component);
SubMenu.propTypes = {
parentMenu: _propTypes2['default'].object,
title: _propTypes2['default'].node,
children: _propTypes2['default'].any,
selectedKeys: _propTypes2['default'].array,
openKeys: _propTypes2['default'].array,
onClick: _propTypes2['default'].func,
onOpenChange: _propTypes2['default'].func,
rootPrefixCls: _propTypes2['default'].string,
eventKey: _propTypes2['default'].string,
multiple: _propTypes2['default'].bool,
active: _propTypes2['default'].bool, // TODO: remove
onItemHover: _propTypes2['default'].func,
onSelect: _propTypes2['default'].func,
triggerSubMenuAction: _propTypes2['default'].string,
onDeselect: _propTypes2['default'].func,
onDestroy: _propTypes2['default'].func,
onMouseEnter: _propTypes2['default'].func,
onMouseLeave: _propTypes2['default'].func,
onTitleMouseEnter: _propTypes2['default'].func,
onTitleMouseLeave: _propTypes2['default'].func,
onTitleClick: _propTypes2['default'].func,
popupOffset: _propTypes2['default'].array,
isOpen: _propTypes2['default'].bool,
store: _propTypes2['default'].object,
mode: _propTypes2['default'].oneOf(['horizontal', 'vertical', 'vertical-left', 'vertical-right', 'inline']),
manualRef: _propTypes2['default'].func,
itemIcon: _propTypes2['default'].oneOfType([_propTypes2['default'].func, _propTypes2['default'].node]),
expandIcon: _propTypes2['default'].oneOfType([_propTypes2['default'].func, _propTypes2['default'].node])
};
SubMenu.defaultProps = {
onMouseEnter: _util.noop,
onMouseLeave: _util.noop,
onTitleMouseEnter: _util.noop,
onTitleMouseLeave: _util.noop,
onTitleClick: _util.noop,
manualRef: _util.noop,
mode: 'vertical',
title: ''
};
var _initialiseProps = function _initialiseProps() {
var _this3 = this;
this.onDestroy = function (key) {
_this3.props.onDestroy(key);
};
this.onKeyDown = function (e) {
var keyCode = e.keyCode;
var menu = _this3.menuInstance;
var _props3 = _this3.props,
isOpen = _props3.isOpen,
store = _props3.store;
if (keyCode === _KeyCode2['default'].ENTER) {
_this3.onTitleClick(e);
updateDefaultActiveFirst(store, _this3.props.eventKey, true);
return true;
}
if (keyCode === _KeyCode2['default'].RIGHT) {
if (isOpen) {
menu.onKeyDown(e);
} else {
_this3.triggerOpenChange(true);
// need to update current menu's defaultActiveFirst value
updateDefaultActiveFirst(store, _this3.props.eventKey, true);
}
return true;
}
if (keyCode === _KeyCode2['default'].LEFT) {
var handled = void 0;
if (isOpen) {
handled = menu.onKeyDown(e);
} else {
return undefined;
}
if (!handled) {
_this3.triggerOpenChange(false);
handled = true;
}
return handled;
}
if (isOpen && (keyCode === _KeyCode2['default'].UP || keyCode === _KeyCode2['default'].DOWN)) {
return menu.onKeyDown(e);
}
};
this.onOpenChange = function (e) {
_this3.props.onOpenChange(e);
};
this.onPopupVisibleChange = function (visible) {
_this3.triggerOpenChange(visible, visible ? 'mouseenter' : 'mouseleave');
};
this.onMouseEnter = function (e) {
var _props4 = _this3.props,
key = _props4.eventKey,
onMouseEnter = _props4.onMouseEnter,
store = _props4.store;
updateDefaultActiveFirst(store, _this3.props.eventKey, false);
onMouseEnter({
key: key,
domEvent: e
});
};
this.onMouseLeave = function (e) {
var _props5 = _this3.props,
parentMenu = _props5.parentMenu,
eventKey = _props5.eventKey,
onMouseLeave = _props5.onMouseLeave;
parentMenu.subMenuInstance = _this3;
onMouseLeave({
key: eventKey,
domEvent: e
});
};
this.onTitleMouseEnter = function (domEvent) {
var _props6 = _this3.props,
key = _props6.eventKey,
onItemHover = _props6.onItemHover,
onTitleMouseEnter = _props6.onTitleMouseEnter;
onItemHover({
key: key,
hover: true
});
onTitleMouseEnter({
key: key,
domEvent: domEvent
});
};
this.onTitleMouseLeave = function (e) {
var _props7 = _this3.props,
parentMenu = _props7.parentMenu,
eventKey = _props7.eventKey,
onItemHover = _props7.onItemHover,
onTitleMouseLeave = _props7.onTitleMouseLeave;
parentMenu.subMenuInstance = _this3;
onItemHover({
key: eventKey,
hover: false
});
onTitleMouseLeave({
key: eventKey,
domEvent: e
});
};
this.onTitleClick = function (e) {
var props = _this3.props;
props.onTitleClick({
key: props.eventKey,
domEvent: e
});
if (props.triggerSubMenuAction === 'hover') {
return;
}
_this3.triggerOpenChange(!props.isOpen, 'click');
updateDefaultActiveFirst(props.store, _this3.props.eventKey, false);
};
this.onSubMenuClick = function (info) {
// in the case of overflowed submenu
// onClick is not copied over
if (typeof _this3.props.onClick === 'function') {
_this3.props.onClick(_this3.addKeyPath(info));
}
};
this.onSelect = function (info) {
_this3.props.onSelect(info);
};
this.onDeselect = function (info) {
_this3.props.onDeselect(info);
};
this.getPrefixCls = function () {
return _this3.props.rootPrefixCls + '-submenu';
};
this.getActiveClassName = function () {
return _this3.getPrefixCls() + '-active';
};
this.getDisabledClassName = function () {
return _this3.getPrefixCls() + '-disabled';
};
this.getSelectedClassName = function () {
return _this3.getPrefixCls() + '-selected';
};
this.getOpenClassName = function () {
return _this3.props.rootPrefixCls + '-submenu-open';
};
this.saveMenuInstance = function (c) {
// children menu instance
_this3.menuInstance = c;
};
this.addKeyPath = function (info) {
return (0, _extends3['default'])({}, info, {
keyPath: (info.keyPath || []).concat(_this3.props.eventKey)
});
};
this.triggerOpenChange = function (open, type) {
var key = _this3.props.eventKey;
var openChange = function openChange() {
_this3.onOpenChange({
key: key,
item: _this3,
trigger: type,
open: open
});
};
if (type === 'mouseenter') {
// make sure mouseenter happen after other menu item's mouseleave
_this3.mouseenterTimeout = setTimeout(function () {
openChange();
}, 0);
} else {
openChange();
}
};
this.isChildrenSelected = function () {
var ret = { find: false };
(0, _util.loopMenuItemRecursively)(_this3.props.children, _this3.props.selectedKeys, ret);
return ret.find;
};
this.isOpen = function () {
return _this3.props.openKeys.indexOf(_this3.props.eventKey) !== -1;
};
this.adjustWidth = function () {
/* istanbul ignore if */
if (!_this3.subMenuTitle || !_this3.menuInstance) {
return;
}
var popupMenu = _reactDom2['default'].findDOMNode(_this3.menuInstance);
if (popupMenu.offsetWidth >= _this3.subMenuTitle.offsetWidth) {
return;
}
/* istanbul ignore next */
popupMenu.style.minWidth = _this3.subMenuTitle.offsetWidth + 'px';
};
this.saveSubMenuTitle = function (subMenuTitle) {
_this3.subMenuTitle = subMenuTitle;
};
};
var connected = (0, _miniStore.connect)(function (_ref, _ref2) {
var openKeys = _ref.openKeys,
activeKey = _ref.activeKey,
selectedKeys = _ref.selectedKeys;
var eventKey = _ref2.eventKey,
subMenuKey = _ref2.subMenuKey;
return {
isOpen: openKeys.indexOf(eventKey) > -1,
active: activeKey[subMenuKey] === eventKey,
selectedKeys: selectedKeys
};
})(SubMenu);
connected.isSubMenu = true;
exports['default'] = connected;