mirror of
https://github.com/ente-io/ente.git
synced 2025-06-14 12:31:49 +00:00
This reverts commit 5b17711b55e9e8995454121e3a209ee5e8c9b3c1, reversing changes made to 85bf3eebcb5ac197ce6a5901cbc8db1d03be0113.
175 lines
5.6 KiB
TypeScript
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>
|
|
);
|