mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
allow NavDrawer to be pinned on 1200+ wide screens
This commit is contained in:
16
src/App.tsx
16
src/App.tsx
@@ -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 />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -13,7 +13,7 @@ export interface INavDrawerItemProps {
|
||||
section: string;
|
||||
tables: Table[];
|
||||
currentSection?: string;
|
||||
closeDrawer: (e: {}) => void;
|
||||
closeDrawer?: (e: {}) => void;
|
||||
}
|
||||
|
||||
export default function NavDrawerItem({
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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={
|
||||
|
||||
@@ -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%" } }}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user