Search code examples
reactjswidgetdrawer

Shadecdn/ui Drawer component inside div


To have the drawer open only within a specific div in my chat application built with Shadecdn/ui, how can I achieve this? I want the drawer to open within a specific div, meaning I want it to open only within the chat screen, not the entire page.

I want it to open only within the chat screen, not the entire page.


Solution

    1. In components/ui/drawer.tsx, replace the fixed className in <DrawerOverlay /> and <DrawerContent /> by absolute.

    2. Don't render <DrawerPortal>. change the line 31, 44 from <DrawerPortal> to <>.

    3. When rendering the drawer give the parent <Chat /> relative className.

    export default function Page() {
      return (
        <div className="min-h-screen flex justify-center items-center">
          <div className="size-96 bg-sky-300 relative overflow-hidden">
            <Drawer>
              <DrawerTrigger>Trigger</DrawerTrigger>
              <DrawerContent>
                Content
              </DrawerContent>
            </Drawer>
          </div>
        </div>
      )
    }
    

    components/ui/drawer.tsx:

    'use client';
    
    import * as React from 'react';
    import { Drawer as DrawerPrimitive } from 'vaul';
    
    import { cn } from "@/lib/utils";
    
    const Drawer = ({ shouldScaleBackground = true, ...props }: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
      <DrawerPrimitive.Root shouldScaleBackground={shouldScaleBackground} {...props} />
    );
    Drawer.displayName = 'Drawer';
    
    const DrawerTrigger = DrawerPrimitive.Trigger;
    
    const DrawerPortal = DrawerPrimitive.Portal;
    
    const DrawerClose = DrawerPrimitive.Close;
    
    const DrawerOverlay = React.forwardRef<
      React.ElementRef<typeof DrawerPrimitive.Overlay>,
      React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
    >(({ className, ...props }, ref) => (
      <DrawerPrimitive.Overlay ref={ref} className={cn('absolute inset-0 z-50 bg-black/80', className)} {...props} />
    ));
    DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
    
    const DrawerContent = React.forwardRef<
      React.ElementRef<typeof DrawerPrimitive.Content>,
      React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
    >(({ className, children, ...props }, ref) => (
      <>
        <DrawerOverlay />
        <DrawerPrimitive.Content
          ref={ref}
          className={cn(
            'absolute inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background',
            className,
          )}
          {...props}
        >
          <div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
          {children}
        </DrawerPrimitive.Content>
      </>
    ));
    DrawerContent.displayName = 'DrawerContent';
    
    const DrawerHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
      <div className={cn('grid gap-1.5 p-4 text-center sm:text-left', className)} {...props} />
    );
    DrawerHeader.displayName = 'DrawerHeader';
    
    const DrawerFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
      <div className={cn('mt-auto flex flex-col gap-2 p-4', className)} {...props} />
    );
    DrawerFooter.displayName = 'DrawerFooter';
    
    const DrawerTitle = React.forwardRef<
      React.ElementRef<typeof DrawerPrimitive.Title>,
      React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
    >(({ className, ...props }, ref) => (
      <DrawerPrimitive.Title
        ref={ref}
        className={cn('text-lg font-semibold leading-none tracking-tight', className)}
        {...props}
      />
    ));
    DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
    
    const DrawerDescription = React.forwardRef<
      React.ElementRef<typeof DrawerPrimitive.Description>,
      React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
    >(({ className, ...props }, ref) => (
      <DrawerPrimitive.Description ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
    ));
    DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
    
    export {
      Drawer,
      DrawerPortal,
      DrawerOverlay,
      DrawerTrigger,
      DrawerClose,
      DrawerContent,
      DrawerHeader,
      DrawerFooter,
      DrawerTitle,
      DrawerDescription,
    };
    
    

    That's it. You might need to change some styles to fit with your use case.