Menu菜单是提供给网站的路由跳转主菜单(通常),组件文档如图所示: 组件源码如下:
import React, { FC, useState, useEffect, memo, useCallback, useMemo } from 'react';
import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons';
import style from './index.module.less';
interface MenuProps {
items: Array<RenderOptions>;
width?: string | number;
dark?: Boolean;
ableToggle?: Boolean;
defaultOpen?: Boolean;
handleRouteChange?: Function;
}
interface MenuHeightProps {
key: string;
height: string;
childNum: number | string;
level: number | number;
children?: Array<Object>;
}
interface RenderOptions {
label: string;
key: string | number;
level?: string | number;
icon?: JSX.Element | null;
children?: Array<any> | null | undefined;
}
const Menu: FC<MenuProps> = (props) => {
const [nowActiveMainKey, setNowActiveMainKey] = useState('');
const [nowActiveKey, setNowActiveKey] = useState('');
const [parentMenuHeightList, setParentMenuHeightList] = useState<any>({});
const { items, width, dark, ableToggle, defaultOpen, handleRouteChange } = props;
useEffect(() => {
const initList = initParentMenuHeight(items, {}, '');
if (defaultOpen) {
for (var key in initList) {
initList[key].height = initList[key].childNum + 1;
if (initList[key].children.length > 0) {
initList[key].children.map(
(item: any) => (item.height = (item.childNum + 1) * 50 + 'px'),
);
initList[key].height += initList[key].children.reduce(
(pre: MenuHeightProps, next: MenuHeightProps) => {
return (pre.childNum as number) + (next.childNum as number);
},
);
}
initList[key].height = initList[key].height * 50 + 'px';
}
}
setParentMenuHeightList(initList);
}, []);
useEffect(() => {
handleRouteChange && handleRouteChange(nowActiveKey);
}, [nowActiveKey]);
const initParentMenuHeight = (
item: Array<RenderOptions>,
obj: any,
fatherKey: string | number,
) => {
item.forEach((m) => {
if (m.children) {
if (m.level == 1) {
obj[m.key] = {
key: m.key,
height: '50px',
childNum: m.children.length,
level: m.level,
children: [],
};
} else {
obj[fatherKey]?.children.push({
key: m.key,
height: '50px',
childNum: m.children.length,
level: m.level,
});
}
initParentMenuHeight(m.children, obj, m.level && m.level == 1 ? m.key : '');
}
});
return obj;
};
const toggleFirstMenu = (fMenu: RenderOptions, e: any) => {
e.stopPropagation();
const selectKey = fMenu.key;
setParentMenuHeightList((old: any) => {
old[selectKey].height =
old[selectKey].height == '50px' ? (old[selectKey].childNum + 1) * 50 + 'px' : '50px';
if (ableToggle) {
if (old[selectKey].height !== '50px') {
for (var key in old) {
if (key !== selectKey) {
old[key].height = '50px';
if (old[key].children) {
old[key].children.map((item: MenuHeightProps) => (item.height = '50px'));
}
}
}
}
} else {
if (old[selectKey].children) {
old[selectKey].children.forEach((c: MenuHeightProps) => {
c.height = '50px';
});
}
}
return JSON.parse(JSON.stringify(old));
});
};
const toggleChildMenu = (cMenu: RenderOptions, e: any, fKey: string) => {
if ((cMenu.level == 2 && !cMenu.children) || cMenu.level == 3) {
setNowActiveMainKey(fKey);
setNowActiveKey(cMenu.key as string);
}
if (cMenu.level == 2) {
setParentMenuHeightList((old: any) => {
for (var key in old) {
if (
old[key].children &&
old[key].children.findIndex((item: MenuHeightProps) => item.key == cMenu.key) !== -1
) {
const childIndex = old[key].children.findIndex(
(item: MenuHeightProps) => item.key == cMenu.key,
);
old[key].children[childIndex].height =
old[key].children[childIndex].height == '50px'
? (old[key].children[childIndex].childNum + 1) * 50 + 'px'
: '50px';
let parentHeight = (old[key].childNum - old[key].children.length) * 50 + 50;
parentHeight += old[key].children.reduce(
(pre: MenuHeightProps, next: MenuHeightProps) => {
return Number(pre.height.split('px')[0]) + Number(next.height.split('px')[0]);
},
);
old[key].height = parentHeight;
}
}
return JSON.parse(JSON.stringify(old));
});
}
if (cMenu.level == 3) {
for (var key in parentMenuHeightList) {
if (
parentMenuHeightList[key].children &&
parentMenuHeightList[key].children.findIndex(
(item: MenuHeightProps) => item.key == fKey,
) !== -1
) {
setNowActiveMainKey(parentMenuHeightList[key].key);
}
}
}
};
const firstMenuHeight = useCallback(
(key) => {
if (parentMenuHeightList[key]) {
return {
height: parentMenuHeightList[key]?.height,
};
}
return {
height: '50px',
};
},
[parentMenuHeightList],
);
const childMenuHeight = useCallback(
(key) => {
for (var i in parentMenuHeightList) {
const childIndex = parentMenuHeightList[i].children?.findIndex(
(item: RenderOptions) => item.key == key,
);
if (childIndex !== -1) {
return {
height: parentMenuHeightList[i].children[childIndex].height,
};
}
}
return {
height: '50px',
};
},
[parentMenuHeightList],
);
const customWidth = useMemo(() => {
if (width) {
if (typeof width == 'string') {
return {
width: (width as string).includes('%') ? width : width + 'px',
};
} else if (typeof width == 'number') {
return {
width: width + 'px',
};
}
}
return {
width: '220px',
};
}, [width]);
const renderChildOptions = (childM: RenderOptions): JSX.Element | any => {
if (childM.children) {
return childM.children.map((m) => {
return (
<div key={m.key}>
<div
className={nowActiveKey == m.key ? style.activeMenuOptions : style.childMenuOptions}
style={{ ...childMenuHeight(m.key) }}
>
<div
className={
m.children &&
m.children.findIndex((i: RenderOptions) => i.key == nowActiveKey) !== -1
? style.activeFatherTitle
: style.fatherTitle
}
onClick={(e) => toggleChildMenu(m, e, childM.key as string)}
>
<span>{m.label}</span>
{m.children &&
(childMenuHeight(m.key).height == '50px' ? (
<CaretDownOutlined />
) : (
<CaretUpOutlined />
))}
</div>
<div className={style.childMenuOptions} key={m.key}>
{m.children && renderChildOptions(m)}
</div>
</div>
</div>
);
});
}
};
return (
<div className={dark ? style.darkMenu : style.menu} style={customWidth}>
{items.map((m) => {
return (
<div key={m.key}>
<div className={style.menuOptions} style={firstMenuHeight(m.key)}>
<div
className={nowActiveMainKey == m.key ? style.activeFatherTitle : style.fatherTitle}
onClick={(e) => toggleFirstMenu(m, e)}
>
<div className={style.left}>
<i>{m.icon}</i>
<span>{m.label}</span>
</div>
{firstMenuHeight(m.key).height == '50px' ? (
<CaretDownOutlined />
) : (
<CaretUpOutlined />
)}
</div>
<>{m.children && renderChildOptions(m)}</>
</div>
</div>
);
})}
</div>
);
};
export default memo(Menu);
组件库线上地址:http://124.222.161.174:8080/#/common/menu 感谢阅览,终于要迎来10+组件了~自己的react UI库也是逐步完善起来了
|