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 (113 <Label114 data-slot="drawer-title"115 class={cn("z-drawer-title z-font-heading", local.class)}116 {...others}117 />118 );119};120
121type DrawerDescriptionProps<T extends ValidComponent = "p"> = DynamicProps<T, DescriptionProps> &122 Pick<ComponentProps<T>, "class">;123
124const DrawerDescription = <T extends ValidComponent = "p">(props: DrawerDescriptionProps<T>) => {125 const [local, others] = splitProps(props as DrawerDescriptionProps, ["class"]);126 return (127 <Description128 data-slot="drawer-description"129 class={cn("z-drawer-description", local.class)}130 {...others}131 />132 );133};134
135export {136 DrawerClose,137 DrawerContent,138 DrawerDescription,139 DrawerFooter,140 DrawerHeader,141 DrawerLabel as DrawerTitle,142 DrawerOverlay,143 DrawerRoot as Drawer,144 DrawerTrigger,145};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 |