mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
replace desktop pinned nav with collapsible nav
This commit is contained in:
@@ -13,8 +13,6 @@ export const altPressAtom = atom(false);
|
||||
|
||||
/** Nav open state stored in local storage. */
|
||||
export const navOpenAtom = atomWithStorage("__ROWY__NAV_OPEN", false);
|
||||
/** Nav pinned state stored in local storage. */
|
||||
export const navPinnedAtom = atomWithStorage("__ROWY__NAV_PINNED", false);
|
||||
|
||||
/** View for tables page */
|
||||
export const tablesViewAtom = atomWithStorage<"grid" | "list">(
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import SearchIcon from "@mui/icons-material/Search";
|
||||
|
||||
import SlideTransition from "@src/components/Modal/SlideTransition";
|
||||
import { APP_BAR_HEIGHT } from "@src/layouts/Navigation";
|
||||
import { TOP_BAR_HEIGHT } from "@src/layouts/Navigation/TopBar";
|
||||
|
||||
export interface IFloatingSearchProps extends Partial<FilledTextFieldProps> {
|
||||
label: string;
|
||||
@@ -26,7 +26,7 @@ export default function FloatingSearch({
|
||||
});
|
||||
const docked = useScrollTrigger({
|
||||
disableHysteresis: true,
|
||||
threshold: APP_BAR_HEIGHT,
|
||||
threshold: TOP_BAR_HEIGHT,
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -5,7 +5,7 @@ import { HashLink } from "react-router-hash-link";
|
||||
import { Stack, StackProps, Typography, IconButton } from "@mui/material";
|
||||
import LinkIcon from "@mui/icons-material/Link";
|
||||
|
||||
import { APP_BAR_HEIGHT } from "@src/layouts/Navigation";
|
||||
import { TOP_BAR_HEIGHT } from "@src/layouts/Navigation/TopBar";
|
||||
|
||||
export interface ISectionHeadingProps extends Omit<StackProps, "children"> {
|
||||
children: string;
|
||||
@@ -35,7 +35,7 @@ export const SectionHeading = forwardRef(function SectionHeading_(
|
||||
opacity: 1,
|
||||
},
|
||||
|
||||
scrollMarginTop: (theme) => theme.spacing(APP_BAR_HEIGHT / 8 + 3.5),
|
||||
scrollMarginTop: (theme) => theme.spacing(TOP_BAR_HEIGHT / 8 + 3.5),
|
||||
scrollBehavior: "smooth",
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { styled, Drawer, drawerClasses } from "@mui/material";
|
||||
import { DRAWER_WIDTH, DRAWER_COLLAPSED_WIDTH } from "./index";
|
||||
import { APP_BAR_HEIGHT } from "@src/layouts/Navigation";
|
||||
import { TOP_BAR_HEIGHT } from "@src/layouts/Navigation/TopBar";
|
||||
import { TABLE_TOOLBAR_HEIGHT } from "@src/components/TableToolbar";
|
||||
|
||||
export const StyledDrawer = styled(Drawer)(({ theme }) => ({
|
||||
@@ -32,15 +32,15 @@ export const StyledDrawer = styled(Drawer)(({ theme }) => ({
|
||||
|
||||
boxSizing: "content-box",
|
||||
|
||||
top: APP_BAR_HEIGHT + TABLE_TOOLBAR_HEIGHT,
|
||||
height: `calc(100% - ${APP_BAR_HEIGHT + TABLE_TOOLBAR_HEIGHT}px)`,
|
||||
top: TOP_BAR_HEIGHT + TABLE_TOOLBAR_HEIGHT,
|
||||
height: `calc(100% - ${TOP_BAR_HEIGHT + TABLE_TOOLBAR_HEIGHT}px)`,
|
||||
".MuiDialog-paperFullScreen &": {
|
||||
top:
|
||||
APP_BAR_HEIGHT +
|
||||
TOP_BAR_HEIGHT +
|
||||
TABLE_TOOLBAR_HEIGHT +
|
||||
Number(theme.spacing(2).replace("px", "")),
|
||||
height: `calc(100% - ${
|
||||
APP_BAR_HEIGHT + TABLE_TOOLBAR_HEIGHT
|
||||
TOP_BAR_HEIGHT + TABLE_TOOLBAR_HEIGHT
|
||||
}px - ${theme.spacing(2)})`,
|
||||
},
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
columnModalAtom,
|
||||
tableModalAtom,
|
||||
} from "@src/atoms/tableScope";
|
||||
import { APP_BAR_HEIGHT } from "@src/layouts/Navigation";
|
||||
import { TOP_BAR_HEIGHT } from "@src/layouts/Navigation/TopBar";
|
||||
|
||||
export default function EmptyTable() {
|
||||
const openColumnModal = useSetAtom(columnModalAtom, tableScope);
|
||||
@@ -130,7 +130,7 @@ export default function EmptyTable() {
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
sx={{
|
||||
height: `calc(100vh - ${APP_BAR_HEIGHT}px)`,
|
||||
height: `calc(100vh - ${TOP_BAR_HEIGHT}px)`,
|
||||
width: "100%",
|
||||
p: 2,
|
||||
maxWidth: 480,
|
||||
|
||||
@@ -29,7 +29,6 @@ import {
|
||||
globalScope,
|
||||
userRolesAtom,
|
||||
userSettingsAtom,
|
||||
navPinnedAtom,
|
||||
} from "@src/atoms/globalScope";
|
||||
import {
|
||||
tableScope,
|
||||
@@ -66,7 +65,6 @@ export default function Table({
|
||||
}) {
|
||||
const [userRoles] = useAtom(userRolesAtom, globalScope);
|
||||
const [userSettings] = useAtom(userSettingsAtom, globalScope);
|
||||
const [navPinned] = useAtom(navPinnedAtom, globalScope);
|
||||
|
||||
const [tableId] = useAtom(tableIdAtom, tableScope);
|
||||
const [tableSettings] = useAtom(tableSettingsAtom, tableScope);
|
||||
@@ -188,8 +186,9 @@ export default function Table({
|
||||
|
||||
const target = event.target as HTMLDivElement;
|
||||
|
||||
if (navPinned && !columns[0].fixed)
|
||||
setShowLeftScrollDivider(target.scrollLeft > 16);
|
||||
// TODO:
|
||||
// if (navPinned && !columns[0].fixed)
|
||||
// setShowLeftScrollDivider(target.scrollLeft > 16);
|
||||
|
||||
const offset = 800;
|
||||
const isAtBottom =
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { colord } from "colord";
|
||||
import { styled, alpha, darken, lighten } from "@mui/material";
|
||||
import { APP_BAR_HEIGHT } from "@src/layouts/Navigation";
|
||||
import { TOP_BAR_HEIGHT } from "@src/layouts/Navigation/TopBar";
|
||||
import { TABLE_TOOLBAR_HEIGHT } from "@src/components/TableToolbar";
|
||||
import {
|
||||
DRAWER_COLLAPSED_WIDTH,
|
||||
@@ -15,7 +15,7 @@ export const TableContainer = styled("div", {
|
||||
display: "flex",
|
||||
position: "relative",
|
||||
flexDirection: "column",
|
||||
height: `calc(100vh - ${APP_BAR_HEIGHT}px - ${TABLE_TOOLBAR_HEIGHT}px)`,
|
||||
height: `calc(100vh - ${TOP_BAR_HEIGHT}px - ${TABLE_TOOLBAR_HEIGHT}px)`,
|
||||
|
||||
"& .left-scroll-divider": {
|
||||
position: "absolute",
|
||||
|
||||
@@ -27,6 +27,7 @@ const WIKI_PATHS = {
|
||||
setupRoles: "/setup/roles",
|
||||
setupUpdate: "/setup/update",
|
||||
|
||||
howTo: "/category/quickstart-guide",
|
||||
howToCreateTable: "/how-to/create-table",
|
||||
howToCreateColumn: "/how-to/create-column",
|
||||
howToAddRow: "/how-to/add-row",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Logo from "@src/assets/Logo";
|
||||
import BreadcrumbsTableRoot from "@src/components/Table/BreadcrumbsTableRoot";
|
||||
import { GrowProps } from "@mui/material";
|
||||
import { FadeProps } from "@mui/material";
|
||||
|
||||
export enum ROUTES {
|
||||
home = "/",
|
||||
@@ -46,22 +46,15 @@ export enum ROUTES {
|
||||
export const ROUTE_TITLES = {
|
||||
[ROUTES.tables]: {
|
||||
title: "Tables",
|
||||
titleComponent: (open, pinned) =>
|
||||
!(open && pinned) && (
|
||||
<Logo
|
||||
style={{
|
||||
display: "block",
|
||||
margin: "0 auto",
|
||||
}}
|
||||
/>
|
||||
titleComponent: (open, isPermanent) =>
|
||||
!(open && isPermanent) && (
|
||||
<Logo style={{ display: "block", margin: "0 auto" }} />
|
||||
),
|
||||
},
|
||||
|
||||
[ROUTES.table]: {
|
||||
title: "Table",
|
||||
titleComponent: (open, pinned) => (
|
||||
<BreadcrumbsTableRoot sx={{ ml: open && pinned ? -48 / 8 : 2 }} />
|
||||
),
|
||||
titleComponent: (_open, _isPermanent) => <BreadcrumbsTableRoot />,
|
||||
titleTransitionProps: { style: { transformOrigin: "0 50%" } },
|
||||
leftAligned: true,
|
||||
},
|
||||
@@ -80,8 +73,8 @@ export const ROUTE_TITLES = {
|
||||
| string
|
||||
| {
|
||||
title: string;
|
||||
titleComponent: (open: boolean, pinned: boolean) => React.ReactNode;
|
||||
titleTransitionProps?: Partial<GrowProps>;
|
||||
titleComponent: (open: boolean, isPermanent: boolean) => React.ReactNode;
|
||||
titleTransitionProps?: Partial<FadeProps>;
|
||||
leftAligned?: boolean;
|
||||
}
|
||||
>;
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
Grow,
|
||||
} from "@mui/material";
|
||||
import HelpIcon from "@mui/icons-material/HelpOutline";
|
||||
import DocsIcon from "@mui/icons-material/DescriptionOutlined";
|
||||
import DocsIcon from "@mui/icons-material/LibraryBooksOutlined";
|
||||
import { Discord as DiscordIcon } from "@src/assets/icons";
|
||||
import GitHubIcon from "@mui/icons-material/GitHub";
|
||||
import TwitterIcon from "@mui/icons-material/Twitter";
|
||||
@@ -44,7 +44,6 @@ export default function UserMenu(props: IconButtonProps) {
|
||||
setOpen(true);
|
||||
analytics.logEvent("open_help_menu");
|
||||
}}
|
||||
sx={{ ml: 1.5 }}
|
||||
>
|
||||
<HelpIcon />
|
||||
</IconButton>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { useAtom, useSetAtom } from "jotai";
|
||||
import { find, groupBy } from "lodash-es";
|
||||
import { colord } from "colord";
|
||||
|
||||
import {
|
||||
Drawer,
|
||||
@@ -9,19 +11,25 @@ import {
|
||||
List,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
// ListSubheader,
|
||||
Avatar,
|
||||
Divider,
|
||||
ListItemSecondaryAction,
|
||||
Box,
|
||||
Fade,
|
||||
} from "@mui/material";
|
||||
import CloseIcon from "@mui/icons-material/MenuOpen";
|
||||
import PinIcon from "@mui/icons-material/PushPinOutlined";
|
||||
import UnpinIcon from "@mui/icons-material/PushPin";
|
||||
import ArrowBackIcon from "@mui/icons-material/WorkspacesOutlined";
|
||||
import HomeIcon from "@mui/icons-material/HomeOutlined";
|
||||
import SettingsIcon from "@mui/icons-material/SettingsOutlined";
|
||||
import ProjectSettingsIcon from "@mui/icons-material/BuildCircleOutlined";
|
||||
import UserManagementIcon from "@mui/icons-material/AccountCircleOutlined";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import { Table as TableIcon } from "@src/assets/icons";
|
||||
import DocsIcon from "@mui/icons-material/LibraryBooksOutlined";
|
||||
import LearningIcon from "@mui/icons-material/LocalLibraryOutlined";
|
||||
import HelpIcon from "@mui/icons-material/HelpOutline";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
|
||||
import { APP_BAR_HEIGHT } from ".";
|
||||
import Logo from "@src/assets/Logo";
|
||||
import NavItem from "./NavItem";
|
||||
import NavTableSection from "./NavTableSection";
|
||||
@@ -36,24 +44,22 @@ import {
|
||||
} from "@src/atoms/globalScope";
|
||||
import { TableSettings } from "@src/types/table";
|
||||
import { ROUTES } from "@src/constants/routes";
|
||||
import { EXTERNAL_LINKS, WIKI_LINKS } from "@src/constants/externalLinks";
|
||||
import { TOP_BAR_HEIGHT } from "./TopBar";
|
||||
|
||||
export const NAV_DRAWER_WIDTH = 256;
|
||||
export const NAV_DRAWER_COLLAPSED_WIDTH = 56;
|
||||
|
||||
export interface INavDrawerProps extends DrawerProps {
|
||||
open: boolean;
|
||||
isPermanent: boolean;
|
||||
onClose: NonNullable<DrawerProps["onClose"]>;
|
||||
pinned: boolean;
|
||||
setPinned: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
canPin: boolean;
|
||||
scrollTrigger: boolean;
|
||||
}
|
||||
|
||||
export default function NavDrawer({
|
||||
open,
|
||||
pinned,
|
||||
setPinned,
|
||||
canPin,
|
||||
scrollTrigger,
|
||||
...props
|
||||
isPermanent,
|
||||
onClose,
|
||||
}: INavDrawerProps) {
|
||||
const [tables] = useAtom(tablesAtom, globalScope);
|
||||
const [userSettings] = useAtom(userSettingsAtom, globalScope);
|
||||
@@ -63,6 +69,8 @@ export default function NavDrawer({
|
||||
globalScope
|
||||
);
|
||||
|
||||
const [hover, setHover] = useState(false);
|
||||
|
||||
const favorites = Array.isArray(userSettings.favoriteTables)
|
||||
? userSettings.favoriteTables
|
||||
: [];
|
||||
@@ -73,146 +81,145 @@ export default function NavDrawer({
|
||||
...groupBy(tables, "section"),
|
||||
};
|
||||
|
||||
const closeDrawer = pinned
|
||||
const collapsed = !open && isPermanent;
|
||||
const tempExpanded = hover && collapsed;
|
||||
const width =
|
||||
collapsed && !tempExpanded ? NAV_DRAWER_COLLAPSED_WIDTH : NAV_DRAWER_WIDTH;
|
||||
const closeDrawer = isPermanent
|
||||
? undefined
|
||||
: (e: {}) => props.onClose(e, "escapeKeyDown");
|
||||
: (e: {}) => onClose(e, "escapeKeyDown");
|
||||
|
||||
const externalLinkIcon = !collapsed && (
|
||||
<ListItemSecondaryAction sx={{ right: 10, color: "text.disabled" }}>
|
||||
<InlineOpenInNewIcon />
|
||||
</ListItemSecondaryAction>
|
||||
);
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
open={open}
|
||||
{...props}
|
||||
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,
|
||||
borderRight: "none",
|
||||
bgcolor: pinned ? "background.default" : "background.paper",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
sx={{
|
||||
height: APP_BAR_HEIGHT,
|
||||
flexShrink: 0,
|
||||
px: 0.5,
|
||||
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
zIndex: "appBar",
|
||||
backgroundColor:
|
||||
pinned && scrollTrigger ? "background.paper" : "inherit",
|
||||
backgroundImage: pinned
|
||||
? "linear-gradient(rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.09))" // Elevation 8
|
||||
: "inherit",
|
||||
|
||||
"&::before": {
|
||||
content: "''",
|
||||
display: "block",
|
||||
position: "absolute",
|
||||
inset: 0,
|
||||
|
||||
bgcolor: "background.default",
|
||||
opacity: pinned ? (scrollTrigger ? 0 : 1) : 0,
|
||||
transition: (theme) =>
|
||||
theme.transitions.create("opacity", {
|
||||
easing:
|
||||
canPin && pinned
|
||||
? theme.transitions.easing.easeOut
|
||||
: theme.transitions.easing.sharp,
|
||||
duration:
|
||||
canPin && pinned
|
||||
? theme.transitions.duration.enteringScreen
|
||||
: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
<>
|
||||
<Drawer
|
||||
open={isPermanent || open}
|
||||
onClose={onClose}
|
||||
hideBackdrop={isPermanent}
|
||||
ModalProps={{ disablePortal: true }}
|
||||
variant={isPermanent ? "permanent" : "temporary"}
|
||||
anchor="left"
|
||||
sx={[
|
||||
{
|
||||
width,
|
||||
flexShrink: 0,
|
||||
"& .MuiDrawer-paper": {
|
||||
width,
|
||||
pt: 0,
|
||||
pb: 1,
|
||||
},
|
||||
},
|
||||
boxShadow: pinned && scrollTrigger ? 1 : 0,
|
||||
transition: (theme) =>
|
||||
theme.transitions.create(["background-color", "box-shadow"], {
|
||||
easing:
|
||||
canPin && pinned
|
||||
? theme.transitions.easing.easeOut
|
||||
: theme.transitions.easing.sharp,
|
||||
duration:
|
||||
canPin && pinned
|
||||
? theme.transitions.duration.enteringScreen
|
||||
: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
}}
|
||||
isPermanent && {
|
||||
position: "fixed",
|
||||
zIndex: (theme) => theme.zIndex.appBar - 1,
|
||||
|
||||
"& .MuiDrawer-paper": {
|
||||
mt: `${TOP_BAR_HEIGHT - 4}px`,
|
||||
height: `calc(100% - ${TOP_BAR_HEIGHT - 4}px)`,
|
||||
pt: 0.5,
|
||||
borderRadius: 2,
|
||||
borderTopLeftRadius: 0,
|
||||
borderBottomLeftRadius: 0,
|
||||
|
||||
width,
|
||||
transitionProperty:
|
||||
"width, transform, background-color, box-shadow",
|
||||
transitionTimingFunction: "var(--nav-transition-timing-function)",
|
||||
transitionDuration: "var(--nav-transition-duration)",
|
||||
overflowX: "hidden",
|
||||
|
||||
borderRight: "none",
|
||||
bgcolor: "background.default",
|
||||
},
|
||||
|
||||
"& .MuiListItemSecondaryAction-root": {
|
||||
transitionProperty: "opacity",
|
||||
transitionTimingFunction: "var(--nav-transition-timing-function)",
|
||||
transitionDuration: "var(--nav-transition-duration)",
|
||||
},
|
||||
},
|
||||
collapsed &&
|
||||
!tempExpanded && {
|
||||
"& .MuiMenuItem-root": {},
|
||||
"& .MuiListItemSecondaryAction-root": {
|
||||
opacity: 0,
|
||||
transitionDelay: "0ms",
|
||||
},
|
||||
},
|
||||
tempExpanded && {
|
||||
zIndex: "drawer",
|
||||
"& .MuiDrawer-paper": {
|
||||
bgcolor: (theme) =>
|
||||
colord(theme.palette.background.paper)
|
||||
.mix("#fff", 0.09)
|
||||
.alpha(1)
|
||||
.toHslString(),
|
||||
boxShadow: (theme) =>
|
||||
theme.shadows[4].replace(/, 0 (\d+px)/g, ", $1 0"),
|
||||
},
|
||||
},
|
||||
]}
|
||||
onMouseEnter={() => setHover(true)}
|
||||
onMouseLeave={() => setHover(false)}
|
||||
>
|
||||
<IconButton
|
||||
aria-label="Close navigation drawer"
|
||||
onClick={props.onClose as any}
|
||||
size="large"
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
|
||||
<Logo style={{ marginLeft: 1, position: "relative", zIndex: 1 }} />
|
||||
|
||||
{canPin && (
|
||||
<IconButton
|
||||
aria-label="Pin navigation drawer"
|
||||
onClick={() => setPinned((p) => !p)}
|
||||
aria-pressed={pinned}
|
||||
size="large"
|
||||
style={{ marginLeft: "auto" }}
|
||||
{!isPermanent && (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
sx={{ height: TOP_BAR_HEIGHT, flexShrink: 0, px: 0.5 }}
|
||||
>
|
||||
{pinned ? <UnpinIcon /> : <PinIcon />}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="Close navigation drawer"
|
||||
onClick={onClose as any}
|
||||
size="large"
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
|
||||
<Logo style={{ marginLeft: 1, position: "relative", zIndex: 1 }} />
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
<nav>
|
||||
<List>
|
||||
<li>
|
||||
<NavItem to={ROUTES.tables} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<HomeIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Home" />
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
{userRoles.includes("ADMIN") && (
|
||||
<Divider variant="middle" sx={{ my: 1 }} />
|
||||
)}
|
||||
|
||||
<li>
|
||||
<NavItem to={ROUTES.userSettings} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<SettingsIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Settings" />
|
||||
</NavItem>
|
||||
</li>
|
||||
{userRoles.includes("ADMIN") && (
|
||||
<nav style={{ flexGrow: 1 }}>
|
||||
<List
|
||||
disablePadding
|
||||
style={{ height: "100%", display: "flex", flexDirection: "column" }}
|
||||
>
|
||||
<li>
|
||||
<NavItem to={ROUTES.projectSettings} onClick={closeDrawer}>
|
||||
<NavItem onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<ProjectSettingsIcon />
|
||||
<ArrowBackIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Project Settings" />
|
||||
<UpdateCheckBadge sx={{ mr: 1.5 }} />
|
||||
<ListItemText primary="Workspace" />
|
||||
</NavItem>
|
||||
</li>
|
||||
)}
|
||||
{userRoles.includes("ADMIN") && (
|
||||
|
||||
<li>
|
||||
<NavItem onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<Avatar
|
||||
sx={{
|
||||
borderRadius: 1,
|
||||
width: 24,
|
||||
height: 24,
|
||||
fontSize: "inherit",
|
||||
bgcolor: "primary.main",
|
||||
color: "primary.contrastText",
|
||||
}}
|
||||
>
|
||||
P
|
||||
</Avatar>
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Project" />
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
{/* {userRoles.includes("ADMIN") && (
|
||||
<li>
|
||||
<NavItem to={ROUTES.userManagement} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
@@ -221,42 +228,126 @@ export default function NavDrawer({
|
||||
<ListItemText primary="User Management" />
|
||||
</NavItem>
|
||||
</li>
|
||||
)}
|
||||
)} */}
|
||||
|
||||
<Divider variant="middle" sx={{ my: 1 }} />
|
||||
<Divider variant="middle" sx={{ my: 1 }} />
|
||||
|
||||
{/* <ListSubheader>Your tables</ListSubheader> */}
|
||||
<li>
|
||||
<NavItem to={ROUTES.tables} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<HomeIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Home" />
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<NavItem
|
||||
{...({ component: "button" } as any)}
|
||||
style={{ textAlign: "left" }}
|
||||
sx={{ mb: 1 }}
|
||||
onClick={(e) => {
|
||||
if (closeDrawer) closeDrawer(e);
|
||||
openTableSettingsDialog({});
|
||||
}}
|
||||
>
|
||||
<li>
|
||||
<NavItem to={ROUTES.userSettings} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<SettingsIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Settings" />
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
{userRoles.includes("ADMIN") && (
|
||||
<li>
|
||||
<NavItem to={ROUTES.projectSettings} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<ProjectSettingsIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Project Settings" />
|
||||
<UpdateCheckBadge sx={{ mr: 1.5 }} />
|
||||
</NavItem>
|
||||
</li>
|
||||
)}
|
||||
|
||||
<Divider variant="middle" sx={{ my: 1 }} />
|
||||
|
||||
{/* <ListSubheader>Your tables</ListSubheader> */}
|
||||
|
||||
<li>
|
||||
<NavItem
|
||||
{...({ component: "button" } as any)}
|
||||
style={{ textAlign: "left" }}
|
||||
onClick={(e) => {
|
||||
if (closeDrawer) closeDrawer(e);
|
||||
openTableSettingsDialog({});
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<AddIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Create table…" />
|
||||
</NavItem>
|
||||
</li>
|
||||
|
||||
{/* <li>
|
||||
<NavItem to={ROUTES.tables} onClick={closeDrawer}>
|
||||
<ListItemIcon>
|
||||
<AddIcon />
|
||||
<TableIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Create table…" />
|
||||
<ListItemText primary="Table" />
|
||||
</NavItem>
|
||||
</li>
|
||||
</li> */}
|
||||
|
||||
{sections &&
|
||||
Object.entries(sections)
|
||||
.filter(([, tables]) => tables.length > 0)
|
||||
.map(([section, tables]) => (
|
||||
<NavTableSection
|
||||
key={section}
|
||||
section={section}
|
||||
tables={tables}
|
||||
closeDrawer={closeDrawer}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
</nav>
|
||||
</Drawer>
|
||||
{sections &&
|
||||
Object.entries(sections)
|
||||
.filter(([, tables]) => tables.length > 0)
|
||||
.map(([section, tables]) => (
|
||||
<NavTableSection
|
||||
key={section}
|
||||
section={section}
|
||||
tables={tables}
|
||||
closeDrawer={closeDrawer}
|
||||
hidden={isPermanent && !open && !tempExpanded}
|
||||
/>
|
||||
))}
|
||||
|
||||
<Divider variant="middle" sx={{ my: 1, mt: "auto" }} />
|
||||
|
||||
<li>
|
||||
<NavItem href={EXTERNAL_LINKS.docs}>
|
||||
<ListItemIcon>
|
||||
<DocsIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Docs" />
|
||||
{externalLinkIcon}
|
||||
</NavItem>
|
||||
</li>
|
||||
<li>
|
||||
<NavItem href={WIKI_LINKS.howTo}>
|
||||
<ListItemIcon>
|
||||
<LearningIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Learning" />
|
||||
{externalLinkIcon}
|
||||
</NavItem>
|
||||
</li>
|
||||
<li>
|
||||
<NavItem href={EXTERNAL_LINKS.docs}>
|
||||
<ListItemIcon>
|
||||
<HelpIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary="Help" />
|
||||
</NavItem>
|
||||
</li>
|
||||
</List>
|
||||
</nav>
|
||||
</Drawer>
|
||||
|
||||
{isPermanent && (
|
||||
<Box
|
||||
sx={{
|
||||
flexShrink: 0,
|
||||
flexGrow: 0,
|
||||
width: open ? NAV_DRAWER_WIDTH : NAV_DRAWER_COLLAPSED_WIDTH,
|
||||
transitionProperty: "width",
|
||||
transitionTimingFunction: "var(--nav-transition-timing-function)",
|
||||
transitionDuration: "var(--nav-transition-duration)",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,29 +1,35 @@
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import { MenuItem, MenuItemProps } from "@mui/material";
|
||||
import { spreadSx } from "@src/utils/ui";
|
||||
|
||||
export default function NavItem(props: MenuItemProps<typeof Link>) {
|
||||
const linkProps = { target: "_blank", rel: "noopener noreferrer" };
|
||||
|
||||
export default function NavItem(props: MenuItemProps<typeof Link | "a">) {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
component={Link}
|
||||
selected={pathname === props.to}
|
||||
component={"to" in props ? Link : "a"}
|
||||
selected={"to" in props ? pathname === props.to : false}
|
||||
{...props}
|
||||
sx={{
|
||||
"& .MuiListItemText-primary": {
|
||||
typography: "button",
|
||||
color: "text.secondary",
|
||||
},
|
||||
"& .MuiListItemIcon-root": { opacity: 0.87 },
|
||||
{...("href" in props ? linkProps : {})}
|
||||
sx={[
|
||||
{
|
||||
overflow: "hidden",
|
||||
|
||||
"&:hover, &.Mui-selected": {
|
||||
"& .MuiListItemText-primary": { color: "text.primary" },
|
||||
"& .MuiSvgIcon-root": { color: "text.primary" },
|
||||
},
|
||||
"& .MuiListItemText-primary": {
|
||||
typography: "button",
|
||||
color: "text.secondary",
|
||||
},
|
||||
"& .MuiListItemIcon-root": { opacity: 0.87 },
|
||||
|
||||
...props.sx,
|
||||
"&&::before": { left: "auto", right: 0 },
|
||||
}}
|
||||
"&:hover, &.Mui-selected": {
|
||||
"& .MuiListItemText-primary": { color: "text.primary" },
|
||||
"& .MuiSvgIcon-root": { color: "text.primary" },
|
||||
},
|
||||
},
|
||||
...spreadSx(props.sx),
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { useState } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
|
||||
import { List, ListItemIcon, ListItemText, Collapse } from "@mui/material";
|
||||
import {
|
||||
List,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
ListItemSecondaryAction,
|
||||
Collapse,
|
||||
} from "@mui/material";
|
||||
import FolderIcon from "@mui/icons-material/FolderOutlined";
|
||||
import FavoriteIcon from "@mui/icons-material/FavoriteBorder";
|
||||
import { ChevronDown } from "@src/assets/icons";
|
||||
@@ -17,12 +23,14 @@ export interface INavTableSectionProps {
|
||||
section: string;
|
||||
tables: TableSettings[];
|
||||
closeDrawer?: (e: {}) => void;
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
export default function NavTableSection({
|
||||
section,
|
||||
tables,
|
||||
closeDrawer,
|
||||
hidden,
|
||||
}: INavTableSectionProps) {
|
||||
const { pathname } = useLocation();
|
||||
const hasMatch = tables.map(getTableRoute).includes(pathname);
|
||||
@@ -31,7 +39,14 @@ export default function NavTableSection({
|
||||
const isFavorites = section === "Favorites";
|
||||
|
||||
return (
|
||||
<li>
|
||||
<li
|
||||
style={{
|
||||
opacity: hidden ? 0.38 : 1,
|
||||
transitionProperty: "opacity",
|
||||
transitionTimingFunction: "var(--nav-transition-timing-function)",
|
||||
transitionDuration: "var(--nav-transition-duration)",
|
||||
}}
|
||||
>
|
||||
<NavItem
|
||||
{...({ component: "button" } as any)}
|
||||
selected={!isFavorites && hasMatch && !open}
|
||||
@@ -43,14 +58,17 @@ export default function NavTableSection({
|
||||
|
||||
<ListItemText primary={section} style={{ textAlign: "left" }} />
|
||||
|
||||
<ChevronDown
|
||||
sx={{
|
||||
color: "action.active",
|
||||
mr: -0.5,
|
||||
transform: open ? "rotate(180deg)" : "rotate(0)",
|
||||
transition: (theme) => theme.transitions.create("transform"),
|
||||
}}
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<ChevronDown
|
||||
sx={{
|
||||
color: "action.active",
|
||||
m: 0.25,
|
||||
display: "block",
|
||||
transform: open ? "rotate(180deg)" : "rotate(0)",
|
||||
transition: (theme) => theme.transitions.create("transform"),
|
||||
}}
|
||||
/>
|
||||
</ListItemSecondaryAction>
|
||||
</NavItem>
|
||||
|
||||
<Collapse in={open}>
|
||||
|
||||
@@ -3,51 +3,28 @@ import { useAtom } from "jotai";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { useLocation, Outlet } from "react-router-dom";
|
||||
|
||||
import {
|
||||
useScrollTrigger,
|
||||
useMediaQuery,
|
||||
Stack,
|
||||
AppBar,
|
||||
Toolbar,
|
||||
IconButton,
|
||||
Box,
|
||||
Typography,
|
||||
Grow,
|
||||
} from "@mui/material";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import { useMediaQuery, Stack, GlobalStyles } from "@mui/material";
|
||||
|
||||
import NavDrawer, { NAV_DRAWER_WIDTH } from "./NavDrawer";
|
||||
import HelpMenu from "./HelpMenu";
|
||||
import UserMenu from "./UserMenu";
|
||||
import TopBar, { TOP_BAR_HEIGHT } from "./TopBar";
|
||||
import NavDrawer from "./NavDrawer";
|
||||
import ErrorFallback, {
|
||||
IErrorFallbackProps,
|
||||
} from "@src/components/ErrorFallback";
|
||||
import Loading from "@src/components/Loading";
|
||||
import UpdateCheckBadge from "./UpdateCheckBadge";
|
||||
|
||||
import {
|
||||
globalScope,
|
||||
projectIdAtom,
|
||||
userRolesAtom,
|
||||
navOpenAtom,
|
||||
navPinnedAtom,
|
||||
} from "@src/atoms/globalScope";
|
||||
import { ROUTE_TITLES } from "@src/constants/routes";
|
||||
import { useDocumentTitle } from "@src/hooks/useDocumentTitle";
|
||||
|
||||
export const APP_BAR_HEIGHT = 56;
|
||||
const StyledErrorFallback = (props: IErrorFallbackProps) => (
|
||||
<ErrorFallback {...props} style={{ marginTop: -APP_BAR_HEIGHT }} />
|
||||
);
|
||||
|
||||
export default function Navigation({ children }: React.PropsWithChildren<{}>) {
|
||||
const [projectId] = useAtom(projectIdAtom, globalScope);
|
||||
const [userRoles] = useAtom(userRolesAtom, globalScope);
|
||||
|
||||
const [open, setOpen] = useAtom(navOpenAtom, globalScope);
|
||||
const [pinned, setPinned] = useAtom(navPinnedAtom, globalScope);
|
||||
const trigger = useScrollTrigger({ disableHysteresis: true, threshold: 0 });
|
||||
const canPin = !useMediaQuery((theme: any) => theme.breakpoints.down("lg"));
|
||||
const isPermanent = useMediaQuery((theme: any) => theme.breakpoints.up("md"));
|
||||
|
||||
const { pathname } = useLocation();
|
||||
const basePath = ("/" + pathname.split("/")[1]) as keyof typeof ROUTE_TITLES;
|
||||
@@ -60,165 +37,52 @@ export default function Navigation({ children }: React.PropsWithChildren<{}>) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppBar
|
||||
position="sticky"
|
||||
color="inherit"
|
||||
elevation={trigger ? 1 : 0}
|
||||
sx={{
|
||||
height: APP_BAR_HEIGHT,
|
||||
backgroundImage:
|
||||
"linear-gradient(rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.09))", // Elevation 8
|
||||
|
||||
"&::before": {
|
||||
content: "''",
|
||||
display: "block",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
|
||||
bgcolor: "background.default",
|
||||
opacity: trigger ? 0 : 1,
|
||||
transition: (theme) =>
|
||||
theme.transitions.create("opacity", {
|
||||
easing:
|
||||
canPin && pinned
|
||||
? theme.transitions.easing.easeOut
|
||||
: theme.transitions.easing.sharp,
|
||||
duration:
|
||||
canPin && pinned
|
||||
? theme.transitions.duration.enteringScreen
|
||||
: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
},
|
||||
|
||||
ml: canPin && pinned && open ? `${NAV_DRAWER_WIDTH}px` : 0,
|
||||
width:
|
||||
canPin && pinned && open
|
||||
? `calc(100% - ${NAV_DRAWER_WIDTH}px)`
|
||||
: "100%",
|
||||
transition: (theme) =>
|
||||
theme.transitions.create(["margin-left", "width", "box-shadow"], {
|
||||
easing:
|
||||
canPin && pinned
|
||||
? theme.transitions.easing.easeOut
|
||||
: theme.transitions.easing.sharp,
|
||||
duration:
|
||||
canPin && pinned
|
||||
? theme.transitions.duration.enteringScreen
|
||||
: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<Toolbar
|
||||
sx={{
|
||||
height: APP_BAR_HEIGHT,
|
||||
minWidth: 0,
|
||||
maxWidth: "none",
|
||||
"&&": {
|
||||
minHeight: APP_BAR_HEIGHT,
|
||||
p: 0,
|
||||
pl: (theme) =>
|
||||
`max(env(safe-area-inset-left), ${theme.spacing(2)})`,
|
||||
pr: (theme) =>
|
||||
`max(env(safe-area-inset-right), ${theme.spacing(2)})`,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{!(open && canPin && pinned) && (
|
||||
<Grow in>
|
||||
<IconButton
|
||||
aria-label="Open navigation drawer"
|
||||
onClick={() => setOpen(true)}
|
||||
size="large"
|
||||
edge="start"
|
||||
>
|
||||
{userRoles.includes("ADMIN") ? (
|
||||
<UpdateCheckBadge>
|
||||
<MenuIcon />
|
||||
</UpdateCheckBadge>
|
||||
) : (
|
||||
<MenuIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</Grow>
|
||||
)}
|
||||
|
||||
<Grow
|
||||
in
|
||||
key={title}
|
||||
{...(typeof routeTitle !== "string"
|
||||
? routeTitle.titleTransitionProps
|
||||
: undefined)}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
flex: 1,
|
||||
overflowX: "auto",
|
||||
userSelect: "none",
|
||||
pl: open && canPin && pinned ? 48 / 8 : 0,
|
||||
pr: 1,
|
||||
ml: (routeTitle as any)?.leftAligned
|
||||
? 0
|
||||
: { xs: 0, sm: 1.5 + 6 + 1 },
|
||||
}}
|
||||
>
|
||||
{typeof routeTitle !== "string" ? (
|
||||
routeTitle.titleComponent(open, canPin && pinned)
|
||||
) : (
|
||||
<Typography
|
||||
variant="h6"
|
||||
component="h1"
|
||||
textAlign="center"
|
||||
noWrap
|
||||
sx={{
|
||||
typography: { sm: "h5" },
|
||||
textAlign: { xs: "left", sm: "center" },
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</Grow>
|
||||
|
||||
<HelpMenu />
|
||||
<UserMenu />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<TopBar
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
isPermanent={isPermanent}
|
||||
routeTitle={routeTitle}
|
||||
title={title}
|
||||
/>
|
||||
|
||||
<Stack direction="row">
|
||||
<NavDrawer
|
||||
open={open}
|
||||
pinned={canPin && pinned}
|
||||
setPinned={setPinned}
|
||||
canPin={canPin}
|
||||
isPermanent={isPermanent}
|
||||
onClose={() => setOpen(false)}
|
||||
scrollTrigger={trigger}
|
||||
/>
|
||||
|
||||
<ErrorBoundary FallbackComponent={StyledErrorFallback}>
|
||||
<Suspense
|
||||
fallback={
|
||||
<Loading fullScreen style={{ marginTop: -APP_BAR_HEIGHT }} />
|
||||
<Loading fullScreen style={{ marginTop: -TOP_BAR_HEIGHT }} />
|
||||
}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
maxWidth:
|
||||
canPin && pinned && open
|
||||
? `calc(100% - ${NAV_DRAWER_WIDTH}px)`
|
||||
: "100%",
|
||||
}}
|
||||
>
|
||||
<div style={{ flexGrow: 1, maxWidth: "100%" }}>
|
||||
<Outlet />
|
||||
{children}
|
||||
</div>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
</Stack>
|
||||
|
||||
<GlobalStyles
|
||||
styles={(theme) => ({
|
||||
":root": {
|
||||
"--nav-transition-timing-function": open
|
||||
? theme.transitions.easing.easeOut
|
||||
: theme.transitions.easing.sharp,
|
||||
"--nav-transition-duration":
|
||||
(open
|
||||
? theme.transitions.duration.enteringScreen
|
||||
: theme.transitions.duration.leavingScreen) + "ms",
|
||||
},
|
||||
})}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const StyledErrorFallback = (props: IErrorFallbackProps) => (
|
||||
<ErrorFallback {...props} style={{ marginTop: -TOP_BAR_HEIGHT }} />
|
||||
);
|
||||
|
||||
177
src/layouts/Navigation/TopBar.tsx
Normal file
177
src/layouts/Navigation/TopBar.tsx
Normal file
@@ -0,0 +1,177 @@
|
||||
import { useAtom } from "jotai";
|
||||
|
||||
import {
|
||||
useScrollTrigger,
|
||||
AppBar,
|
||||
Toolbar,
|
||||
IconButton,
|
||||
Box,
|
||||
Typography,
|
||||
Grow,
|
||||
Fade,
|
||||
} from "@mui/material";
|
||||
import MenuIcon from "@mui/icons-material/Menu";
|
||||
import MenuCloseIcon from "@mui/icons-material/MenuOpen";
|
||||
|
||||
import Logo from "@src/assets/Logo";
|
||||
import { NAV_DRAWER_WIDTH, NAV_DRAWER_COLLAPSED_WIDTH } from "./NavDrawer";
|
||||
import HelpMenu from "./HelpMenu";
|
||||
import UserMenu from "./UserMenu";
|
||||
import UpdateCheckBadge from "./UpdateCheckBadge";
|
||||
|
||||
import { globalScope, userRolesAtom } from "@src/atoms/globalScope";
|
||||
import { ROUTE_TITLES } from "@src/constants/routes";
|
||||
|
||||
export const TOP_BAR_HEIGHT = 56;
|
||||
|
||||
export interface ITopBarProps {
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
isPermanent: boolean;
|
||||
routeTitle: typeof ROUTE_TITLES[keyof typeof ROUTE_TITLES];
|
||||
title: string;
|
||||
}
|
||||
|
||||
export default function TopBar({
|
||||
open,
|
||||
setOpen,
|
||||
isPermanent,
|
||||
routeTitle,
|
||||
title,
|
||||
}: ITopBarProps) {
|
||||
const [userRoles] = useAtom(userRolesAtom, globalScope);
|
||||
|
||||
const trigger = useScrollTrigger({ disableHysteresis: true, threshold: 0 });
|
||||
const menuIcon = open ? <MenuCloseIcon /> : <MenuIcon />;
|
||||
|
||||
return (
|
||||
<AppBar
|
||||
position="sticky"
|
||||
color="inherit"
|
||||
elevation={trigger ? 1 : 0}
|
||||
sx={{
|
||||
height: TOP_BAR_HEIGHT,
|
||||
backgroundImage:
|
||||
"linear-gradient(rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.09))", // Elevation 8
|
||||
|
||||
"&::before": {
|
||||
content: "''",
|
||||
display: "block",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
|
||||
bgcolor: "background.default",
|
||||
opacity: trigger ? 0 : 1,
|
||||
|
||||
transitionProperty: "opacity",
|
||||
transitionTimingFunction: "var(--nav-transition-timing-function)",
|
||||
transitionDuration: "var(--nav-transition-duration)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Toolbar
|
||||
sx={{
|
||||
height: TOP_BAR_HEIGHT,
|
||||
minWidth: 0,
|
||||
maxWidth: "none",
|
||||
"&&": {
|
||||
minHeight: TOP_BAR_HEIGHT,
|
||||
p: 0,
|
||||
pl: (theme) =>
|
||||
`max(env(safe-area-inset-left), ${theme.spacing(0.5)})`,
|
||||
pr: (theme) =>
|
||||
`max(env(safe-area-inset-right), ${theme.spacing(0.5)})`,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Grow in>
|
||||
<IconButton
|
||||
aria-label={`${open ? "Close" : "Open"} navigation drawer`}
|
||||
onClick={() => setOpen(!open)}
|
||||
size="large"
|
||||
>
|
||||
{userRoles.includes("ADMIN") ? (
|
||||
<UpdateCheckBadge>{menuIcon}</UpdateCheckBadge>
|
||||
) : (
|
||||
menuIcon
|
||||
)}
|
||||
</IconButton>
|
||||
</Grow>
|
||||
|
||||
{isPermanent && (
|
||||
<Box
|
||||
sx={{
|
||||
width: open ? NAV_DRAWER_WIDTH : NAV_DRAWER_COLLAPSED_WIDTH,
|
||||
height: 2,
|
||||
|
||||
transform: `scale(${open ? 1 : 0})`,
|
||||
transformOrigin: "0% 50%",
|
||||
opacity: open ? 1 : 0,
|
||||
|
||||
transitionProperty: "width, opacity, transform",
|
||||
transitionTimingFunction: "var(--nav-transition-timing-function)",
|
||||
transitionDuration: "var(--nav-transition-duration)",
|
||||
|
||||
display: "flex",
|
||||
justifyContent: "flex-start",
|
||||
alignItems: "center",
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
>
|
||||
<Logo />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Fade
|
||||
in
|
||||
key={title}
|
||||
{...(typeof routeTitle !== "string"
|
||||
? routeTitle.titleTransitionProps
|
||||
: undefined)}
|
||||
>
|
||||
<Box
|
||||
sx={[
|
||||
{
|
||||
flex: 1,
|
||||
overflowX: "auto",
|
||||
userSelect: "none",
|
||||
ml: 1,
|
||||
},
|
||||
!(routeTitle as any)?.leftAligned && {
|
||||
ml: { xs: 1, sm: 6 },
|
||||
},
|
||||
isPermanent &&
|
||||
(routeTitle as any)?.leftAligned && {
|
||||
ml: -NAV_DRAWER_COLLAPSED_WIDTH / 8 + 0.5 + 2,
|
||||
},
|
||||
]}
|
||||
>
|
||||
{typeof routeTitle !== "string" ? (
|
||||
routeTitle.titleComponent(open, isPermanent)
|
||||
) : (
|
||||
<Typography
|
||||
variant="h6"
|
||||
component="h1"
|
||||
noWrap
|
||||
sx={{
|
||||
flexShrink: 0,
|
||||
flexGrow: 1,
|
||||
typography: { sm: "h5" },
|
||||
textAlign: { xs: "left", sm: "center" },
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</Fade>
|
||||
|
||||
<HelpMenu />
|
||||
<UserMenu />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
);
|
||||
}
|
||||
@@ -66,7 +66,6 @@ export default function UserMenu(props: IconButtonProps) {
|
||||
aria-label="Open user menu"
|
||||
aria-controls="user-menu"
|
||||
aria-haspopup="true"
|
||||
edge="end"
|
||||
size="large"
|
||||
{...props}
|
||||
ref={anchorEl}
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
import { TableSettings } from "@src/types/table";
|
||||
import { ROUTES } from "@src/constants/routes";
|
||||
import useBasicSearch from "@src/hooks/useBasicSearch";
|
||||
import { APP_BAR_HEIGHT } from "@src/layouts/Navigation";
|
||||
import { TOP_BAR_HEIGHT } from "@src/layouts/Navigation/TopBar";
|
||||
|
||||
const SEARCH_KEYS = ["id", "name", "section", "description"];
|
||||
|
||||
@@ -112,7 +112,7 @@ export default function HomePage() {
|
||||
message="No tables"
|
||||
description="There are no tables in this project. Sign in with an ADMIN account to create tables."
|
||||
fullScreen
|
||||
style={{ marginTop: -APP_BAR_HEIGHT }}
|
||||
style={{ marginTop: -TOP_BAR_HEIGHT }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import { Go as GoIcon } from "@src/assets/icons";
|
||||
import HomeIcon from "@mui/icons-material/HomeOutlined";
|
||||
|
||||
import AuthLayout from "@src/layouts/AuthLayout";
|
||||
import Navigation, { APP_BAR_HEIGHT } from "@src/layouts/Navigation";
|
||||
import Navigation from "@src/layouts/Navigation";
|
||||
import { TOP_BAR_HEIGHT } from "@src/layouts/Navigation/TopBar";
|
||||
import EmptyState from "@src/components/EmptyState";
|
||||
|
||||
import meta from "@root/package.json";
|
||||
@@ -33,7 +34,7 @@ export default function NotFound() {
|
||||
Home
|
||||
</Button>
|
||||
}
|
||||
style={{ marginTop: -APP_BAR_HEIGHT }}
|
||||
style={{ marginTop: -TOP_BAR_HEIGHT }}
|
||||
/>
|
||||
</Navigation>
|
||||
);
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
tableSchemaAtom,
|
||||
} from "@src/atoms/tableScope";
|
||||
import { ROUTES } from "@src/constants/routes";
|
||||
import { APP_BAR_HEIGHT } from "@src/layouts/Navigation";
|
||||
import { TOP_BAR_HEIGHT } from "@src/layouts/Navigation/TopBar";
|
||||
import { TABLE_TOOLBAR_HEIGHT } from "@src/components/TableToolbar";
|
||||
|
||||
/**
|
||||
@@ -92,15 +92,15 @@ export default function ProvidedSubTablePage() {
|
||||
backgroundImage: "none",
|
||||
},
|
||||
"& .modal-title-row": {
|
||||
height: APP_BAR_HEIGHT,
|
||||
height: TOP_BAR_HEIGHT,
|
||||
"& .MuiDialogTitle-root": {
|
||||
px: 2,
|
||||
py: (APP_BAR_HEIGHT - 28) / 2 / 8,
|
||||
py: (TOP_BAR_HEIGHT - 28) / 2 / 8,
|
||||
},
|
||||
"& .dialog-close": { m: (APP_BAR_HEIGHT - 40) / 2 / 8, ml: -1 },
|
||||
"& .dialog-close": { m: (TOP_BAR_HEIGHT - 40) / 2 / 8, ml: -1 },
|
||||
},
|
||||
"& .table-container": {
|
||||
height: `calc(100vh - ${APP_BAR_HEIGHT}px - ${TABLE_TOOLBAR_HEIGHT}px - 16px)`,
|
||||
height: `calc(100vh - ${TOP_BAR_HEIGHT}px - ${TABLE_TOOLBAR_HEIGHT}px - 16px)`,
|
||||
},
|
||||
}}
|
||||
ScrollableDialogContentProps={{
|
||||
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
import { TableSettings } from "@src/types/table";
|
||||
import { ROUTES } from "@src/constants/routes";
|
||||
import useBasicSearch from "@src/hooks/useBasicSearch";
|
||||
import { APP_BAR_HEIGHT } from "@src/layouts/Navigation";
|
||||
import { TOP_BAR_HEIGHT } from "@src/layouts/Navigation/TopBar";
|
||||
|
||||
const SEARCH_KEYS = ["id", "name", "section", "description"];
|
||||
|
||||
@@ -112,7 +112,7 @@ export default function HomePage() {
|
||||
message="No tables"
|
||||
description="There are no tables in this project. Sign in with an ADMIN account to create tables."
|
||||
fullScreen
|
||||
style={{ marginTop: -APP_BAR_HEIGHT }}
|
||||
style={{ marginTop: -TOP_BAR_HEIGHT }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -165,7 +165,10 @@ export default function HomePage() {
|
||||
label="Search tables"
|
||||
onChange={(e) => handleQuery(e.target.value)}
|
||||
paperSx={{
|
||||
maxWidth: (theme) => ({ md: theme.breakpoints.values.sm - 48 }),
|
||||
maxWidth: (theme) => ({
|
||||
md: theme.breakpoints.values.sm - 48 * 4,
|
||||
lg: theme.breakpoints.values.sm - 48,
|
||||
}),
|
||||
mb: { xs: 2, md: -6 },
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user