allow NavDrawer to be pinned on 1200+ wide screens

This commit is contained in:
Sidney Alcantara
2021-09-22 19:31:01 +10:00
parent 859119a394
commit 0cf43be5a1
7 changed files with 160 additions and 49 deletions

View File

@@ -139,13 +139,15 @@ export default function App() {
render={() => (
<Navigation
title="Home"
titleComponent={
<Logo
style={{
display: "block",
margin: "0 auto",
}}
/>
titleComponent={(open, pinned) =>
!(open && pinned) && (
<Logo
style={{
display: "block",
margin: "0 auto",
}}
/>
)
}
>
<HomePage />

View File

@@ -16,6 +16,8 @@ import SettingsIcon from "@mui/icons-material/SettingsOutlined";
import ProjectSettingsIcon from "@mui/icons-material/BuildCircleOutlined";
import UserManagementIcon from "@mui/icons-material/AccountCircleOutlined";
import CloseIcon from "assets/icons/Backburger";
import PinIcon from "@mui/icons-material/PushPinOutlined";
import UnpinIcon from "@mui/icons-material/PushPin";
import { APP_BAR_HEIGHT } from ".";
import Logo from "assets/Logo";
@@ -31,10 +33,17 @@ export const NAV_DRAWER_WIDTH = 256;
export interface INavDrawerProps extends DrawerProps {
currentSection?: string;
onClose: NonNullable<DrawerProps["onClose"]>;
pinned: boolean;
setPinned: React.Dispatch<React.SetStateAction<boolean>>;
canPin: boolean;
}
export default function NavDrawer({
open,
currentSection,
pinned,
setPinned,
canPin,
...props
}: INavDrawerProps) {
const { userDoc, userClaims } = useAppContext();
@@ -52,14 +61,45 @@ export default function NavDrawer({
return (
<Drawer
open={open}
{...props}
sx={{ "& .MuiDrawer-paper": { minWidth: NAV_DRAWER_WIDTH } }}
variant={pinned ? "persistent" : "temporary"}
anchor="left"
sx={{
width: open ? NAV_DRAWER_WIDTH : 0,
transition: (theme) =>
theme.transitions.create("width", {
easing: pinned
? theme.transitions.easing.easeOut
: theme.transitions.easing.sharp,
duration: pinned
? theme.transitions.duration.enteringScreen
: theme.transitions.duration.leavingScreen,
}),
flexShrink: 0,
"& .MuiDrawer-paper": {
minWidth: NAV_DRAWER_WIDTH,
bgcolor: pinned ? "background.default" : "background.paper",
},
}}
>
<Stack
direction="row"
spacing={1.5}
alignItems="center"
sx={{ height: APP_BAR_HEIGHT, flexShrink: 0, pl: 0.5 }}
sx={{
height: APP_BAR_HEIGHT,
flexShrink: 0,
px: 0.5,
position: "sticky",
top: 0,
zIndex: "appBar",
backgroundColor: "inherit",
backgroundImage: "inherit",
}}
>
<IconButton
aria-label="Close navigation drawer"
@@ -70,12 +110,27 @@ export default function NavDrawer({
</IconButton>
<Logo />
{canPin && (
<IconButton
aria-label="Pin navigation drawer"
onClick={() => setPinned((p) => !p)}
aria-pressed={pinned}
size="large"
style={{ marginLeft: "auto" }}
>
{pinned ? <UnpinIcon /> : <PinIcon />}
</IconButton>
)}
</Stack>
<nav>
<List disablePadding>
<li>
<NavItem to={routes.home} onClick={closeDrawer}>
<NavItem
to={routes.home}
onClick={pinned ? undefined : closeDrawer}
>
<ListItemIcon>
<HomeIcon />
</ListItemIcon>
@@ -83,7 +138,10 @@ export default function NavDrawer({
</NavItem>
</li>
<li>
<NavItem to={routes.userSettings} onClick={closeDrawer}>
<NavItem
to={routes.userSettings}
onClick={pinned ? undefined : closeDrawer}
>
<ListItemIcon>
<SettingsIcon />
</ListItemIcon>
@@ -92,7 +150,10 @@ export default function NavDrawer({
</li>
{userClaims?.roles?.includes("ADMIN") && (
<li>
<NavItem to={routes.projectSettings} onClick={closeDrawer}>
<NavItem
to={routes.projectSettings}
onClick={pinned ? undefined : closeDrawer}
>
<ListItemIcon>
<ProjectSettingsIcon />
</ListItemIcon>
@@ -102,7 +163,10 @@ export default function NavDrawer({
)}
{userClaims?.roles?.includes("ADMIN") && (
<li>
<NavItem to={routes.userManagement} onClick={closeDrawer}>
<NavItem
to={routes.userManagement}
onClick={pinned ? undefined : closeDrawer}
>
<ListItemIcon>
<UserManagementIcon />
</ListItemIcon>
@@ -122,7 +186,7 @@ export default function NavDrawer({
section={section}
tables={tables}
currentSection={currentSection}
closeDrawer={closeDrawer}
closeDrawer={pinned ? undefined : closeDrawer}
/>
))}
</List>

View File

@@ -13,7 +13,7 @@ export interface INavDrawerItemProps {
section: string;
tables: Table[];
currentSection?: string;
closeDrawer: (e: {}) => void;
closeDrawer?: (e: {}) => void;
}
export default function NavDrawerItem({

View File

@@ -1,7 +1,10 @@
import { ReactNode, useState, Suspense } from "react";
import { ReactNode, Suspense, useEffect } from "react";
import createPersistedState from "use-persisted-state";
import {
useScrollTrigger,
useMediaQuery,
Stack,
AppBar,
Toolbar,
IconButton,
@@ -12,21 +15,23 @@ import {
} from "@mui/material";
import MenuIcon from "@mui/icons-material/Menu";
import NavDrawer from "./NavDrawer";
import NavDrawer, { NAV_DRAWER_WIDTH } from "./NavDrawer";
import UserMenu from "./UserMenu";
import ErrorBoundary from "components/ErrorBoundary";
import Loading from "components/Loading";
import { name } from "@root/package.json";
import { useAppContext } from "contexts/AppContext";
import useDocumentTitle from "hooks/useDocumentTitle";
export const APP_BAR_HEIGHT = 56;
const useOpenState = createPersistedState("__ROWY__NAV_OPEN");
const usePinnedState = createPersistedState("__ROWY__NAV_PINNED");
export interface INavigationProps {
children: ReactNode;
title: string;
titleComponent?: ReactNode;
titleComponent?: (open: boolean, pinned: boolean) => ReactNode;
currentSection?: string;
titleTransitionProps?: Partial<GrowProps>;
}
@@ -41,9 +46,18 @@ export default function Navigation({
const { projectId } = useAppContext();
useDocumentTitle(projectId, title);
const [open, setOpen] = useState(false);
const [open, setOpen] = useOpenState(false);
const [pinned, setPinned] = usePinnedState(false);
const trigger = useScrollTrigger({ disableHysteresis: true, threshold: 0 });
const canPin = !useMediaQuery((theme: any) => theme.breakpoints.down("lg"));
useEffect(() => {
if (!canPin && pinned) {
setPinned(false);
setOpen(false);
}
}, [canPin, pinned, setPinned, setOpen]);
return (
<>
<AppBar
@@ -68,6 +82,17 @@ export default function Navigation({
opacity: trigger ? 0 : 1,
transition: (theme) => theme.transitions.create("opacity"),
},
pl: pinned && open ? `${NAV_DRAWER_WIDTH}px` : 0,
transition: (theme) =>
theme.transitions.create("padding-left", {
easing: pinned
? theme.transitions.easing.easeOut
: theme.transitions.easing.sharp,
duration: pinned
? theme.transitions.duration.enteringScreen
: theme.transitions.duration.leavingScreen,
}),
}}
>
<Toolbar
@@ -85,20 +110,31 @@ export default function Navigation({
},
}}
>
<Grow in>
<IconButton
aria-label="Open navigation drawer"
onClick={() => setOpen(true)}
size="large"
edge="start"
>
<MenuIcon />
</IconButton>
</Grow>
{!(open && pinned) && (
<Grow in>
<IconButton
aria-label="Open navigation drawer"
onClick={() => setOpen(true)}
size="large"
edge="start"
>
<MenuIcon />
</IconButton>
</Grow>
)}
<Grow in key={title} {...titleTransitionProps}>
<Box sx={{ flex: 1, overflowX: "auto", userSelect: "none" }}>
{titleComponent || (
<Box
sx={{
flex: 1,
overflowX: "auto",
userSelect: "none",
pl: open && pinned ? 48 / 8 : 0,
}}
>
{titleComponent ? (
titleComponent(open, pinned)
) : (
<Typography
variant="h6"
component="h1"
@@ -116,21 +152,26 @@ export default function Navigation({
</Toolbar>
</AppBar>
<NavDrawer
open={open}
onClose={() => setOpen(false)}
currentSection={currentSection}
/>
<Stack direction="row">
<NavDrawer
open={open}
pinned={pinned}
setPinned={setPinned}
canPin={canPin}
onClose={() => setOpen(false)}
currentSection={currentSection}
/>
<ErrorBoundary style={{ marginTop: -APP_BAR_HEIGHT }}>
<Suspense
fallback={
<Loading fullScreen style={{ marginTop: -APP_BAR_HEIGHT }} />
}
>
{children}
</Suspense>
</ErrorBoundary>
<ErrorBoundary style={{ marginTop: -APP_BAR_HEIGHT }}>
<Suspense
fallback={
<Loading fullScreen style={{ marginTop: -APP_BAR_HEIGHT }} />
}
>
<div style={{ flexGrow: 1 }}>{children}</div>
</Suspense>
</ErrorBoundary>
</Stack>
</>
);
}

View File

@@ -31,7 +31,7 @@ export default function PageNotFound() {
);
return (
<Navigation title="Page Not Found" titleComponent={<div />}>
<Navigation title="Page Not Found" titleComponent={() => <div />}>
<EmptyState
message="Page Not Found"
description={

View File

@@ -67,7 +67,9 @@ export default function TablePage() {
return (
<Navigation
title={tableName}
titleComponent={<Breadcrumbs />}
titleComponent={(open, pinned) => (
<Breadcrumbs sx={{ ml: open && pinned ? -48 / 8 : 2 }} />
)}
currentSection={currentSection}
titleTransitionProps={{ style: { transformOrigin: "0 50%" } }}
>

View File

@@ -70,7 +70,9 @@ export default function TestView() {
return (
<Navigation title="Theme Test">
<Container style={{ margin: "24px 0 200px", overflowX: "auto" }}>
<Container
style={{ margin: "24px 0 200px", overflowX: "auto", width: "100%" }}
>
<Stack spacing={8}>
<Table stickyHeader>
<TableHead>