🎉 Initial Commit for v2!

This commit is contained in:
2024-02-24 14:16:57 +08:00
parent 240ed2deaf
commit 742894d2e9
71 changed files with 522 additions and 2383 deletions

138
components/AppShell.tsx Normal file
View File

@ -0,0 +1,138 @@
"use client";
import {
Slide,
Toolbar,
Typography,
AppBar as MuiAppBar,
AppBarProps as MuiAppBarProps,
useScrollTrigger,
IconButton,
styled, Box, useMediaQuery
} from "@mui/material";
import { ReactElement, ReactNode, useEffect, useState } from "react";
import { SITE_NAME } from "@/app/consts";
import NavigationDrawer, { DRAWER_WIDTH, AppNavigationHeader, isMobileQuery } from "@/components/NavigationDrawer";
import MenuIcon from "@mui/icons-material/Menu";
import Image from "next/image";
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>
);
}
interface AppBarProps extends MuiAppBarProps {
open?: boolean;
}
const ShellAppBar = styled(MuiAppBar, {
shouldForwardProp: (prop) => prop !== "open"
})<AppBarProps>(({ theme, open }) => {
const isMobile = useMediaQuery(isMobileQuery);
return ({
transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
...(!isMobile && open && {
width: `calc(100% - ${DRAWER_WIDTH}px)`,
transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
}),
marginRight: DRAWER_WIDTH
})
});
});
const AppMain = styled("main", { shouldForwardProp: (prop) => prop !== "open" })<{
open?: boolean;
}>(({ theme, open }) => {
const isMobile = useMediaQuery(isMobileQuery);
return ({
flexGrow: 1,
padding: theme.spacing(3),
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
marginRight: -DRAWER_WIDTH,
...(!isMobile && open && {
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
}),
marginRight: 0
}),
position: "relative"
});
});
export default function AppShell({ children }: {
children: ReactNode,
}) {
let documentWindow: Window;
const isMobile = useMediaQuery(isMobileQuery);
const [open, setOpen] = useState(false);
useEffect(() => {
documentWindow = window;
})
return (
<Box sx={{ display: "flex" }}>
<HideOnScroll window={() => documentWindow}>
<ShellAppBar open={open}>
<Toolbar>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="menu"
sx={{ mr: 2 }}
>
<Image src="smartsheep.svg" alt="Logo" width={32} height={32} />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
{SITE_NAME}
</Typography>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="menu"
onClick={() => setOpen(true)}
sx={{ mr: 1, display: !isMobile && open ? "none" : "block" }}
>
<MenuIcon />
</IconButton>
</Toolbar>
</ShellAppBar>
</HideOnScroll>
<AppMain open={open}>
<AppNavigationHeader />
{children}
</AppMain>
<NavigationDrawer open={open} onClose={() => setOpen(false)} />
</Box>
);
}

View File

@ -0,0 +1,119 @@
"use client";
import ChevronLeftIcon from "@mui/icons-material/ChevronLeft";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import InboxIcon from "@mui/icons-material/MoveToInbox";
import MailIcon from "@mui/icons-material/Mail";
import {
Box,
Divider,
Drawer,
IconButton,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
styled,
useMediaQuery
} from "@mui/material";
import { theme } from "@/app/theme";
export const DRAWER_WIDTH = 320;
export const AppNavigationHeader = styled("div")(({ theme }) => ({
display: "flex",
alignItems: "center",
padding: theme.spacing(0, 1),
justifyContent: "flex-start",
...theme.mixins.toolbar
}));
export function AppNavigation({ showClose, onClose }: {
showClose?: boolean,
onClose: () => void
}) {
return (
<>
<AppNavigationHeader>
{
showClose &&
<IconButton onClick={onClose}>
{theme.direction === "rtl" ? <ChevronLeftIcon /> : <ChevronRightIcon />}
</IconButton>
}
</AppNavigationHeader>
<Divider />
<List>
{["Inbox", "Starred", "Send email", "Drafts"].map((text, index) => (
<ListItem key={text} disablePadding>
<ListItemButton>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItemButton>
</ListItem>
))}
</List>
<Divider />
<List>
{["All mail", "Trash", "Spam"].map((text, index) => (
<ListItem key={text} disablePadding>
<ListItemButton>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItemButton>
</ListItem>
))}
</List>
</>
);
}
export const isMobileQuery = theme.breakpoints.down("md");
export default function NavigationDrawer({ open, onClose }: {
open: boolean,
onClose: () => void,
}) {
const isMobile = useMediaQuery(isMobileQuery);
return isMobile ? (
<>
<Box sx={{ flexShrink: 0, width: DRAWER_WIDTH }} />
<Drawer
keepMounted
anchor="right"
variant="temporary"
open={open}
onClose={onClose}
sx={{
"& .MuiDrawer-paper": {
boxSizing: "border-box",
width: DRAWER_WIDTH
}
}}
>
<AppNavigation onClose={onClose} />
</Drawer>
</>
) : (
<Drawer
variant="persistent"
anchor="right"
open={open}
sx={{
width: DRAWER_WIDTH,
flexShrink: 0,
"& .MuiDrawer-paper": {
width: DRAWER_WIDTH
}
}}
>
<AppNavigation showClose onClose={onClose} />
</Drawer>
);
}