Search for a command to run...
A drawer component for SolidJS.
Drawer is built on top of Corvu Drawer by Jasmin.
npx shadcn@latest add @zaidan/drawerpnpx shadcn add @zaidan/draweryarn dlx shadcn@latest add @zaidan/drawerbunx shadcn@latest add @zaidan/drawerInstall the following dependencies:
bun add corvuCopy and paste the following code into your project.
1import {2 Close,3 type CloseProps,4 Content,5 type ContentProps,6 Description,7 type DescriptionProps,8 type DynamicProps,9 Label,10 type LabelProps,11 Overlay,12 type OverlayProps,13 Portal,14 Root,15 type RootProps,16 Trigger,17 type TriggerProps,18 useContext,19} from "@corvu/drawer";20import type { ComponentProps, ValidComponent } from "solid-js";21import { splitProps } from "solid-js";22import { cn } from "~/lib/utils";23
24type DrawerRootProps<T extends ValidComponent = "div"> = DynamicProps<T, RootProps>;25
26const DrawerRoot = <T extends ValidComponent = "div">(props: DrawerRootProps<T>) => {27 return <Root data-slot="drawer" {...(props as RootProps)} />;28};29
30type DrawerTriggerProps<T extends ValidComponent = "button"> = DynamicProps<T, TriggerProps>;31
32const DrawerTrigger = <T extends ValidComponent = "button">(props: DrawerTriggerProps<T>) => {33 return <Trigger data-slot="drawer-trigger" {...(props as TriggerProps)} />;34};35
36type DrawerCloseProps<T extends ValidComponent = "button"> = DynamicProps<T, CloseProps>;37
38const DrawerClose = <T extends ValidComponent = "button">(props: DrawerCloseProps<T>) => {39 return <Close data-slot="drawer-close" {...(props as CloseProps)} />;40};41
42type DrawerOverlayProps<T extends ValidComponent = "div"> = DynamicProps<T, OverlayProps> &43 Pick<ComponentProps<T>, "class">;44
45const DrawerOverlay = <T extends ValidComponent = "div">(props: DrawerOverlayProps<T>) => {46 const [local, others] = splitProps(props as DrawerOverlayProps, ["class"]);47 const context = useContext();48 return (49 <Overlay50 data-slot="drawer-overlay"51 class={cn("fixed inset-0 z-50 z-drawer-overlay", local.class)}52 {...others}53 style={{54 "background-color": `rgb(0 0 0 / ${0.1 * context.openPercentage()})`,55 "backdrop-filter": `blur(${4 * context.openPercentage()}px)`,56 }}57 />58 );59};60
61type DrawerContentProps<T extends ValidComponent = "div"> = DynamicProps<T, ContentProps> &62 Pick<ComponentProps<T>, "class" | "children">;63
64const DrawerContent = <T extends ValidComponent = "div">(props: DrawerContentProps<T>) => {65 const [local, others] = splitProps(props as DrawerContentProps, ["class", "children"]);66 return (67 <Portal data-slot="drawer-portal">68 <DrawerOverlay />69 <Content70 data-slot="drawer-content"71 class={cn("group/drawer-content fixed z-50 z-drawer-content", local.class)}72 {...others}73 >74 <div class="z-drawer-handle mx-auto hidden shrink-0 bg-muted group-data-[side=bottom]/drawer-content:block" />75 {local.children}76 </Content>77 </Portal>78 );79};80
81type DrawerHeaderProps = ComponentProps<"div">;82
83const DrawerHeader = (props: DrawerHeaderProps) => {84 const [local, others] = splitProps(props, ["class"]);85 return (86 <div87 data-slot="drawer-header"88 class={cn("z-drawer-header flex flex-col", local.class)}89 {...others}90 />91 );92};93
94type DrawerFooterProps = ComponentProps<"div">;95
96const DrawerFooter = (props: DrawerFooterProps) => {97 const [local, others] = splitProps(props, ["class"]);98 return (99 <div100 data-slot="drawer-footer"101 class={cn("z-drawer-footer mt-auto flex flex-col", local.class)}102 {...others}103 />104 );105};106
107type DrawerLabelProps<T extends ValidComponent = "h2"> = DynamicProps<T, LabelProps> &108 Pick<ComponentProps<T>, "class">;109
110const DrawerLabel = <T extends ValidComponent = "h2">(props: DrawerLabelProps<T>) => {111 const [local, others] = splitProps(props as DrawerLabelProps, ["class"]);112 return <Label data-slot="drawer-title" class={cn("z-drawer-title", local.class)} {...others} />;113};114
115type DrawerDescriptionProps<T extends ValidComponent = "p"> = DynamicProps<T, DescriptionProps> &116 Pick<ComponentProps<T>, "class">;117
118const DrawerDescription = <T extends ValidComponent = "p">(props: DrawerDescriptionProps<T>) => {119 const [local, others] = splitProps(props as DrawerDescriptionProps, ["class"]);120 return (121 <Description122 data-slot="drawer-description"123 class={cn("z-drawer-description", local.class)}124 {...others}125 />126 );127};128
129export {130 DrawerRoot as Drawer,131 DrawerOverlay,132 DrawerTrigger,133 DrawerClose,134 DrawerContent,135 DrawerHeader,136 DrawerFooter,137 DrawerLabel as DrawerTitle,138 DrawerDescription,139};Here are the source code of all the examples from the preview page:
import { Index } from "solid-js";import { Button } from "~/components/ui/button";import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger,} from "~/components/ui/drawer";function DrawerScrollableContent() { return ( <Example title="Scrollable Content"> <Drawer side="right"> <DrawerTrigger as={Button} variant="outline"> Scrollable Content </DrawerTrigger> <DrawerContent> <DrawerHeader> <DrawerTitle>Move Goal</DrawerTitle> <DrawerDescription>Set your daily activity goal.</DrawerDescription> </DrawerHeader> <div class="no-scrollbar overflow-y-auto px-4"> <Index each={Array.from({ length: 10 })}> {() => ( <p class="mb-4 style-lyra:mb-2 leading-normal style-lyra:leading-relaxed"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. </p> )} </Index> </div> <DrawerFooter> <Button>Submit</Button> <DrawerClose as={Button} variant="outline"> Cancel </DrawerClose> </DrawerFooter> </DrawerContent> </Drawer> </Example> );}import { For, Index } from "solid-js";import { Button } from "~/components/ui/button";import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger,} from "~/components/ui/drawer";function DrawerWithSides() { return ( <Example title="Sides"> <div class="flex flex-wrap gap-2"> <For each={DRAWER_SIDES}> {(side) => ( <Drawer side={side}> <DrawerTrigger as={Button} variant="outline" class="capitalize"> {side} </DrawerTrigger> <DrawerContent class="data-[side=bottom]:max-h-[50vh] data-[side=top]:max-h-[50vh]"> <DrawerHeader> <DrawerTitle>Move Goal</DrawerTitle> <DrawerDescription>Set your daily activity goal.</DrawerDescription> </DrawerHeader> <div class="no-scrollbar overflow-y-auto px-4"> <Index each={Array.from({ length: 10 })}> {() => ( <p class="mb-4 style-lyra:mb-2 leading-normal style-lyra:leading-relaxed"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. </p> )} </Index> </div> <DrawerFooter> <Button>Submit</Button> <DrawerClose as={Button} variant="outline"> Cancel </DrawerClose> </DrawerFooter> </DrawerContent> </Drawer> )} </For> </div> </Example> );}The Drawer component uses Corvu's Drawer primitive under the hood. Here are the main props:
| Prop | Type | Default | Description |
|---|---|---|---|
side | "top" | "right" | "bottom" | "left" | "bottom" | The side the drawer opens from |
open | boolean | - | The controlled open state |
defaultOpen | boolean | false | The default open state |
onOpenChange | (open: boolean) => void | - | Handler called when open state changes |
snapPoints | Size[] | [0, 1] | Positions to snap to |
activeSnapPoint | Size | - | The controlled active snap point |
allowSkippingSnapPoints | boolean | true | Whether to allow skipping snap points |
closeOnOutsidePointer | boolean | true | Whether to close on outside pointer |
closeOnEscapeKeyDown | boolean | true | Whether to close on escape key |