Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

234 rindas
5.6KB

  1. import { ExpandLess, ExpandMore } from "@mui/icons-material";
  2. import HomeIcon from "@mui/icons-material/Home";
  3. import PeopleIcon from "@mui/icons-material/People";
  4. import SettingsIcon from "@mui/icons-material/Settings";
  5. import { Collapse } from "@mui/material";
  6. import Box from "@mui/material/Box";
  7. import Divider from "@mui/material/Divider";
  8. import Drawer, { DrawerProps } from "@mui/material/Drawer";
  9. import List from "@mui/material/List";
  10. import ListItem from "@mui/material/ListItem";
  11. import ListItemButton from "@mui/material/ListItemButton";
  12. import ListItemIcon from "@mui/material/ListItemIcon";
  13. import ListItemText from "@mui/material/ListItemText";
  14. import { PageID } from "codes/page";
  15. import useAuth from "hooks/useAuth";
  16. import useNavigateCustom from "hooks/useNavigateCustom";
  17. import usePage from "hooks/usePage";
  18. import * as React from "react";
  19. import { getPath } from "routes/path";
  20. type Group = {
  21. label: string;
  22. children: SubGroup[];
  23. };
  24. type SubGroup = {
  25. label: string;
  26. icon: React.ReactNode;
  27. children?: Child[];
  28. // 子要素を持たない場合は設定
  29. id?: PageID;
  30. };
  31. type Child = {
  32. label: string;
  33. id: PageID;
  34. };
  35. const categories: Group[] = [
  36. {
  37. label: "管理",
  38. children: [
  39. {
  40. label: "契約",
  41. icon: <PeopleIcon />,
  42. children: [
  43. {
  44. id: PageID.DASHBOARD_CONTRACT_LIST,
  45. label: "一覧",
  46. },
  47. {
  48. id: PageID.DASHBOARD_CONTRACT_DETAIL,
  49. label: "詳細",
  50. },
  51. ],
  52. },
  53. {
  54. label: "領収証発行依頼",
  55. icon: <PeopleIcon />,
  56. children: [
  57. {
  58. id: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST,
  59. label: "一覧",
  60. },
  61. {
  62. id: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_CREATE,
  63. label: "新規",
  64. },
  65. ],
  66. },
  67. ],
  68. },
  69. {
  70. label: "アカウント",
  71. children: [
  72. { label: "ログアウト", icon: <SettingsIcon />, id: PageID.LOGOUT },
  73. ],
  74. },
  75. ];
  76. const item = {
  77. py: "2px",
  78. px: 3,
  79. color: "rgba(255, 255, 255, 0.7)",
  80. "&:hover, &:focus": {
  81. bgcolor: "rgba(255, 255, 255, 0.08)",
  82. },
  83. };
  84. const itemCategory = {
  85. boxShadow: "0 -1px 0 rgb(255,255,255,0.1) inset",
  86. py: 1.5,
  87. px: 3,
  88. };
  89. export default function Navigator(props: DrawerProps) {
  90. const { ...other } = props;
  91. return (
  92. <Drawer variant="permanent" {...other}>
  93. <List disablePadding>
  94. <ListItem
  95. sx={{ ...item, ...itemCategory, fontSize: 22, color: "#fff" }}
  96. >
  97. SateReceipt
  98. </ListItem>
  99. <ListItem sx={{ ...item, ...itemCategory }}>
  100. <ListItemIcon>
  101. <HomeIcon />
  102. </ListItemIcon>
  103. <ListItemText>Project Overview</ListItemText>
  104. </ListItem>
  105. {categories.map((group, index) => {
  106. return <Group {...group} key={index} />;
  107. })}
  108. </List>
  109. </Drawer>
  110. );
  111. }
  112. function Group(group: Group) {
  113. const { label, children } = group;
  114. const elements = children.map((ele, index) => (
  115. <SubGroup {...ele} key={index} />
  116. ));
  117. if (elements.length === 0) return null;
  118. return (
  119. <Box sx={{ bgcolor: "#101F33" }}>
  120. <ListItem sx={{ py: 2, px: 3 }}>
  121. <ListItemText sx={{ color: "#fff" }}>{label}</ListItemText>
  122. </ListItem>
  123. {elements}
  124. <Divider sx={{ mt: 2 }} />
  125. </Box>
  126. );
  127. }
  128. function SubGroup({ icon, label, id, children }: SubGroup) {
  129. const { pageId } = usePage();
  130. const { navigateWhenChanged } = useNavigateCustom();
  131. const { elements, shouldOpen } = useContents(children ?? []);
  132. const [open, setOpen] = React.useState(false);
  133. React.useEffect(() => {
  134. setOpen(shouldOpen);
  135. }, [shouldOpen]);
  136. // 子要素ありの場合
  137. if (elements && elements.length !== 0) {
  138. const handleClick = () => {
  139. setOpen(!open);
  140. };
  141. return (
  142. <>
  143. <ListItemButton onClick={handleClick} sx={item} selected={false}>
  144. <ListItemIcon>{icon}</ListItemIcon>
  145. <ListItemText>{label}</ListItemText>
  146. {open ? <ExpandLess /> : <ExpandMore />}
  147. </ListItemButton>
  148. <Collapse in={open} timeout="auto" unmountOnExit>
  149. <List component="div" disablePadding>
  150. {elements}
  151. </List>
  152. </Collapse>
  153. </>
  154. );
  155. }
  156. // 子要素なしの場合
  157. if (id !== undefined) {
  158. const handleClick = () => {
  159. if (id) {
  160. const path = getPath(id);
  161. navigateWhenChanged(path);
  162. }
  163. };
  164. const selected = id === pageId;
  165. return (
  166. <ListItemButton onClick={handleClick} selected={selected} sx={item}>
  167. <ListItemIcon>{icon}</ListItemIcon>
  168. <ListItemText>{label}</ListItemText>
  169. </ListItemButton>
  170. );
  171. }
  172. return null;
  173. }
  174. function useContents(children: Child[]) {
  175. const { pageId } = usePage();
  176. const { navigateWhenChanged } = useNavigateCustom();
  177. const { canAccess, initialized } = useAuth();
  178. const [shouldOpen, setShouldOpen] = React.useState(false);
  179. const elements = React.useMemo(() => {
  180. setShouldOpen(false);
  181. return children
  182. .filter(({ id }) => canAccess(id))
  183. .map(({ label, id }, index) => {
  184. const selected = id === pageId;
  185. if (selected) {
  186. setShouldOpen(true);
  187. }
  188. const handleClick = () => {
  189. const path = getPath(id);
  190. navigateWhenChanged(path);
  191. };
  192. return (
  193. <ListItemButton
  194. selected={selected}
  195. sx={{ ...item, pl: 4 }}
  196. key={index}
  197. onClick={handleClick}
  198. >
  199. <ListItemText primary={label} />
  200. </ListItemButton>
  201. );
  202. });
  203. }, [pageId, initialized]);
  204. return {
  205. elements,
  206. shouldOpen,
  207. };
  208. }