NavMenu.tsx 6.5 KB

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