115 lines
3.2 KiB
TypeScript
115 lines
3.2 KiB
TypeScript
import { Box, Container, Flex, Loader, useComputedColorScheme } from "@mantine/core";
|
|
import { PropsWithChildren, Suspense, useEffect, useRef } from "react";
|
|
import { Drawer as VaulDrawer } from "vaul";
|
|
import styles from "./styles.module.css";
|
|
|
|
interface DrawerProps extends PropsWithChildren {
|
|
title?: string;
|
|
opened: boolean;
|
|
onChange: (next: boolean) => void;
|
|
}
|
|
|
|
const Drawer: React.FC<DrawerProps> = ({
|
|
title,
|
|
children,
|
|
opened,
|
|
onChange,
|
|
}) => {
|
|
const colorScheme = useComputedColorScheme("light");
|
|
const contentRef = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
const appElement = document.querySelector(".app") as HTMLElement;
|
|
|
|
if (!appElement) return;
|
|
|
|
let themeColorMeta = document.querySelector(
|
|
'meta[name="theme-color"]'
|
|
) as HTMLMetaElement;
|
|
if (!themeColorMeta) {
|
|
themeColorMeta = document.createElement("meta");
|
|
themeColorMeta.name = "theme-color";
|
|
document.head.appendChild(themeColorMeta);
|
|
}
|
|
|
|
const colors = {
|
|
light: {
|
|
normal: "rgb(255,255,255)",
|
|
overlay: "rgb(153,153,153)",
|
|
},
|
|
dark: {
|
|
normal: "rgb(36,36,36)",
|
|
overlay: "rgb(22,22,22)",
|
|
},
|
|
};
|
|
|
|
const currentColors = colors[colorScheme] || colors.light;
|
|
|
|
if (opened) {
|
|
appElement.classList.add("drawer-scaling");
|
|
themeColorMeta.content = currentColors.overlay;
|
|
} else {
|
|
appElement.classList.remove("drawer-scaling");
|
|
themeColorMeta.content = currentColors.normal;
|
|
}
|
|
|
|
return () => {
|
|
appElement.classList.remove("drawer-scaling");
|
|
themeColorMeta.content = currentColors.normal;
|
|
};
|
|
}, [opened, colorScheme]);
|
|
|
|
useEffect(() => {
|
|
if (!opened || !contentRef.current) return;
|
|
|
|
const resizeObserver = new ResizeObserver(() => {
|
|
if (contentRef.current) {
|
|
const drawerContent = contentRef.current.closest('[data-vaul-drawer-wrapper]');
|
|
if (drawerContent) {
|
|
(drawerContent as HTMLElement).style.height = 'auto';
|
|
(drawerContent as HTMLElement).offsetHeight;
|
|
}
|
|
}
|
|
});
|
|
|
|
resizeObserver.observe(contentRef.current);
|
|
|
|
return () => {
|
|
resizeObserver.disconnect();
|
|
};
|
|
}, [opened, children]);
|
|
|
|
return (
|
|
<VaulDrawer.Root open={opened} onOpenChange={onChange}>
|
|
<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}</VaulDrawer.Title>
|
|
<Suspense fallback={
|
|
<Flex justify='center' align='center' w='100%' h={400}>
|
|
<Loader size='lg' />
|
|
</Flex>
|
|
}>
|
|
{children}
|
|
</Suspense>
|
|
</Container>
|
|
</Container>
|
|
</VaulDrawer.Content>
|
|
</VaulDrawer.Portal>
|
|
</VaulDrawer.Root>
|
|
);
|
|
};
|
|
|
|
export default Drawer;
|