yqcloud-ui
Version:
An enterprise-class UI design language and React-based implementation
305 lines (289 loc) • 9.23 kB
JavaScript
import _defineProperty from 'babel-runtime/helpers/defineProperty';
import _extends from 'babel-runtime/helpers/extends';
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import KeyCode from '../util/KeyCode';
import createChainedFunction from '../util/createChainedFunction';
import classNames from 'classnames';
import scrollIntoView from 'dom-scroll-into-view';
import { getKeyFromChildrenIndex, loopMenuItem } from './util';
import DOMWrap from './DOMWrap';
function allDisabled(arr) {
if (!arr.length) {
return true;
}
return arr.every(function (c) {
return !!c.props.disabled;
});
}
function updateActiveKey(store, menuId, activeKey) {
var state = store.getState();
store.setState({
activeKey: _extends({}, state.activeKey, _defineProperty({}, menuId, activeKey))
});
}
export function getActiveKey(props, originalActiveKey) {
var activeKey = originalActiveKey;
var children = props.children,
eventKey = props.eventKey;
if (activeKey) {
var found = void 0;
loopMenuItem(children, function (c, i) {
if (c && !c.props.disabled && activeKey === getKeyFromChildrenIndex(c, eventKey, i)) {
found = true;
}
});
if (found) {
return activeKey;
}
}
activeKey = null;
if (props.defaultActiveFirst) {
loopMenuItem(children, function (c, i) {
if (!activeKey && c && !c.props.disabled) {
activeKey = getKeyFromChildrenIndex(c, eventKey, i);
}
});
return activeKey;
}
return activeKey;
}
function saveRef(index, subIndex, c) {
if (c) {
if (subIndex !== undefined) {
var array = this.instanceArray[index];
if (!array || !(array instanceof Array)) {
this.instanceArray[index] = [];
}
this.instanceArray[index][subIndex] = c;
} else {
this.instanceArray[index] = c;
}
}
}
var MenuMixin = {
propTypes: {
focusable: PropTypes.bool,
multiple: PropTypes.bool,
style: PropTypes.object,
defaultActiveFirst: PropTypes.bool,
visible: PropTypes.bool,
activeKey: PropTypes.string,
selectedKeys: PropTypes.arrayOf(PropTypes.string),
defaultSelectedKeys: PropTypes.arrayOf(PropTypes.string),
defaultOpenKeys: PropTypes.arrayOf(PropTypes.string),
openKeys: PropTypes.arrayOf(PropTypes.string),
children: PropTypes.any
},
getDefaultProps: function getDefaultProps() {
return {
prefixCls: 'rc-menu',
className: '',
mode: 'vertical',
level: 1,
inlineIndent: 24,
visible: true,
focusable: true,
style: {}
};
},
componentWillReceiveProps: function componentWillReceiveProps(nextProps) {
if (nextProps.children) {
this.instanceArray = this.instanceArray.slice(0, nextProps.children.length);
}
var activeKey = void 0;
var originalActiveKey = this.getStore().getState().activeKey[this.getEventKey()];
activeKey = getActiveKey(nextProps, originalActiveKey);
// fix: this.setState(), parent.render(),
if (activeKey !== originalActiveKey) {
updateActiveKey(this.getStore(), this.getEventKey(), activeKey);
}
},
shouldComponentUpdate: function shouldComponentUpdate(nextProps) {
return this.props.visible || nextProps.visible;
},
componentDidUpdate: function componentDidUpdate() {
if (this.activeItem && this.activeItem.__isMounted !== false) {
scrollIntoView(ReactDOM.findDOMNode(this.activeItem), ReactDOM.findDOMNode(this), {
onlyScrollIfNeeded: true
});
this.activeItem = undefined;
}
},
componentWillMount: function componentWillMount() {
this.instanceArray = [];
},
// all keyboard events callbacks run from here at first
onKeyDown: function onKeyDown(e, callback) {
var keyCode = e.keyCode;
var handled = void 0;
this.getFlatInstanceArray().forEach(function (obj) {
if (obj && obj.props.active && obj.onKeyDown) {
handled = obj.onKeyDown(e);
}
});
if (handled) {
return 1;
}
var activeItem = null;
if (keyCode === KeyCode.UP || keyCode === KeyCode.DOWN) {
activeItem = this.step(keyCode === KeyCode.UP ? -1 : 1);
}
if (activeItem) {
e.preventDefault();
updateActiveKey(this.getStore(), this.getEventKey(), activeItem.props.eventKey);
this.activeItem = activeItem;
if (typeof callback === 'function') {
callback(activeItem);
}
return 1;
} else if (activeItem === undefined) {
e.preventDefault();
updateActiveKey(this.getStore(), this.getEventKey(), null);
return 1;
}
},
onItemHover: function onItemHover(e) {
var key = e.key,
hover = e.hover;
updateActiveKey(this.getStore(), this.getEventKey(), hover ? key : null);
},
getEventKey: function getEventKey() {
// when eventKey not available ,it's menu and return menu id '0-menu-'
return this.props.eventKey || '0-menu-';
},
getStore: function getStore() {
var store = this.store || this.props.store;
return store;
},
getFlatInstanceArray: function getFlatInstanceArray() {
var instanceArray = this.instanceArray;
var hasInnerArray = instanceArray.some(function (a) {
return Array.isArray(a);
});
if (hasInnerArray) {
instanceArray = [];
this.instanceArray.forEach(function (a) {
if (Array.isArray(a)) {
instanceArray.push.apply(instanceArray, a);
} else {
instanceArray.push(a);
}
});
this.instanceArray = instanceArray;
}
return instanceArray;
},
renderCommonMenuItem: function renderCommonMenuItem(child, i, subIndex, extraProps) {
var state = this.getStore().getState();
var props = this.props;
var key = getKeyFromChildrenIndex(child, props.eventKey, i);
var childProps = child.props;
var isActive = key === state.activeKey;
var newChildProps = _extends({
mode: props.mode,
level: props.level,
inlineIndent: props.inlineIndent,
renderMenuItem: this.renderMenuItem,
rootPrefixCls: props.prefixCls,
index: i,
parentMenu: this,
// customized ref function, need to be invoked manually in child's componentDidMount
manualRef: childProps.disabled ? undefined : createChainedFunction(child.ref, saveRef.bind(this, i, subIndex)),
eventKey: key,
active: !childProps.disabled && isActive,
multiple: props.multiple,
onClick: this.onClick,
onItemHover: this.onItemHover,
openTransitionName: this.getOpenTransitionName(),
openAnimation: props.openAnimation,
subMenuOpenDelay: props.subMenuOpenDelay,
subMenuCloseDelay: props.subMenuCloseDelay,
forceSubMenuRender: props.forceSubMenuRender,
onOpenChange: this.onOpenChange,
onDeselect: this.onDeselect,
onSelect: this.onSelect
}, extraProps);
if (props.mode === 'inline') {
newChildProps.triggerSubMenuAction = 'click';
}
return React.cloneElement(child, newChildProps);
},
renderRoot: function renderRoot(props) {
var _this = this;
var className = classNames(props.prefixCls, props.className, props.prefixCls + '-' + props.mode);
var domProps = {
className: className,
role: 'menu',
'aria-activedescendant': ''
};
if (props.id) {
domProps.id = props.id;
}
if (props.focusable) {
domProps.tabIndex = '0';
domProps.onKeyDown = this.onKeyDown;
}
return (
// ESLint is not smart enough to know that the type of `children` was checked.
/* eslint-disable */
React.createElement(
DOMWrap,
_extends({
style: props.style,
tag: 'ul',
hiddenClassName: props.prefixCls + '-hidden',
visible: props.visible
}, domProps, {
virtualizedOptions: props.virtualizedOptions,
virtualized: props.virtualized
}),
React.Children.map(props.children, function (c, i, subIndex) {
return _this.renderMenuItem(c, i, subIndex, props.eventKey || '0-menu-');
})
)
/*eslint-enable */
);
},
step: function step(direction) {
var children = this.getFlatInstanceArray();
var activeKey = this.getStore().getState().activeKey[this.getEventKey()];
var len = children.length;
if (!len) {
return null;
}
if (direction < 0) {
children = children.concat().reverse();
}
// find current activeIndex
var activeIndex = -1;
children.every(function (c, ci) {
if (c && c.props.eventKey === activeKey) {
activeIndex = ci;
return false;
}
return true;
});
if (!this.props.defaultActiveFirst && activeIndex !== -1) {
if (allDisabled(children.slice(activeIndex, len - 1))) {
return undefined;
}
}
var start = (activeIndex + 1) % len;
var i = start;
for (;;) {
var child = children[i];
if (!child || child.props.disabled) {
i = (i + 1 + len) % len;
// complete a loop
if (i === start) {
return null;
}
} else {
return child;
}
}
}
};
export default MenuMixin;