skyline/src/layouts/Base/Menu.jsx
zhangjingwei 84d1e7af72 fix: update long title style for menu
1. update long title style for the sub menu
2. update long title style for the menu which has no children

Change-Id: Id2b319e52ebf849de50af7303117868b4dd4d7d6
2024-04-12 09:47:39 +08:00

350 lines
9.3 KiB
JavaScript

// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React, { Component } from 'react';
import { Menu, Tooltip } from 'antd';
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
import { inject, observer } from 'mobx-react';
import { toJS } from 'mobx';
import { isString, isEqual } from 'lodash';
import classnames from 'classnames';
import { getPath } from 'utils/route-map';
import i18n from 'core/i18n';
import styles from './index.less';
const { SubMenu } = Menu;
const { getLocaleShortName } = i18n;
export class LayoutMenu extends Component {
constructor(props) {
super(props);
this.state = {
collapsed: false,
hover: false,
openKeys: [],
};
const shortName = getLocaleShortName();
this.maxTitleLength = shortName === 'zh' ? 9 : 17;
}
componentDidMount() {
this.init();
}
componentDidUpdate(prevProps) {
const { pathname } = this.props;
const { pathname: prevPathname } = prevProps;
if (prevPathname && pathname !== prevPathname) {
this.updateOpenKeysByRoute();
}
}
get menu() {
return this.props.menu || [];
}
get isAdminPage() {
return this.props.isAdminPage || false;
}
getRouteName(routeName) {
return this.isAdminPage ? `${routeName}Admin` : routeName;
}
getRoutePath(routeName, params = {}, query = {}) {
const realName = this.getRouteName(routeName);
return getPath({ key: realName, params, query });
}
getOpenKeysByRoute() {
const { currentRoutes } = this.props;
const selectedKeys = this.getSelectedKeys(currentRoutes);
const currentOpenKeys = this.getCurrentOpenKeys(selectedKeys);
return currentOpenKeys;
}
get rootStore() {
return this.props.rootStore;
}
get routing() {
return this.props.rootStore.routing;
}
onCollapse = (collapsed) => {
this.setState({ collapsed });
};
changeCollapse = () => {
const { collapsed } = this.state;
this.setState({
collapsed: !collapsed,
hover: false,
});
const { onCollapseChange } = this.props;
onCollapseChange && onCollapseChange(!collapsed);
};
onMouseEnter = (e) => {
const { collapsed } = this.state;
if (collapsed) {
const target = (e && e.target) || null;
const className = target ? target.className || '' : '';
if (isString(className) && !className.includes('trigger')) {
this.setState({
hover: true,
});
}
}
};
onMouseLeave = () => {
const { hover } = this.state;
if (hover) {
this.setState({
hover: false,
});
}
};
onClickMenuItem = ({ key }) => {
const path = getPath({ key });
const { pathname } = this.props;
if (pathname !== path) {
this.routing.push(path);
}
};
// eslint-disable-next-line no-unused-vars
renderMenuItemIcon = ({ item, collapsed, isSubMenu }) => {
return item.icon;
};
renderMenuItem = (item, isSubMenu) => {
const { collapsed, hover } = this.state;
if (collapsed && !hover) {
return (
<Menu.Item key={item.key} className={styles['menu-item-collapsed']}>
{this.renderMenuItemIcon({ item, collapsed, isSubMenu })}
</Menu.Item>
);
}
if (item.level > 1) {
return null;
}
const { showChildren = true } = item;
if (
!showChildren ||
!item.children ||
item.children.length === 0 ||
item.level
) {
return (
<Menu.Item
key={item.key}
className={styles['menu-item']}
onClick={this.onClickMenuItem}
>
<span className={styles['menu-item-title-wrapper']}>
{/* <Menu.Item key={item.key} className={styles['menu-item-no-child']}> */}
{this.renderMenuItemIcon({ item, isSubMenu })}
<span
className={
item.level === 0 || (item.level === 1 && !showChildren)
? styles['menu-item-title']
: styles['sub-menu-item-title']
}
>
{item.name.length >= this.maxTitleLength ? (
<Tooltip title={item.name} placement="right">
{item.name}
</Tooltip>
) : (
item.name
)}
</span>
</span>
</Menu.Item>
);
}
const title = (
<span className={styles['sub-menu-title']}>
{this.renderMenuItemIcon({ item })}
<span className={styles['menu-item-title']}>
{item.name.length >= this.maxTitleLength ? (
<Tooltip title={item.name} placement="right">
{item.name}
</Tooltip>
) : (
item.name
)}
</span>
</span>
);
const subMenuItems = item.children.map((it) =>
this.renderMenuItem(it, true)
);
return (
<SubMenu key={item.key} title={title} className={styles['sub-menu']}>
{subMenuItems}
</SubMenu>
);
};
getFirstLevelKeys = (selectedKeys) => {
const fathers = this.menu.filter((it) => {
const { children = [] } = it;
if (!children.length) {
return selectedKeys.includes(it.key);
}
let hasFather = children.find((c) => selectedKeys.includes(c.key));
if (hasFather) {
return true;
}
children.forEach((c) => {
const { children: cc = [] } = c;
const child = cc.find((ccc) => selectedKeys.includes(ccc.key));
if (child) {
hasFather = true;
}
});
return hasFather;
});
return fathers.map((f) => f.key);
};
getSelectedKeysForMenu = (selectedKeys) => {
const { collapsed, hover } = this.state;
if (!collapsed || hover) {
return selectedKeys;
}
return this.getFirstLevelKeys(selectedKeys);
};
getCurrentOpenKeys = (selectedKeys) => {
return this.getFirstLevelKeys(selectedKeys);
};
renderMenu = (selectedKeys = []) => {
const { collapsed } = this.state;
const { openKeys } = this.rootStore;
const menuItems = this.menu
.map((item) => this.renderMenuItem(item))
.filter((it) => it !== null);
const newSelectedKeys = this.getSelectedKeysForMenu(selectedKeys);
return (
<Menu
theme={GLOBAL_VARIABLES.menuTheme}
mode="inline"
className={collapsed ? styles['menu-collapsed'] : styles.menu}
defaultSelectedKeys={newSelectedKeys}
selectedKeys={newSelectedKeys}
openKeys={openKeys}
onOpenChange={this.onOpenChange}
>
{menuItems}
</Menu>
);
};
onOpenChange = (openKeys) => {
const { openKeys: stateOpenKeys } = this.state;
const { openKeys: defaultOpenKeys } = this.rootStore;
const oldKeys = Array.from(
new Set(stateOpenKeys.concat(toJS(defaultOpenKeys)))
);
const latestOpenKey = openKeys.find((key) => oldKeys.indexOf(key) === -1);
const newKeys = latestOpenKey ? [latestOpenKey] : [];
this.updateOpenKeys(newKeys);
};
updateOpenKeys = (keys) => {
this.rootStore.updateOpenKeys(keys);
this.setState({
openKeys: keys,
});
};
getSelectedKeys = (currentRoutes) => {
if (currentRoutes.length === 0) {
return [];
}
if (currentRoutes.length === 1) {
return [currentRoutes[0].key];
}
if (currentRoutes.length >= 2) {
return [currentRoutes[1].key];
}
return [];
};
updateOpenKeysByRoute() {
const currentOpenKeys = this.getOpenKeysByRoute();
const { openKeys: defaultOpenKeys } = this.rootStore;
if (!isEqual(currentOpenKeys, toJS(defaultOpenKeys))) {
this.init();
}
}
init() {
const currentOpenKeys = this.getOpenKeysByRoute();
this.updateOpenKeys(currentOpenKeys);
}
renderTrigger() {
const { collapsed } = this.state;
// const triggerIcon = <MenuUnfoldOutlined />;
const triggerIcon = collapsed ? (
<MenuUnfoldOutlined className={styles['trigger-icon']} />
) : (
<MenuFoldOutlined className={styles['trigger-icon']} />
);
return (
<div className={styles['trigger-wrapper']}>
<div className={styles.trigger} onClick={this.changeCollapse}>
{triggerIcon}
</div>
</div>
);
}
render() {
const { currentRoutes } = this.props;
const selectedKeys = this.getSelectedKeys(currentRoutes);
const { hover, collapsed } = this.state;
const trigger = this.renderTrigger();
return (
<div
className={classnames(
styles['base-layout-sider'],
collapsed ? styles['base-layout-sider-collapsed'] : '',
hover ? styles['base-layout-sider-hover'] : ''
)}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
>
{this.renderMenu(selectedKeys)}
{trigger}
</div>
);
}
}
export default inject('rootStore')(observer(LayoutMenu));