🎉 Initial New Design Project
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
import { ReactNode, useEffect } from "react";
|
||||
import { useWellKnown } from "@/stores/wellKnown.tsx";
|
||||
import { useUserinfo } from "@/stores/userinfo.tsx";
|
||||
|
||||
export default function AppLoader({ children }: { children: ReactNode }) {
|
||||
const { readWellKnown } = useWellKnown();
|
||||
const { readProfiles } = useUserinfo();
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([readWellKnown(), readProfiles()]);
|
||||
}, []);
|
||||
|
||||
return children;
|
||||
}
|
@@ -1,95 +0,0 @@
|
||||
import {
|
||||
AppBar,
|
||||
Avatar,
|
||||
Box,
|
||||
IconButton,
|
||||
Slide,
|
||||
Toolbar,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useScrollTrigger
|
||||
} from "@mui/material";
|
||||
import { ReactElement, ReactNode, useEffect, useRef, useState } from "react";
|
||||
import { SITE_NAME } from "@/consts";
|
||||
import { Link } from "react-router-dom";
|
||||
import NavigationMenu, { AppNavigationHeader, isMobileQuery } from "@/components/NavigationMenu.tsx";
|
||||
import AccountCircleIcon from "@mui/icons-material/AccountCircleOutlined";
|
||||
import { useUserinfo } from "@/stores/userinfo.tsx";
|
||||
|
||||
function HideOnScroll(props: { window?: () => Window; children: ReactElement }) {
|
||||
const { children, window } = props;
|
||||
const trigger = useScrollTrigger({
|
||||
target: window ? window() : undefined
|
||||
});
|
||||
|
||||
return (
|
||||
<Slide appear={false} direction="down" in={!trigger}>
|
||||
{children}
|
||||
</Slide>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AppShell({ children }: { children: ReactNode }) {
|
||||
let documentWindow: Window;
|
||||
|
||||
const { userinfo } = useUserinfo();
|
||||
|
||||
const isMobile = useMediaQuery(isMobileQuery);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
documentWindow = window;
|
||||
}, []);
|
||||
|
||||
const container = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<HideOnScroll window={() => documentWindow}>
|
||||
<AppBar position="fixed">
|
||||
<Toolbar sx={{ height: 64 }}>
|
||||
<IconButton
|
||||
size="large"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
aria-label="menu"
|
||||
sx={{ ml: isMobile ? 0.5 : 0, mr: 2 }}
|
||||
>
|
||||
<img src="/favicon.svg" alt="Logo" width={32} height={32} />
|
||||
</IconButton>
|
||||
|
||||
<Typography variant="h6" component="div" sx={{ flexGrow: 1, fontSize: "1.2rem" }}>
|
||||
<Link to="/">{SITE_NAME}</Link>
|
||||
</Typography>
|
||||
|
||||
<IconButton
|
||||
size="large"
|
||||
edge="start"
|
||||
color="inherit"
|
||||
aria-label="menu"
|
||||
onClick={() => setOpen(true)}
|
||||
sx={{ mr: 1 }}
|
||||
>
|
||||
<Avatar
|
||||
sx={{ width: 32, height: 32, bgcolor: "transparent" }}
|
||||
ref={container}
|
||||
alt={userinfo?.displayName}
|
||||
src={`/api/avatar/${userinfo?.data?.avatar}`}
|
||||
>
|
||||
<AccountCircleIcon />
|
||||
</Avatar>
|
||||
</IconButton>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
</HideOnScroll>
|
||||
|
||||
<Box component="main">
|
||||
<AppNavigationHeader />
|
||||
|
||||
{children}
|
||||
</Box>
|
||||
|
||||
<NavigationMenu anchorEl={container.current} open={open} onClose={() => setOpen(false)} />
|
||||
</>
|
||||
);
|
||||
}
|
@@ -1,98 +0,0 @@
|
||||
import { Collapse, Divider, ListItemIcon, ListItemText, Menu, MenuItem, styled } from "@mui/material";
|
||||
import { theme } from "@/theme";
|
||||
import { Fragment, ReactNode, useState } from "react";
|
||||
import HowToRegIcon from "@mui/icons-material/HowToReg";
|
||||
import LoginIcon from "@mui/icons-material/Login";
|
||||
import FaceIcon from "@mui/icons-material/Face";
|
||||
import LogoutIcon from "@mui/icons-material/ExitToApp";
|
||||
import ExpandLess from "@mui/icons-material/ExpandLess";
|
||||
import ExpandMore from "@mui/icons-material/ExpandMore";
|
||||
import { useUserinfo } from "@/stores/userinfo.tsx";
|
||||
import { PopoverProps } from "@mui/material/Popover";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export interface NavigationItem {
|
||||
icon?: ReactNode;
|
||||
title?: string;
|
||||
link?: string;
|
||||
divider?: boolean;
|
||||
children?: NavigationItem[];
|
||||
}
|
||||
|
||||
export const DRAWER_WIDTH = 320;
|
||||
|
||||
export const AppNavigationHeader = styled("div")(({ theme }) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
padding: theme.spacing(0, 1),
|
||||
justifyContent: "flex-start",
|
||||
height: 64,
|
||||
...theme.mixins.toolbar
|
||||
}));
|
||||
|
||||
export function AppNavigationSection({ items, depth }: { items: NavigationItem[], depth?: number }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return items.map((item, idx) => {
|
||||
if (item.divider) {
|
||||
return <Divider key={idx} sx={{ my: 1 }} />;
|
||||
} else if (item.children) {
|
||||
return (
|
||||
<Fragment key={idx}>
|
||||
<MenuItem onClick={() => setOpen(!open)} sx={{ pl: 2 + (depth ?? 0) * 2, width: 180 }}>
|
||||
<ListItemIcon>{item.icon}</ListItemIcon>
|
||||
<ListItemText primary={item.title} />
|
||||
{open ? <ExpandLess /> : <ExpandMore />}
|
||||
</MenuItem>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
<AppNavigationSection items={item.children} depth={(depth ?? 0) + 1} />
|
||||
</Collapse>
|
||||
</Fragment>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Link key={idx} to={item.link ?? "/"}>
|
||||
<MenuItem sx={{ pl: 2 + (depth ?? 0) * 2, width: 180 }}>
|
||||
<ListItemIcon>{item.icon}</ListItemIcon>
|
||||
<ListItemText primary={item.title} />
|
||||
</MenuItem>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function AppNavigation() {
|
||||
const { checkLoggedIn } = useUserinfo();
|
||||
|
||||
const nav: NavigationItem[] = [
|
||||
...(
|
||||
checkLoggedIn() ?
|
||||
[
|
||||
{ icon: <FaceIcon />, title: "Account", link: "/users" },
|
||||
{ divider: true },
|
||||
{ icon: <LogoutIcon />, title: "Sign out", link: "/auth/sign-out" }
|
||||
] :
|
||||
[
|
||||
{ icon: <HowToRegIcon />, title: "Sign up", link: "/auth/sign-up" },
|
||||
{ icon: <LoginIcon />, title: "Sign in", link: "/auth/sign-in" }
|
||||
]
|
||||
)
|
||||
];
|
||||
|
||||
return <AppNavigationSection items={nav} />;
|
||||
}
|
||||
|
||||
export const isMobileQuery = theme.breakpoints.down("md");
|
||||
|
||||
export default function NavigationMenu({ anchorEl, open, onClose }: {
|
||||
anchorEl: PopoverProps["anchorEl"];
|
||||
open: boolean;
|
||||
onClose: () => void
|
||||
}) {
|
||||
return (
|
||||
<Menu anchorEl={anchorEl} open={open} onClose={onClose}>
|
||||
<AppNavigation />
|
||||
</Menu>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user