Files
flxn-app/src/components/sheet/drawer.tsx
T
2026-08-23 17:17:04 -07:00

123 lines
3.3 KiB
TypeScript

import { Box, Container, Title, useComputedColorScheme } from "@mantine/core";
import { PropsWithChildren, useEffect, useRef } from "react";
import { Drawer as VaulDrawer } from "vaul";
import {
pushDimmedThemeColor,
popDimmedThemeColor,
} from "@/lib/mantine/theme-colors";
import { getTheme } from "@/themes/registry";
import { resolveChromeColors } from "@/themes/chrome";
import { useAuth } from "@/contexts/auth-context";
import styles from "./styles.module.css";
interface DrawerProps extends PropsWithChildren {
title?: string;
opened: boolean;
onChange: (next: boolean) => void;
onClose?: () => void;
onExited?: () => void;
}
const Drawer: React.FC<DrawerProps> = ({
title,
children,
opened,
onChange,
onExited,
}) => {
const colorScheme = useComputedColorScheme("light");
const { metadata } = useAuth();
const contentRef = useRef<HTMLDivElement>(null);
const themeId = metadata.theme;
useEffect(() => {
if (!opened) return;
const appElement = document.querySelector<HTMLElement>(".app");
const colors = resolveChromeColors(getTheme(themeId))[colorScheme];
appElement?.classList.add("drawer-scaling");
pushDimmedThemeColor(colors.dimmed);
return () => {
appElement?.classList.remove("drawer-scaling");
popDimmedThemeColor(colors.base);
};
}, [opened, colorScheme, themeId]);
useEffect(() => {
if (!opened || !contentRef.current) return;
const updateDrawerHeight = () => {
if (contentRef.current) {
const drawerContent = contentRef.current;
const visualViewport = window.visualViewport;
if (visualViewport) {
const availableHeight = visualViewport.height;
const maxDrawerHeight = Math.min(
availableHeight * 0.75,
window.innerHeight * 0.75
);
drawerContent.style.maxHeight = `${maxDrawerHeight}px`;
} else {
drawerContent.style.maxHeight = "75vh";
}
}
};
updateDrawerHeight();
if (window.visualViewport) {
window.visualViewport.addEventListener("resize", updateDrawerHeight);
}
return () => {
if (window.visualViewport) {
window.visualViewport.removeEventListener("resize", updateDrawerHeight);
}
};
}, [opened]);
return (
<VaulDrawer.Root
repositionInputs={false}
open={opened}
onOpenChange={onChange}
onAnimationEnd={(open) => {
if (!open) onExited?.();
}}
>
<VaulDrawer.Portal>
<VaulDrawer.Overlay className={styles.drawerOverlay} />
<VaulDrawer.Content
className={styles.drawerContent}
aria-describedby="drawer"
ref={contentRef}
>
<Container flex={1} p="md">
<Box
mb="sm"
bg="var(--mantine-color-gray-4)"
w="3rem"
h="0.375rem"
ml="auto"
mr="auto"
style={{ borderRadius: "9999px" }}
/>
<Container mx="auto" maw="28rem" px={0}>
<VaulDrawer.Title>
<Title order={2}>{title}</Title>
</VaulDrawer.Title>
{children}
</Container>
</Container>
</VaulDrawer.Content>
</VaulDrawer.Portal>
</VaulDrawer.Root>
);
};
export default Drawer;