ente/web/packages/base/components/mui/SidebarDrawer.tsx
Neeraj Gupta 7621041ce0 Revert "[mob] Fix exif time parsing (#4985)"
This reverts commit 5b17711b55e9e8995454121e3a209ee5e8c9b3c1, reversing
changes made to 85bf3eebcb5ac197ce6a5901cbc8db1d03be0113.
2025-02-11 18:27:17 +05:30

175 lines
5.6 KiB
TypeScript

import { isDesktop } from "@/base/app";
import type { ModalVisibilityProps } from "@/base/components/utils/modal";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import CloseIcon from "@mui/icons-material/Close";
import {
Box,
Drawer,
IconButton,
Stack,
styled,
Typography,
type DrawerProps,
} from "@mui/material";
import React from "react";
/**
* A MUI {@link Drawer} with a standard set of styling that we use for our left
* and right sidebar panels.
*
* It is width limited to 375px, and always at full width. It also has a default
* padding.
*
* It also does some trickery with a sticky opaque bar to ensure that the
* content scrolls below our inline title bar on desktop.
*/
export const SidebarDrawer: React.FC<DrawerProps> = ({ children, ...rest }) => (
<Drawer
{...rest}
PaperProps={{
sx: {
maxWidth: "375px",
width: "100%",
scrollbarWidth: "thin",
// Need to increase specificity to override inherited padding.
"&&": { padding: 0 },
},
}}
>
{isDesktop && <AppTitlebarBackdrop />}
<Box sx={{ p: 1 }}>{children}</Box>
</Drawer>
);
/**
* When running on desktop, we adds a sticky opaque bar at the top of the
* sidebar with a z-index greater than the expected sidebar contents. This
* ensures that any title bar overlays added by the system (e.g. the traffic
* lights on macOS) have a opaque-ish background and the sidebar contents scroll
* underneath them.
*
* See: [Note: Customize the desktop title bar]
*/
const AppTitlebarBackdrop = styled("div")(({ theme }) => ({
position: "sticky",
top: 0,
left: 0,
width: "100%",
minHeight: "env(titlebar-area-height, 30px)",
zIndex: 1,
backgroundColor: theme.vars.palette.backdrop.muted,
backdropFilter: "blur(12px)",
}));
/**
* Common props for a {@link NestedSidebarDrawer} component. In addition to the
* regular modal visibility controls for opening and closing itself, these also
* surface an option to close the entire drawer.
*/
export type NestedSidebarDrawerVisibilityProps = ModalVisibilityProps & {
/**
* Called when the user wants to close the entire stack of drawers.
*
* Note that this does not automatically imply onClose. Each step in the
* nesting will have to chain their own onCloses to construct a new
* `onRootClose` suitable for passing to its children.
*/
onRootClose: () => void;
};
/**
* A variant of {@link SidebarDrawer} for second level, nested drawers that are
* shown atop an already visible {@link SidebarDrawer}.
*/
export const NestedSidebarDrawer: React.FC<
NestedSidebarDrawerVisibilityProps & DrawerProps
> = ({ onClose, onRootClose, ...rest }) => {
// Intercept backdrop taps and repurpose them to close the entire stack.
const handleClose: DrawerProps["onClose"] = (_, reason) => {
if (reason == "backdropClick") {
onClose();
onRootClose();
} else {
onClose();
}
};
// MUI doesn't (currently, AFAIK) have support for nested drawers, so we
// emulate that by showing a drawer atop another. To make it fit, we need to
// modify a few knobs.
return (
<SidebarDrawer
// Disable the transition (otherwise our nested drawer visibly
// slides in from the wrong direction).
transitionDuration={0}
// Make the backdrop transparent (otherwise we end up with two of
// them - one from the original drawer, and one from this nested
// one).
//
// Note that there is an easy way to hide the backdrop (using the
// `hideBackdrop` attribute), but that takes away our ability to
// intercept backdrop clicks to close it.
slotProps={{
backdrop: { sx: { "&&&": { backgroundColor: "transparent" } } },
}}
onClose={handleClose}
{...rest}
/>
);
};
type SidebarDrawerTitlebarProps = Pick<
NestedSidebarDrawerVisibilityProps,
"onClose" | "onRootClose"
> & {
/** Title for the drawer. */
title: string;
/** An optional secondary caption shown below the title. */
caption?: string;
/**
* An optional action button shown alongwith the close button at the
* trailing edge of the sidebar.
*/
actionButton?: React.ReactNode;
};
/**
* A bar with a title and back / close buttons, suitable for being used in
* tandem with a {@link NestedSidebarDrawer}.
*/
export const SidebarDrawerTitlebar: React.FC<SidebarDrawerTitlebarProps> = ({
title,
caption,
onClose,
onRootClose,
actionButton,
}) => (
<Stack sx={{ gap: "4px" }}>
<Stack direction="row" sx={{ justifyContent: "space-between" }}>
<IconButton onClick={onClose} color="primary">
<ArrowBackIcon />
</IconButton>
<Stack direction="row" sx={{ gap: "4px" }}>
{actionButton && actionButton}
<IconButton onClick={onRootClose} color="secondary">
<CloseIcon />
</IconButton>
</Stack>
</Stack>
<Box sx={{ px: "16px", py: "4px" }}>
<Typography variant="h3">{title}</Typography>
<Typography
variant="small"
sx={{
color: "text.muted",
wordBreak: "break-all",
minHeight: "17px",
}}
>
{caption}
</Typography>
</Box>
</Stack>
);