Drawer
A drawer component for React.
import Button from '@dinui/react/button'import Drawer from '@dinui/react/drawer'import { IconMinus, IconPlus } from '@tabler/icons-react'import * as React from 'react'import { Bar, BarChart, ResponsiveContainer } from 'recharts'
const data = [ { goal: 400, }, { goal: 300, }, { goal: 200, }, { goal: 300, }, { goal: 200, }, { goal: 278, }, { goal: 189, }, { goal: 239, }, { goal: 300, }, { goal: 200, }, { goal: 278, }, { goal: 189, }, { goal: 349, },]
export default function DrawerDemo() { const [goal, setGoal] = React.useState(350)
function onClick(adjustment: number) { setGoal(Math.max(200, Math.min(400, goal + adjustment))) }
return ( <Drawer> <Drawer.Trigger asChild> <Button variant="outline">Open Drawer</Button> </Drawer.Trigger> <Drawer.Content> <div className="mx-auto w-full max-w-sm"> <Drawer.Title>Move Goal</Drawer.Title> <Drawer.Description>Set your daily activity goal.</Drawer.Description> <div className="p-4 pb-0"> <div className="flex items-center justify-center space-x-2"> <Button variant="outline" icon className="h-8 w-8 shrink-0 rounded-full" onClick={() => onClick(-10)} disabled={goal <= 200} > <Button.Icon> <IconMinus /> </Button.Icon> <span className="sr-only">Decrease</span> </Button> <div className="flex-1 text-center"> <div className="text-7xl font-bold tracking-tighter">{goal}</div> <div className="text-[0.70rem] uppercase text-fg-weaker">Calories/day</div> </div> <Button variant="outline" icon className="h-8 w-8 shrink-0 rounded-full" onClick={() => onClick(10)} disabled={goal >= 400} > <Button.Icon> <IconPlus /> </Button.Icon> <span className="sr-only">Increase</span> </Button> </div> <div className="mt-3 h-[120px]"> <ResponsiveContainer width="100%" height="100%"> <BarChart data={data}> <Bar dataKey="goal" className="fill-fg-weak" /> </BarChart> </ResponsiveContainer> </div> </div> <Drawer.Actions> <Button>Submit</Button> <Drawer.Close asChild> <Button variant="outline">Cancel</Button> </Drawer.Close> </Drawer.Actions> </div> </Drawer.Content> </Drawer> )}About
Drawer is built on top of Vaul by emilkowalski_.
Responsive Dialog
You can combine the Dialog and Drawer components to create a responsive dialog. This renders a Dialog component on desktop and a Drawer on mobile.
import Button from '@dinui/react/button'import Dialog from '@dinui/react/dialog'import Drawer from '@dinui/react/drawer'import Input from '@dinui/react/input'import Label from '@dinui/react/label'import { cn } from '@dinui/react/utils'import { useMediaQuery } from '@web/hooks/use-media-query'import * as React from 'react'
export default function DrawerDialogDemo() { const [open, setOpen] = React.useState(false) const isDesktop = useMediaQuery('(min-width: 768px)')
if (isDesktop) { return ( <Dialog open={open} onOpenChange={setOpen}> <Dialog.Trigger asChild> <Button variant="outline">Edit Profile</Button> </Dialog.Trigger> <Dialog.Content className="sm:max-w-[425px]"> <Dialog.Title>Edit profile</Dialog.Title> <Dialog.Description> Make changes to your profile here. Click save when you're done. </Dialog.Description> <ProfileForm /> </Dialog.Content> </Dialog> ) }
return ( <Drawer open={open} onOpenChange={setOpen}> <Drawer.Trigger asChild> <Button variant="outline">Edit Profile</Button> </Drawer.Trigger> <Drawer.Content> <Drawer.Title>Edit profile</Drawer.Title> <Drawer.Description> Make changes to your profile here. Click save when you're done. </Drawer.Description> <ProfileForm /> <Drawer.Actions className="pt-2"> <Drawer.Close asChild> <Button variant="outline">Cancel</Button> </Drawer.Close> </Drawer.Actions> </Drawer.Content> </Drawer> )}
function ProfileForm({ className }: React.ComponentProps<'form'>) { return ( <form className={cn('grid items-start gap-4', className)}> <div className="grid gap-2"> <Label htmlFor="email">Email</Label> <Input type="email" id="email" defaultValue="dinwwwh@gmail.com" /> </div> <div className="grid gap-2"> <Label htmlFor="username">Username</Label> <Input id="username" defaultValue="@dinwwwh" /> </div> <Button type="submit">Save changes</Button> </form> )}Installation
-
Follow Installation Guide
To enable DinUI functionality in your project, you will need to properly set up Tailwind and install the necessary dependencies. -
All done
You now can start using this component in your project.
-
Follow Installation Guide
To enable DinUI functionality in your project, you will need to properly set up Tailwind and install the necessary dependencies. -
Run the following command in your project
Terminal window npx @dinui/cli@latest add drawerTerminal window yarn dlx @dinui/cli@latest add drawerTerminal window pnpm dlx @dinui/cli@latest add drawerTerminal window bunx @dinui/cli@latest add drawer -
Update the import paths to match your project setup
-
All done
You now can start using this component in your project.
-
Follow Installation Guide
To enable DinUI functionality in your project, you will need to properly set up Tailwind and install the necessary dependencies. -
Install dependencies
Terminal window npm install tailwind-variants type-fest vaulTerminal window yarn add tailwind-variants type-fest vaulTerminal window pnpm add tailwind-variants type-fest vaulTerminal window bun add tailwind-variants type-fest vaul -
Copy and paste the following code into your project
'use client'import type React from 'react'import { forwardRef } from 'react'import { tv } from 'tailwind-variants'import type { Merge } from 'type-fest'import { Drawer as DrawerPrimitive } from 'vaul'const drawer = tv({slots: {overlay: 'fixed inset-0 z-50 bg-[#000]/80',content: ['@container','fixed inset-x-0 bottom-0 z-50','p-4 mt-24 flex h-auto flex-col rounded-t-xl border bg-bg--contrast',],slidingHandle: 'mx-auto h-2 w-24 rounded-full bg-border/50',title: 'text-lg font-semibold leading-none tracking-tight text-center @xl:text-left',description: 'mt-1.5 text-sm text-fg-weaker text-center @xl:text-left',actions: 'mt-4 flex flex-col gap-2',},})function DrawerRoot(props: React.ComponentProps<typeof DrawerPrimitive.Root>) {return <DrawerPrimitive.Root shouldScaleBackground {...props} />}const DrawerContent = forwardRef<React.ElementRef<typeof DrawerPrimitive.Content>,Merge<React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>,{portalProps?: React.ComponentProps<typeof DrawerPrimitive.Portal>overlayProps?: React.ComponentProps<typeof DrawerPrimitive.Overlay>slidingHandleProps?: React.ComponentProps<'div'>}>>(({ portalProps, overlayProps, slidingHandleProps, children, ...props }, ref) => {const { overlay, content, slidingHandle } = drawer()return (<DrawerPrimitive.Portal {...portalProps}><DrawerPrimitive.Overlay{...overlayProps}className={overlay({ className: overlayProps?.className })}/><DrawerPrimitive.Content{...props}ref={ref}className={content({ className: props.className })}><div{...slidingHandleProps}className={slidingHandle({ className: slidingHandleProps?.className })}/>{children}</DrawerPrimitive.Content></DrawerPrimitive.Portal>)})DrawerContent.displayName = 'DrawerContent'const DrawerTitle = forwardRef<React.ElementRef<typeof DrawerPrimitive.Title>,React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>>((props, ref) => {const { title } = drawer()return (<DrawerPrimitive.Title {...props} ref={ref} className={title({ className: props.className })} />)})DrawerTitle.displayName = DrawerPrimitive.Title.displayNameconst DrawerDescription = forwardRef<React.ElementRef<typeof DrawerPrimitive.Description>,React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>>((props, ref) => {const { description } = drawer()return (<DrawerPrimitive.Description{...props}ref={ref}className={description({ className: props.className })}/>)})DrawerDescription.displayName = DrawerPrimitive.Description.displayNamefunction DrawerActions(props: React.ComponentProps<'div'>) {const { actions } = drawer()return <div {...props} className={actions({ className: props.className })} />}const Drawer = Object.assign(DrawerRoot, {Trigger: DrawerPrimitive.Trigger,Close: DrawerPrimitive.Close,Content: DrawerContent,Title: DrawerTitle,Description: DrawerDescription,Actions: DrawerActions,})export default Drawerexport { drawer, DrawerPrimitive } -
Update the import paths to match your project setup
-
All done
You now can start using this component in your project.