NavMenu.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import { useState, FC } from 'react';
  2. import { makeStyles, Theme, createStyles } from '@material-ui/core/styles';
  3. import List from '@material-ui/core/List';
  4. import ListItem from '@material-ui/core/ListItem';
  5. import ListItemIcon from '@material-ui/core/ListItemIcon';
  6. import ListItemText from '@material-ui/core/ListItemText';
  7. import Collapse from '@material-ui/core/Collapse';
  8. import { NavMenuItem, NavMenuType } from './Types';
  9. import icons from '../icons/Icons';
  10. import { useTranslation } from 'react-i18next';
  11. import Typography from '@material-ui/core/Typography';
  12. const useStyles = makeStyles((theme: Theme) =>
  13. createStyles({
  14. root: {
  15. width: (props: any) => props.width || '100%',
  16. background: theme.palette.common.white,
  17. paddingBottom: theme.spacing(5),
  18. display: 'flex',
  19. flexDirection: 'column',
  20. justifyContent: 'space-between',
  21. },
  22. nested: {
  23. paddingLeft: theme.spacing(4),
  24. },
  25. item: {
  26. marginBottom: theme.spacing(2),
  27. marginLeft: theme.spacing(3),
  28. width: 'initial',
  29. color: theme.palette.milvusGrey.dark,
  30. },
  31. itemIcon: {
  32. minWidth: '20px',
  33. marginRight: theme.spacing(1),
  34. '& .icon': {
  35. fill: 'transparent',
  36. '& path': {
  37. stroke: theme.palette.milvusGrey.dark,
  38. },
  39. },
  40. },
  41. active: {
  42. color: theme.palette.primary.main,
  43. borderRight: `2px solid ${theme.palette.primary.main}`,
  44. '& .icon': {
  45. '& path': {
  46. stroke: theme.palette.primary.main,
  47. },
  48. },
  49. },
  50. logoWrapper: {
  51. width: '100%',
  52. display: 'flex',
  53. justifyContent: 'center',
  54. alignItems: 'center',
  55. marginTop: theme.spacing(3),
  56. marginBottom: theme.spacing(8),
  57. '& .title': {
  58. margin: 0,
  59. fontSize: '16px',
  60. lineHeight: '19px',
  61. letterSpacing: '0.15px',
  62. color: '#323232',
  63. },
  64. },
  65. logo: {
  66. marginRight: theme.spacing(1),
  67. },
  68. })
  69. );
  70. const NavMenu: FC<NavMenuType> = props => {
  71. const { width, data, defaultActive = '', defaultOpen = {} } = props;
  72. const classes = useStyles({ width });
  73. const [open, setOpen] = useState<{ [x: string]: boolean }>(defaultOpen);
  74. const [active, setActive] = useState<string>(defaultActive);
  75. const { t } = useTranslation();
  76. const milvusTrans: { [key in string]: string } = t('milvus');
  77. const ExpandLess = icons.expandLess;
  78. const ExpandMore = icons.expandMore;
  79. const handleClick = (label: string) => {
  80. setOpen(v => ({
  81. ...v,
  82. [label]: !v[label],
  83. }));
  84. };
  85. const NestList = (props: { data: NavMenuItem[]; className?: string }) => {
  86. const { className, data } = props;
  87. return (
  88. <>
  89. {data.map((v: NavMenuItem) => {
  90. const IconComponent = v.icon;
  91. const isActive = active === v.label;
  92. const iconClass =
  93. v.iconActiveClass && v.iconNormalClass
  94. ? isActive
  95. ? v.iconActiveClass
  96. : v.iconNormalClass
  97. : 'icon';
  98. if (v.children) {
  99. return (
  100. <div key={v.label}>
  101. <ListItem button onClick={() => handleClick(v.label)}>
  102. <ListItemIcon>
  103. <IconComponent classes={{ root: iconClass }} />
  104. </ListItemIcon>
  105. <ListItemText primary={v.label} />
  106. {open[v.label] ? <ExpandLess /> : <ExpandMore />}
  107. </ListItem>
  108. <Collapse in={open[v.label]} timeout="auto" unmountOnExit>
  109. <List component="div" disablePadding>
  110. <NestList data={v.children} className={classes.nested} />
  111. </List>
  112. </Collapse>
  113. </div>
  114. );
  115. }
  116. return (
  117. <ListItem
  118. button
  119. key={v.label}
  120. className={`${className || ''} ${classes.item} ${
  121. isActive ? classes.active : ''
  122. }`}
  123. onClick={() => {
  124. setActive(v.label);
  125. v.onClick && v.onClick();
  126. }}
  127. >
  128. <ListItemIcon className={classes.itemIcon}>
  129. <IconComponent classes={{ root: iconClass }} />
  130. </ListItemIcon>
  131. <ListItemText primary={v.label} />
  132. </ListItem>
  133. );
  134. })}
  135. </>
  136. );
  137. };
  138. const Logo = icons.milvus;
  139. return (
  140. <List component="nav" className={classes.root}>
  141. <div>
  142. <div className={classes.logoWrapper}>
  143. <Logo classes={{ root: classes.logo }} />
  144. <Typography variant="h3" className="title">
  145. {milvusTrans.admin}
  146. </Typography>
  147. </div>
  148. <NestList data={data} />
  149. </div>
  150. </List>
  151. );
  152. };
  153. export default NavMenu;