zarm-web
Version:
基于 React 的桌面端UI库
301 lines (264 loc) • 7.19 kB
JavaScript
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import React, { Component, Children, cloneElement } from 'react';
import classnames from 'classnames';
import dom from '../utils/dom';
import events from '../utils/events';
import MenuContext from './menu-context';
export class SubMenu extends Component {
constructor(props) {
super(props);
this.subTitle = void 0;
this.sub = void 0;
this.timeout = void 0;
this.toggleSubMenuOpen = e => {
e.stopPropagation();
const {
subMenuKey
} = this.props;
this.props.toggleOpenKeys(subMenuKey);
};
this.onSubAnimationEnd = () => {
const {
subMenuKey,
openKeys
} = this.props;
const isOpen = openKeys.indexOf(subMenuKey) > -1;
this.setState({
collapsedSubVisible: isOpen
});
};
this.onClickOutSide = e => {
const {
target
} = e;
const {
subMenuKey,
openKeys
} = this.props;
if (this.subTitle.contains(target)) {
return;
}
if (!this.sub.contains(target) && openKeys.indexOf(subMenuKey) > -1) {
this.props.toggleOpenKeys(subMenuKey);
}
};
this.state = {
collapsedSubVisible: false,
collapsedSubAnimation: ''
};
}
componentDidMount() {
const {
openKeys,
inlineCollapsed
} = this.props;
if (openKeys.length > 0) {
if (!inlineCollapsed) {
this.setSubHeight({
openKeys: []
});
}
}
if (inlineCollapsed) {
events.on(document, 'click', this.onClickOutSide);
}
}
componentWillReceiveProps(nextProps) {
const {
inlineCollapsed
} = nextProps;
if (!inlineCollapsed) {
// eslint-disable-next-line react/destructuring-assignment
if (this.props.inlineCollapsed) {
events.off(document, 'click', this.onClickOutSide);
}
return;
} // eslint-disable-next-line react/destructuring-assignment
if (!this.props.inlineCollapsed) {
events.on(document, 'click', this.onClickOutSide);
}
const {
subMenuKey,
openKeys
} = this.props;
const isOpenNow = openKeys.indexOf(subMenuKey) > -1;
const isOpenNext = nextProps.openKeys.indexOf(subMenuKey) > -1;
if (!isOpenNow && isOpenNext) {
// 展开菜单
this.slideDown();
} else if (isOpenNow && !isOpenNext) {
// 收起菜单
this.slideUp();
}
}
componentDidUpdate(prevProps) {
const {
inlineCollapsed
} = this.props;
if (!inlineCollapsed) {
this.setSubHeight(prevProps);
}
}
componentWillUnmount() {
const {
inlineCollapsed
} = this.props;
if (inlineCollapsed) {
events.off(document, 'click', this.onClickOutSide);
}
}
getSubHeight() {
if (!this.sub) {
return;
}
const childs = [...this.sub.children];
let marginBottom = 0;
if (childs[0]) {
marginBottom = parseFloat(dom.getStyleComputedProperty(childs[0], 'marginBottom'));
}
const height = childs.reduce((res, next) => {
res += next.offsetHeight + marginBottom;
return res;
}, marginBottom / 2);
return height;
}
setSubHeight(prevProps) {
if (!this.sub) {
return;
}
const {
openKeys: lastOpenKeys
} = prevProps;
const {
openKeys,
subMenuKey
} = this.props;
const keyIndex = openKeys.indexOf(subMenuKey);
const keysLength = openKeys.length;
if (keyIndex > -1) {
if (keysLength > 1 && keyIndex < keysLength - 1 || keysLength < lastOpenKeys.length) {
// 如果不是最后一级子菜单,或者嵌套的子菜单被收起,当前子菜单高度自适应
this.sub.style.height = 'auto';
} else {
// 否则,设置具体的高度产生过渡动画
const height = this.getSubHeight();
this.sub.style.height = `${height}px`;
}
} else {
const height = this.getSubHeight();
this.sub.style.height = `${height}px`;
setTimeout(() => {
this.sub.style.height = 0;
}, 0);
}
}
slideUp() {
this.setState({
collapsedSubVisible: false,
collapsedSubAnimation: 'up'
});
}
slideDown() {
this.setState({
collapsedSubVisible: true,
collapsedSubAnimation: 'down'
});
}
renderChildren() {
const {
children,
level,
inlineIndent,
mode,
prefixCls,
subMenuKey
} = this.props;
const childProps = {
mode,
level: level + 1,
inlineIndent,
prefixCls
};
return Children.map(children, (child, index) => {
const c = child;
const {
key
} = child;
childProps.itemKey = key || `${subMenuKey}-${level}-${index}`;
childProps.subMenuKey = key || `${subMenuKey}-${level}-${index}`;
return cloneElement(c, childProps);
});
}
render() {
const {
title,
level,
mode,
style,
inlineIndent,
prefixCls,
openKeys,
subMenuKey,
inlineCollapsed
} = this.props;
const {
collapsedSubVisible,
collapsedSubAnimation
} = this.state;
const subMenuStyle = {};
if (mode === 'inline' && !inlineCollapsed) {
subMenuStyle.paddingLeft = level * inlineIndent;
}
const isOpen = openKeys.indexOf(subMenuKey) > -1;
const cls = classnames(`${prefixCls}-submenu`, {
open: isOpen,
[`${prefixCls}-level-${level}`]: level
});
let subStyle = {
display: 'block'
};
let subCls = `${prefixCls}-submenu-sub`;
if (inlineCollapsed) {
subStyle = {
display: collapsedSubVisible ? 'block' : 'none'
};
subCls = classnames(`${prefixCls}`, `${prefixCls}-submenu-sub`, {
[`slide-${collapsedSubAnimation}`]: !!collapsedSubAnimation
});
}
return React.createElement("li", {
className: cls,
style: style
}, React.createElement("div", {
ref: subTitle => {
this.subTitle = subTitle;
},
onClick: this.toggleSubMenuOpen,
style: subMenuStyle,
className: `${prefixCls}-submenu-title`
}, title, React.createElement("i", {
className: `${prefixCls}-submenu-arrow`
})), React.createElement("ul", {
ref: sub => {
this.sub = sub;
},
className: subCls,
style: subStyle,
onAnimationEnd: this.onSubAnimationEnd
}, this.renderChildren()));
}
}
SubMenu.defaultProps = {
prefixCls: 'za-menu',
title: '',
level: 1,
style: {},
openKeys: []
};
export default function SubMenuConsumer(props) {
return React.createElement(MenuContext.Consumer, null, menuKeys => React.createElement(SubMenu, _extends({}, props, {
inlineCollapsed: menuKeys.inlineCollapsed,
openKeys: menuKeys.openKeys,
toggleOpenKeys: menuKeys.toggleOpenKeys
})));
}