"use client"; import * as React from "react"; import { Drawer as DrawerPrimitive } from "vaul"; import { cn } from "@/src/utils/tailwind"; import { useMediaQuery } from "react-responsive"; import { cva } from "class-variance-authority"; type DrawerProps = React.ComponentProps & { forceDirection?: "right" | "bottom" | "responsive"; /** * Whether to block text selection in the drawer. * Set to false to allow text selection (e.g., in comment sections). * @default false */ blockTextSelection?: boolean; }; type DrawerContentProps = React.ComponentPropsWithoutRef< typeof DrawerPrimitive.Content > & { overlayClassName?: string; size?: "default" | "md" | "lg" | "full"; position?: "top"; height?: "default" | "md"; blockTextSelection?: boolean; }; // https://tailwindcss.com/docs/responsive-design const TAILWIND_MD_MEDIA_QUERY = 768; const drawerVariants = cva( "fixed inset-x-0 z-50 flex h-auto flex-col rounded-t-lg border bg-background md:inset-x-auto md:right-0 md:mt-0 md:rounded-l-lg md:rounded-r-none ", { variants: { size: { default: "md:w-1/2 lg:w-2/5 xl:w-1/3 2xl:w-1/4", md: "w-3/5", lg: "w-2/3", full: "w-full", }, position: { top: "md:bottom-0 md:top-banner-offset md:max-h-screen-with-banner", }, height: { default: "h-1/3 md:h-full", md: "md:h-1/2", }, }, defaultVariants: { size: "default", position: "top", height: "default", }, }, ); const DrawerContext = React.createContext<{ blockTextSelection: boolean; }>({ blockTextSelection: false, }); const useDrawerContext = () => React.useContext(DrawerContext); const Drawer = ({ shouldScaleBackground = true, forceDirection = "responsive", blockTextSelection = false, ...props }: DrawerProps) => { const isMediumScreen = useMediaQuery({ query: `(min-width: ${TAILWIND_MD_MEDIA_QUERY}px)`, }); const direction = forceDirection === "responsive" ? isMediumScreen ? "right" : "bottom" : forceDirection; return ( ); }; Drawer.displayName = "Drawer"; const DrawerTrigger = DrawerPrimitive.Trigger; const DrawerPortal = DrawerPrimitive.Portal; const DrawerClose = DrawerPrimitive.Close; const DrawerOverlay = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( )); DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName; const DrawerContent = React.forwardRef< React.ElementRef, DrawerContentProps >( ( { className, children, overlayClassName, size, height, position, ...props }, ref, ) => { const { blockTextSelection } = useDrawerContext(); return ( {props.title ?? ""} {children} ); }, ); DrawerContent.displayName = "DrawerContent"; const DrawerHeader = ({ className, ...props }: React.HTMLAttributes) => (
); DrawerHeader.displayName = "DrawerHeader"; const DrawerFooter = ({ className, ...props }: React.HTMLAttributes) => (
); DrawerFooter.displayName = "DrawerFooter"; const DrawerTitle = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( )); DrawerTitle.displayName = DrawerPrimitive.Title.displayName; const DrawerDescription = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( )); DrawerDescription.displayName = DrawerPrimitive.Description.displayName; export { Drawer, DrawerPortal, DrawerOverlay, DrawerTrigger, DrawerClose, DrawerContent, DrawerHeader, DrawerFooter, DrawerTitle, DrawerDescription, };