Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions apps/v4/content/docs/components/base/drawer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,25 @@ You can combine the `Dialog` and `Drawer` components to create a responsive dial

<ComponentPreview styleName="base-nova" name="drawer-dialog" />

### Non-modal Drawer

By default, the Drawer traps focus inside when open and blocks interaction with the rest of the page. To allow users to focus inputs or interact with elements outside the drawer while it remains open, set `modal={false}` on `<Drawer>` and `showOverlay={false}` on `<DrawerContent>`.

<ComponentPreview styleName="base-nova" name="drawer-non-modal" />

```tsx showLineNumbers
<Drawer modal={false}>
<DrawerTrigger asChild>
<Button variant="outline">Open</Button>
</DrawerTrigger>
<DrawerContent showOverlay={false}>
{/* ... */}
</DrawerContent>
</Drawer>
```

`modal={false}` disables vaul's focus trap so keyboard focus can leave the drawer freely. `showOverlay={false}` removes the backdrop so the rest of the page remains fully visible and clickable.

## RTL

To enable RTL support in shadcn/ui, see the [RTL configuration guide](/docs/rtl).
Expand Down
19 changes: 19 additions & 0 deletions apps/v4/content/docs/components/radix/drawer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,25 @@ You can combine the `Dialog` and `Drawer` components to create a responsive dial

<ComponentPreview styleName="radix-nova" name="drawer-dialog" />

### Non-modal Drawer

By default, the Drawer traps focus inside when open and blocks interaction with the rest of the page. To allow users to focus inputs or interact with elements outside the drawer while it remains open, set `modal={false}` on `<Drawer>` and `showOverlay={false}` on `<DrawerContent>`.

<ComponentPreview styleName="radix-nova" name="drawer-non-modal" />

```tsx showLineNumbers
<Drawer modal={false}>
<DrawerTrigger asChild>
<Button variant="outline">Open</Button>
</DrawerTrigger>
<DrawerContent showOverlay={false}>
{/* ... */}
</DrawerContent>
</Drawer>
```

`modal={false}` disables vaul's focus trap so keyboard focus can leave the drawer freely. `showOverlay={false}` removes the backdrop so the rest of the page remains fully visible and clickable.

## RTL

To enable RTL support in shadcn/ui, see the [RTL configuration guide](/docs/rtl).
Expand Down
26 changes: 26 additions & 0 deletions apps/v4/examples/__index__.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2217,6 +2217,19 @@ export const ExamplesIndex: Record<string, Record<string, any>> = {
return { default: mod.default || mod[exportName] }
}),
},
"drawer-non-modal": {
name: "drawer-non-modal",
filePath: "examples/radix/drawer-non-modal.tsx",
component: React.lazy(async () => {
const mod = await import("./radix/drawer-non-modal")
const exportName =
Object.keys(mod).find(
(key) =>
typeof mod[key] === "function" || typeof mod[key] === "object"
) || "drawer-non-modal"
return { default: mod.default || mod[exportName] }
}),
},
"drawer-rtl": {
name: "drawer-rtl",
filePath: "examples/radix/drawer-rtl.tsx",
Expand Down Expand Up @@ -7874,6 +7887,19 @@ export const ExamplesIndex: Record<string, Record<string, any>> = {
return { default: mod.default || mod[exportName] }
}),
},
"drawer-non-modal": {
name: "drawer-non-modal",
filePath: "examples/base/drawer-non-modal.tsx",
component: React.lazy(async () => {
const mod = await import("./base/drawer-non-modal")
const exportName =
Object.keys(mod).find(
(key) =>
typeof mod[key] === "function" || typeof mod[key] === "object"
) || "drawer-non-modal"
return { default: mod.default || mod[exportName] }
}),
},
"drawer-rtl": {
name: "drawer-rtl",
filePath: "examples/base/drawer-rtl.tsx",
Expand Down
55 changes: 55 additions & 0 deletions apps/v4/examples/base/drawer-non-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"use client"

import * as React from "react"

import { Button } from "@/styles/base-nova/ui/button"
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/styles/base-nova/ui/drawer"
import { Input } from "@/styles/base-nova/ui/input"
import { Label } from "@/styles/base-nova/ui/label"

export function DrawerNonModal() {
const [open, setOpen] = React.useState(false)

return (
<div className="flex w-full max-w-sm flex-col gap-4">
<div className="flex flex-col gap-2">
<Label htmlFor="outside-input">Outside input (focusable while drawer is open)</Label>
<Input
id="outside-input"
placeholder="Click here while the drawer is open..."
/>
</div>
<Drawer open={open} onOpenChange={setOpen} modal={false}>
<DrawerTrigger asChild>
<Button variant="outline">Open Non-modal Drawer</Button>
</DrawerTrigger>
<DrawerContent showOverlay={false}>
<DrawerHeader>
<DrawerTitle>Non-modal Drawer</DrawerTitle>
<DrawerDescription>
You can still interact with content outside this drawer.
</DrawerDescription>
</DrawerHeader>
<div className="flex flex-col gap-2 p-4">
<Label htmlFor="inside-input">Inside input</Label>
<Input id="inside-input" placeholder="Type here..." />
</div>
<DrawerFooter>
<DrawerClose asChild>
<Button variant="outline">Close</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
</div>
)
}
55 changes: 55 additions & 0 deletions apps/v4/examples/radix/drawer-non-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"use client"

import * as React from "react"

import { Button } from "@/styles/radix-nova/ui/button"
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/styles/radix-nova/ui/drawer"
import { Input } from "@/styles/radix-nova/ui/input"
import { Label } from "@/styles/radix-nova/ui/label"

export function DrawerNonModal() {
const [open, setOpen] = React.useState(false)

return (
<div className="flex w-full max-w-sm flex-col gap-4">
<div className="flex flex-col gap-2">
<Label htmlFor="outside-input">Outside input (focusable while drawer is open)</Label>
<Input
id="outside-input"
placeholder="Click here while the drawer is open..."
/>
</div>
<Drawer open={open} onOpenChange={setOpen} modal={false}>
<DrawerTrigger asChild>
<Button variant="outline">Open Non-modal Drawer</Button>
</DrawerTrigger>
<DrawerContent showOverlay={false}>
<DrawerHeader>
<DrawerTitle>Non-modal Drawer</DrawerTitle>
<DrawerDescription>
You can still interact with content outside this drawer.
</DrawerDescription>
</DrawerHeader>
<div className="flex flex-col gap-2 p-4">
<Label htmlFor="inside-input">Inside input</Label>
<Input id="inside-input" placeholder="Type here..." />
</div>
<DrawerFooter>
<DrawerClose asChild>
<Button variant="outline">Close</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
</div>
)
}
2 changes: 1 addition & 1 deletion apps/v4/public/r/styles/base-luma/drawer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"files": [
{
"path": "registry/base-luma/ui/drawer.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Drawer as DrawerPrimitive } from \"vaul\"\n\nimport { cn } from \"@/registry/base-luma/lib/utils\"\n\nfunction Drawer({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Root>) {\n return <DrawerPrimitive.Root data-slot=\"drawer\" {...props} />\n}\n\nfunction DrawerTrigger({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {\n return <DrawerPrimitive.Trigger data-slot=\"drawer-trigger\" {...props} />\n}\n\nfunction DrawerPortal({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Portal>) {\n return <DrawerPrimitive.Portal data-slot=\"drawer-portal\" {...props} />\n}\n\nfunction DrawerClose({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Close>) {\n return <DrawerPrimitive.Close data-slot=\"drawer-close\" {...props} />\n}\n\nfunction DrawerOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {\n return (\n <DrawerPrimitive.Overlay\n data-slot=\"drawer-overlay\"\n className={cn(\n \"fixed inset-0 z-50 bg-black/30 supports-backdrop-filter:backdrop-blur-sm data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DrawerContent({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Content>) {\n return (\n <DrawerPortal data-slot=\"drawer-portal\">\n <DrawerOverlay />\n <DrawerPrimitive.Content\n data-slot=\"drawer-content\"\n className={cn(\n \"group/drawer-content fixed z-50 flex h-auto flex-col bg-transparent p-4 text-sm before:absolute before:inset-2 before:-z-10 before:rounded-4xl before:border before:border-border before:bg-popover before:shadow-xl data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm\",\n className\n )}\n {...props}\n >\n <div className=\"mx-auto mt-4 hidden h-1.5 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block\" />\n {children}\n </DrawerPrimitive.Content>\n </DrawerPortal>\n )\n}\n\nfunction DrawerHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"drawer-header\"\n className={cn(\n \"flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DrawerFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"drawer-footer\"\n className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)}\n {...props}\n />\n )\n}\n\nfunction DrawerTitle({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Title>) {\n return (\n <DrawerPrimitive.Title\n data-slot=\"drawer-title\"\n className={cn(\n \"cn-font-heading text-base font-medium text-foreground\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DrawerDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Description>) {\n return (\n <DrawerPrimitive.Description\n data-slot=\"drawer-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Drawer,\n DrawerPortal,\n DrawerOverlay,\n DrawerTrigger,\n DrawerClose,\n DrawerContent,\n DrawerHeader,\n DrawerFooter,\n DrawerTitle,\n DrawerDescription,\n}\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Drawer as DrawerPrimitive } from \"vaul\"\n\nimport { cn } from \"@/registry/base-luma/lib/utils\"\n\nfunction Drawer({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Root>) {\n return <DrawerPrimitive.Root data-slot=\"drawer\" {...props} />\n}\n\nfunction DrawerTrigger({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {\n return <DrawerPrimitive.Trigger data-slot=\"drawer-trigger\" {...props} />\n}\n\nfunction DrawerPortal({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Portal>) {\n return <DrawerPrimitive.Portal data-slot=\"drawer-portal\" {...props} />\n}\n\nfunction DrawerClose({\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Close>) {\n return <DrawerPrimitive.Close data-slot=\"drawer-close\" {...props} />\n}\n\nfunction DrawerOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {\n return (\n <DrawerPrimitive.Overlay\n data-slot=\"drawer-overlay\"\n className={cn(\n \"fixed inset-0 z-50 bg-black/30 supports-backdrop-filter:backdrop-blur-sm data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DrawerContent({\n className,\n children,\n showOverlay = true,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Content> & {\n showOverlay?: boolean\n}) {\n return (\n <DrawerPortal data-slot=\"drawer-portal\">\n {showOverlay && <DrawerOverlay />}\n <DrawerPrimitive.Content\n data-slot=\"drawer-content\"\n className={cn(\n \"group/drawer-content fixed z-50 flex h-auto flex-col bg-transparent p-4 text-sm before:absolute before:inset-2 before:-z-10 before:rounded-4xl before:border before:border-border before:bg-popover before:shadow-xl data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm\",\n className\n )}\n {...props}\n >\n <div className=\"mx-auto mt-4 hidden h-1.5 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block\" />\n {children}\n </DrawerPrimitive.Content>\n </DrawerPortal>\n )\n}\n\nfunction DrawerHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"drawer-header\"\n className={cn(\n \"flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DrawerFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"drawer-footer\"\n className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)}\n {...props}\n />\n )\n}\n\nfunction DrawerTitle({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Title>) {\n return (\n <DrawerPrimitive.Title\n data-slot=\"drawer-title\"\n className={cn(\n \"cn-font-heading text-base font-medium text-foreground\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction DrawerDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Description>) {\n return (\n <DrawerPrimitive.Description\n data-slot=\"drawer-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Drawer,\n DrawerPortal,\n DrawerOverlay,\n DrawerTrigger,\n DrawerClose,\n DrawerContent,\n DrawerHeader,\n DrawerFooter,\n DrawerTitle,\n DrawerDescription,\n}\n",
"type": "registry:ui"
}
],
Expand Down
Loading
Loading