NavMenu.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import { useState, FC, useEffect } from 'react';
  2. import clsx from 'clsx';
  3. import { makeStyles, Theme, createStyles } from '@material-ui/core/styles';
  4. import Fade from '@material-ui/core/Fade';
  5. import Button from '@material-ui/core/Button';
  6. import List from '@material-ui/core/List';
  7. import ListItem from '@material-ui/core/ListItem';
  8. import ListItemIcon from '@material-ui/core/ListItemIcon';
  9. import ListItemText from '@material-ui/core/ListItemText';
  10. import { NavMenuItem, NavMenuType } from './Types';
  11. import icons from '../icons/Icons';
  12. import { useTranslation } from 'react-i18next';
  13. import Typography from '@material-ui/core/Typography';
  14. import ChevronRightIcon from '@material-ui/icons/ChevronRight';
  15. const timeout = 150;
  16. const duration = `${timeout}ms`;
  17. const useStyles = makeStyles((theme: Theme) =>
  18. createStyles({
  19. root: {
  20. background: theme.palette.common.white,
  21. paddingTop: 0,
  22. paddingBottom: theme.spacing(5),
  23. display: 'flex',
  24. flexDirection: 'column',
  25. justifyContent: 'space-between',
  26. transition: theme.transitions.create('width', {
  27. duration,
  28. }),
  29. overflow: 'hidden',
  30. },
  31. rootCollapse: {
  32. width: '86px',
  33. },
  34. rootExpand: {
  35. width: (props: any) => props.width || '100%',
  36. },
  37. nested: {
  38. paddingLeft: theme.spacing(4),
  39. },
  40. item: {
  41. marginBottom: theme.spacing(2),
  42. paddingLeft: theme.spacing(4),
  43. boxSizing: 'content-box',
  44. height: theme.spacing(3),
  45. width: 'initial',
  46. color: theme.palette.milvusGrey.dark,
  47. },
  48. itemIcon: {
  49. minWidth: '20px',
  50. marginRight: theme.spacing(1),
  51. '& .icon': {
  52. fill: 'transparent',
  53. '& path': {
  54. stroke: theme.palette.milvusGrey.dark,
  55. },
  56. },
  57. },
  58. itemText: {
  59. whiteSpace: 'nowrap',
  60. },
  61. active: {
  62. color: theme.palette.primary.main,
  63. borderRight: `2px solid ${theme.palette.primary.main}`,
  64. '& .icon': {
  65. '& path': {
  66. stroke: theme.palette.primary.main,
  67. },
  68. },
  69. },
  70. logoWrapper: {
  71. width: '100%',
  72. display: 'flex',
  73. alignItems: 'center',
  74. height: '86px',
  75. marginBottom: theme.spacing(8),
  76. backgroundColor: theme.palette.primary.main,
  77. paddingLeft: theme.spacing(4),
  78. '& .title': {
  79. margin: 0,
  80. fontSize: '16px',
  81. letterSpacing: '0.15px',
  82. color: 'white',
  83. whiteSpace: 'nowrap',
  84. lineHeight: '86px',
  85. },
  86. },
  87. logo: {
  88. transition: theme.transitions.create('all', {
  89. duration,
  90. }),
  91. },
  92. logoExpand: {
  93. marginRight: theme.spacing(1),
  94. '& path': {
  95. fill: 'white',
  96. },
  97. },
  98. logoCollapse: {
  99. backgroundColor: theme.palette.primary.main,
  100. '& path': {
  101. fill: 'white',
  102. },
  103. transform: 'scale(1.5)',
  104. },
  105. actionIcon: {
  106. position: 'fixed',
  107. borderRadius: '50%',
  108. backgroundColor: 'white',
  109. top: '74px',
  110. transition: theme.transitions.create('all', {
  111. duration,
  112. }),
  113. minWidth: '24px',
  114. padding: 0,
  115. '& svg path': {
  116. fill: theme.palette.milvusGrey.dark,
  117. },
  118. '&:hover': {
  119. backgroundColor: theme.palette.primary.main,
  120. '& svg path': {
  121. fill: 'white',
  122. },
  123. },
  124. },
  125. expandIcon: {
  126. left: '187px',
  127. transform: 'rotateZ(180deg)',
  128. },
  129. collapseIcon: {
  130. left: '73px',
  131. },
  132. })
  133. );
  134. const NavMenu: FC<NavMenuType> = props => {
  135. const { width, data, defaultActive = '' } = props;
  136. const classes = useStyles({ width });
  137. const [expanded, setExpanded] = useState<boolean>(false);
  138. const [active, setActive] = useState<string>(defaultActive);
  139. const { t } = useTranslation();
  140. const milvusTrans: { [key in string]: string } = t('milvus');
  141. useEffect(() => {
  142. if (defaultActive) {
  143. setActive(defaultActive);
  144. }
  145. }, [defaultActive]);
  146. const NestList = (props: { data: NavMenuItem[]; className?: string }) => {
  147. const { className = '', data } = props;
  148. return (
  149. <>
  150. {data.map((v: NavMenuItem) => {
  151. const IconComponent = v.icon;
  152. const isActive = active === v.label;
  153. const iconClass =
  154. v.iconActiveClass && v.iconNormalClass
  155. ? isActive
  156. ? v.iconActiveClass
  157. : v.iconNormalClass
  158. : 'icon';
  159. return (
  160. <ListItem
  161. button
  162. key={v.label}
  163. title={v.label}
  164. className={
  165. clsx(classes.item, {
  166. [className]: className,
  167. [classes.active]: isActive,
  168. })
  169. }
  170. onClick={() => {
  171. setActive(v.label);
  172. v.onClick && v.onClick();
  173. }}
  174. >
  175. <ListItemIcon className={classes.itemIcon}>
  176. <IconComponent classes={{ root: iconClass }} />
  177. </ListItemIcon>
  178. <Fade in={expanded} timeout={timeout}>
  179. <ListItemText className={classes.itemText}
  180. primary={v.label} />
  181. </Fade>
  182. </ListItem>
  183. );
  184. })
  185. }
  186. </>
  187. );
  188. };
  189. const Logo = icons.milvus;
  190. return (
  191. <List component="nav" className={
  192. clsx(classes.root, {
  193. [classes.rootExpand]: expanded,
  194. [classes.rootCollapse]: !expanded,
  195. })
  196. }>
  197. <div>
  198. <div className={classes.logoWrapper}>
  199. <Logo
  200. classes={{ root: classes.logo }}
  201. className={
  202. clsx({
  203. [classes.logoExpand]: expanded,
  204. [classes.logoCollapse]: !expanded,
  205. })
  206. }
  207. />
  208. <Fade in={expanded} timeout={timeout}>
  209. <Typography variant="h3" className="title">
  210. {milvusTrans.admin}
  211. </Typography>
  212. </Fade>
  213. </div>
  214. <Button
  215. onClick={() => { setExpanded(!expanded) }}
  216. className={
  217. clsx(classes.actionIcon, {
  218. [classes.expandIcon]: expanded,
  219. [classes.collapseIcon]: !expanded,
  220. })
  221. }
  222. >
  223. <ChevronRightIcon />
  224. </Button>
  225. <NestList data={data} />
  226. </div>
  227. </List>
  228. );
  229. };
  230. export default NavMenu;