Responsive Sidebar
An animated dialog component powered by Framer Motion, offering smooth transitions and interactive visual effects for modal windows
An animated dialog component powered by Framer Motion, offering smooth transitions and interactive visual effects for modal windows
npm install vaul
'use client';import React, {createContext,useContext,useState,useEffect,ReactNode,} from 'react';import { X } from 'lucide-react';import { Drawer as VaulSidebar } from 'vaul';import { cn } from '@/lib/utils';interface DrawerContextProps {open: boolean;setOpen: (open: boolean) => void;}const DrawerContext = createContext<DrawerContextProps | undefined>(undefined);const useSidebarDrawer = () => {const context = useContext(DrawerContext);if (!context) {throw new Error('useDrawer must be used within a DrawerProvider');}return context;};interface DrawerSidebarProps {children: ReactNode;open?: boolean;setOpen?: (open: boolean) => void;direction?: 'left' | 'right';outsideClose?: boolean;className?: string;triggerClassName?: string;DefaultTrigger?: () => React.ReactNode; // Changed to a function that returns ReactNode}export function SidebarDrawer({children,open: controlledOpen,setOpen: controlledSetOpen,direction = 'left',outsideClose = true,className,triggerClassName,DefaultTrigger, // Now a function prop}: DrawerSidebarProps) {const [internalOpen, setInternalOpen] = useState(false);const open = controlledOpen !== undefined ? controlledOpen : internalOpen;const setOpen =controlledSetOpen !== undefined ? controlledSetOpen : setInternalOpen;const [isDesktop, setIsDesktop] = useState(false);useEffect(() => {const mediaQuery = window.matchMedia('(min-width: 768px)');const handleMediaChange = (event: MediaQueryListEvent) => {setIsDesktop(event.matches);};setIsDesktop(mediaQuery.matches);mediaQuery.addEventListener('change', handleMediaChange);return () => {mediaQuery.removeEventListener('change', handleMediaChange);};}, []);return (<DrawerContext.Provider value={{ open, setOpen }}><>{DefaultTrigger && (<div onClick={() => setOpen(true)}>{DefaultTrigger()}</div>)}<VaulSidebar.Rootopen={open}direction={direction === 'right' ? 'right' : 'left'}onOpenChange={setOpen}dismissible={isDesktop ? false : true}><VaulSidebar.Portal><VaulSidebar.OverlayclassName='fixed inset-0 dark:bg-black/40 bg-white/50 backdrop-blur-sm z-50 'onClick={() => setOpen(false)}/><VaulSidebar.ContentclassName={cn(` border-l z-50 ${outsideClose? 'sm:w-[450px] w-[90%] h-[100%] dark:bg-zinc-950 bg-zinc-100': `w-full h-[100%] `} fixed bottom-0 ${direction === 'right' ? 'right-0' : 'left-0'}`,className)}><divclassName={`${outsideClose? 'w-full h-full': 'dark:bg-gray-900 relative bg-white border-r sm:w-[450px] w-[90%] h-full'} `}>{isDesktop ? (<><buttonclassName='flex justify-end w-full absolute right-2 top-2'onClick={() => setOpen(false)}><X /></button></>) : (<><divclassName={`absolute top-[40%] ${direction === 'right' ? 'left-2' : 'right-2'} mx-auto h-16 w-[0.30rem] flex-shrink-0 rounded-full bg-gray-600 my-4`}/></>)}{children}</div></VaulSidebar.Content></VaulSidebar.Portal></VaulSidebar.Root></></DrawerContext.Provider>);}export function DrawerContent({ children }: { children: ReactNode }) {return <>{children}</>;}
you can use state or default button to control the dialog
<SidebarDrawerDefaultTrigger={() => {return (<buttonwhileTap={{ scale: 0.8 }}className='absolute right-2 bottom-2 p-4 dark:bg-black bg-white rounded-lg shadow-black'><Edit /></button>);}}direction={'right'}outsideClose={true}><DrawerContent></DrawerContent></SidebarDrawer>
Prop | Type | Description |
---|---|---|
open | boolean | The content to be displayed within the AuroraBackground component. |
setOpen | boolean | this is an function to close and open the drawer |
drawerBtn | function | this is an function for default button, when you don't to use state then you can use drawerBtn |