import React, { memo, useEffect, useMemo, useState } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { Menu, type MenuValue } from '@miragenflow/ui/menu';
import { MiragenFlowButton } from '@miragenflow/ui';
import router, { IRouter } from 'router';
import { useAppDispatch, useAppSelector } from 'modules/store';
import { selectGlobal, toggleMenu } from 'modules/global';
import MenuLogo from './MenuLogo';
import Style from './Menu.module.less';
const { SubMenu, MenuItem, HeadMenu } = Menu;
interface IMenuProps { showLogo?: boolean; showOperation?: boolean; }
const routePath = (item: IRouter, parentPath = '') => item.absolute ? item.path : `${parentPath}${item.path}` || '/';
const isVisible = (item: IRouter, scopes: string[], roles: string[]) => {
if (!item.meta || item.meta.hidden) return false;
if (item.meta.requiredScope && !roles.includes('super_admin') && !scopes.includes(item.meta.requiredScope)) return false;
return true;
};
const visibleChildren = (item: IRouter, scopes: string[], roles: string[]) => (item.children || []).filter((child) => isVisible(child, scopes, roles));
const hasVisibleChild = (item: IRouter, scopes: string[], roles: string[]) => visibleChildren(item, scopes, roles).length > 0;
const renderMenuItems = (menu: IRouter[], scopes: string[], roles: string[], parentPath = ''): React.ReactNode[] => menu.map((item) => {
const { children, meta } = item;
if (!meta || !isVisible(item, scopes, roles)) return null;
const currentPath = routePath(item, parentPath);
if (children?.length) {
const visible = visibleChildren(item, scopes, roles);
if (!visible.length) return null;
if (visible.length === 1) {
const child = visible[0];
const childPath = routePath(child, currentPath);
const ChildIcon = child.meta?.Icon || meta.Icon;
return : undefined}>{child.meta?.title || meta.title};
}
return : undefined}>
{renderMenuItems(visible, scopes, roles, currentPath)}
;
}
return : undefined}>{meta.title};
});
const expandedFor = (menu: IRouter[], pathname: string, scopes: string[], roles: string[], parent = ''): string[] => menu.flatMap((item) => {
if (!item.children?.length || !hasVisibleChild(item, scopes, roles)) return [];
const visible = visibleChildren(item, scopes, roles);
if (visible.length === 1) return [];
const current = routePath(item, parent);
const childPaths = visible.map((child) => routePath(child, current));
return childPaths.includes(pathname) ? [current] : expandedFor(visible, pathname, scopes, roles, current);
});
export const HeaderMenu = memo(() => {
const globalState = useAppSelector(selectGlobal);
const location = useLocation();
const navigate = useNavigate();
const user = useAppSelector((state) => state.user.userInfo as { scopes?: string[]; roles?: string[] });
const [active, setActive] = useState(location.pathname);
useEffect(() => setActive(location.pathname), [location.pathname]);
return { setActive(value); navigate(String(value)); }}>
{renderMenuItems(router, user.scopes || [], user.roles || [])}
;
});
export default memo((props: IMenuProps) => {
const location = useLocation();
const navigate = useNavigate();
const dispatch = useAppDispatch();
const globalState = useAppSelector(selectGlobal);
const user = useAppSelector((state) => state.user.userInfo as { scopes?: string[]; roles?: string[] });
const scopes = user.scopes || [];
const roles = user.roles || [];
const requiredExpanded = useMemo(() => expandedFor(router, location.pathname, scopes, roles), [location.pathname, scopes.join(','), roles.join(',')]);
const [expanded, setExpanded] = useState(requiredExpanded);
useEffect(() => {
setExpanded((current) => Array.from(new Set([...current, ...requiredExpanded])));
}, [requiredExpanded.join('|')]);
const onNavigate = (value: MenuValue) => {
navigate(String(value));
if (window.innerWidth < 1000) dispatch(toggleMenu(true));
};
if (globalState.collapsed) return null;
return <>
dispatch(toggleMenu(true))} />
>;
});