NavMenu.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import { useState, FC, useEffect } from 'react';
  2. import clsx from 'clsx';
  3. import { Theme } from '@mui/material/styles';
  4. import List from '@mui/material/List';
  5. import ListItem from '@mui/material/ListItem';
  6. import Tooltip from '@mui/material/Tooltip';
  7. import ListItemIcon from '@mui/material/ListItemIcon';
  8. import Typography from '@mui/material/Typography';
  9. import { makeStyles } from '@mui/styles';
  10. import icons from '@/components/icons/Icons';
  11. import type { NavMenuItem, NavMenuType } from './Types';
  12. const useStyles = makeStyles((theme: Theme) => ({
  13. root: {
  14. boxShadow: '0px 6px 30px rgba(0, 0, 0, 0.1)',
  15. borderRight: `1px solid ${theme.palette.divider}`,
  16. width: 48,
  17. paddingTop: 0,
  18. color: theme.palette.text.primary,
  19. backgroundColor: theme.palette.background.default,
  20. },
  21. item: {
  22. width: 'initial',
  23. borderRadius: 8,
  24. margin: theme.spacing(0.5),
  25. marginBottom: theme.spacing(1.5),
  26. cursor: 'pointer',
  27. '&:hover': {
  28. backgroundColor: theme.palette.primary.main,
  29. color: '#fff',
  30. '& .icon': { color: '#fff' },
  31. },
  32. '&.attu .icon': {
  33. color: theme.palette.primary.main,
  34. '&:hover': { color: '#fff' },
  35. '&.active:hover': { color: '#fff' },
  36. },
  37. '& .itemIcon': {
  38. marginLeft: -8,
  39. minWidth: 24,
  40. },
  41. '&.active': {
  42. borderRadius: 8,
  43. backgroundColor: theme.palette.primary.main,
  44. color: '#fff',
  45. '& .icon path': { fill: '#fff' },
  46. },
  47. },
  48. version: {
  49. position: 'absolute',
  50. fontSize: 10,
  51. bottom: 8,
  52. left: 8,
  53. },
  54. }));
  55. const NavMenu: FC<NavMenuType> = props => {
  56. const { width, data, defaultActive = '', versionInfo } = props;
  57. const classes = useStyles({ width });
  58. const [active, setActive] = useState<string>(defaultActive);
  59. useEffect(() => {
  60. if (defaultActive) {
  61. setActive(defaultActive);
  62. }
  63. }, [defaultActive]);
  64. const NestList = (props: { data: NavMenuItem[]; className?: string }) => {
  65. const { className = '', data } = props;
  66. return (
  67. <>
  68. {data.map((v: NavMenuItem) => {
  69. const IconComponent = v.icon;
  70. const isActive = active === v.label;
  71. const iconClass =
  72. v.iconActiveClass && v.iconNormalClass
  73. ? isActive
  74. ? v.iconActiveClass
  75. : v.iconNormalClass
  76. : 'icon';
  77. return (
  78. <ListItem
  79. key={v.label}
  80. title={v.label}
  81. className={clsx(classes.item, {
  82. [className]: className,
  83. ['active']: isActive,
  84. ['attu']: v.icon === icons.attu,
  85. })}
  86. onClick={() => {
  87. setActive(v.label);
  88. v.onClick && v.onClick();
  89. }}
  90. >
  91. <Tooltip title={v.label} placement="right">
  92. <ListItemIcon className="itemIcon">
  93. <IconComponent classes={{ root: iconClass }} />
  94. </ListItemIcon>
  95. </Tooltip>
  96. </ListItem>
  97. );
  98. })}
  99. </>
  100. );
  101. };
  102. return (
  103. <List component="nav" className={clsx(classes.root)}>
  104. <div>
  105. <NestList data={data} />
  106. <Typography
  107. classes={{
  108. root: classes.version,
  109. }}
  110. >
  111. v {versionInfo.attu}
  112. </Typography>
  113. </div>
  114. </List>
  115. );
  116. };
  117. export default NavMenu;