Drawer-style sheet based on dialog primitives
Edit profile
Make changes to your profile here. Click save when you're done.
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
...import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@/components/ui/sheet'
export default function SheetDemoExample() {
return (
<Sheet>
<SheetTrigger asChild>
<Button variant="outline">Open</Button>
</SheetTrigger>
<SheetContent>
<SheetClose class="absolute right-4 top-4" aria-label="Close">×</SheetClose>
<SheetHeader>
<SheetTitle>Edit profile</SheetTitle>
<SheetDescription>Make changes to your profile here. Click save when you're done.</SheetDescription>
</SheetHeader>
<div class="grid flex-1 gap-4 py-4">
<div class="grid gap-2">
<Label for="sheet-demo-name">Name</Label>
<Input id="sheet-demo-name" value="Pedro Duarte" />
</div>
<div class="grid gap-2">
<Label for="sheet-demo-username">Username</Label>
<Input id="sheet-demo-username" value="@peduarte" />
</div>
</div>
<SheetFooter>
<Button type="submit">Save changes</Button>
<SheetClose asChild><Button variant="outline">Close</Button></SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
)
}
Installation
npx @fictjs/shadcn@latest add sheetInstall the runtime dependencies:
npm install @fictjs/radix-ui class-variance-authorityCopy the Fict shadcn component into your project.
Source
components/ui/sheet.tsx
src/components/ui/sheet.tsx
import { Dialog as DialogPrimitive } from '@fictjs/radix-ui'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/cn'
const sheetVariants = cva(
'fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
{
variants: {
side: {
top: 'inset-x-0 top-0 border-b',
bottom: 'inset-x-0 bottom-0 border-t',
left: 'inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm',
right: 'inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm',
},
},
defaultVariants: {
side: 'right',
},
},
)
export const Sheet = DialogPrimitive.Root
export const SheetTrigger = DialogPrimitive.Trigger
export const SheetClose = DialogPrimitive.Close
type GenericProps = {
class?: string
children?: unknown
[key: string]: unknown
}
type SheetContentProps = GenericProps & VariantProps<typeof sheetVariants>
export function SheetOverlay(props: GenericProps) {
const { class: className, ...rest } = props
return <DialogPrimitive.Overlay class={cn('fixed inset-0 z-50 bg-black/80', className)} {...rest} />
}
export function SheetContent(props: SheetContentProps) {
const { class: className, children, side, ...rest } = props
return (
<>
<SheetOverlay />
<DialogPrimitive.Content class={cn(sheetVariants({ side }), className)} {...rest}>
{children}
</DialogPrimitive.Content>
</>
)
}
export function SheetHeader(props: GenericProps) {
const { class: className, ...rest } = props
return <div class={cn('flex flex-col space-y-2 text-center sm:text-left', className)} {...rest} />
}
export function SheetFooter(props: GenericProps) {
const { class: className, ...rest } = props
return <div class={cn('mt-auto flex flex-col gap-2 sm:flex-row sm:justify-end', className)} {...rest} />
}
export function SheetTitle(props: GenericProps) {
const { class: className, ...rest } = props
return <DialogPrimitive.Title class={cn('text-lg font-semibold text-foreground', className)} {...rest} />
}
export function SheetDescription(props: GenericProps) {
const { class: className, ...rest } = props
return <DialogPrimitive.Description class={cn('text-sm text-muted-foreground', className)} {...rest} />
}
Usage
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet"Examples
Side
Edit profile
Make changes to your profile here. Click save when you're done.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Edit profile
Make changes to your profile here. Click save when you're done.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Edit profile
Make changes to your profile here. Click save when you're done.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Edit profile
Make changes to your profile here. Click save when you're done.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
import { Button } from '@/components/ui/button'
import {
...import { Button } from '@/components/ui/button'
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@/components/ui/sheet'
const sides = ['top', 'right', 'bottom', 'left'] as const
export default function SheetSideExample() {
return (
<div class="flex flex-wrap gap-2">
{sides.map(side => (
<Sheet>
<SheetTrigger asChild><Button variant="outline" class="capitalize">{side}</Button></SheetTrigger>
<SheetContent side={side} class="data-[side=bottom]:max-h-[50vh] data-[side=top]:max-h-[50vh]">
<SheetClose class="absolute right-4 top-4" aria-label="Close">×</SheetClose>
<SheetHeader>
<SheetTitle>Edit profile {side}</SheetTitle>
<SheetDescription>Make changes to your profile here. Click save when you're done.</SheetDescription>
</SheetHeader>
<div class="overflow-y-auto px-4">
{Array.from({ length: 10 }, (_, index) => (
<p class="mb-2 leading-relaxed">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
))}
</div>
<SheetFooter>
<Button type="submit">Save changes</Button>
<SheetClose asChild><Button variant="outline">Cancel</Button></SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
))}
</div>
)
}
No Close Button
No Close Button
This sheet doesn't have a close button in the top-right corner. Click outside to close.
import { Button } from '@/components/ui/button'
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'
...import { Button } from '@/components/ui/button'
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'
export default function SheetNoCloseButtonExample() {
return (
<Sheet>
<SheetTrigger asChild><Button variant="outline">Open Sheet</Button></SheetTrigger>
<SheetContent>
<SheetHeader>
<SheetTitle>No Close Button</SheetTitle>
<SheetDescription>
This sheet doesn't have a close button in the top-right corner. Click outside to close.
</SheetDescription>
</SheetHeader>
</SheetContent>
</Sheet>
)
}
Rtl
تعديل الملف الشخصي
قم بإجراء تغييرات على ملفك الشخصي هنا. انقر حفظ عند الانتهاء.
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
...import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from '@/components/ui/sheet'
const translations = {
ar: { dir: 'rtl', open: 'فتح', title: 'تعديل الملف الشخصي', description: 'قم بإجراء تغييرات على ملفك الشخصي هنا. انقر حفظ عند الانتهاء.', name: 'الاسم', username: 'اسم المستخدم', save: 'حفظ التغييرات', close: 'إغلاق' },
he: { dir: 'rtl', open: 'פתח', title: 'עריכת פרופיל', description: 'בצע שינויים בפרופיל שלך כאן. לחץ שמור כשתסיים.', name: 'שם', username: 'שם משתמש', save: 'שמור שינויים', close: 'סגור' },
en: { dir: 'ltr', open: 'Open', title: 'Edit profile', description: "Make changes to your profile here. Click save when you're done.", name: 'Name', username: 'Username', save: 'Save changes', close: 'Close' },
} as const
export default function SheetRtlExample() {
let language = $state<keyof typeof translations>('ar')
const text = () => translations[language]
return (
<div class="grid gap-4">
<select value={language} onChange={event => { language = event.currentTarget.value as keyof typeof translations }}>
<option value="ar">Arabic (العربية)</option>
<option value="he">Hebrew (עברית)</option>
<option value="en">English</option>
</select>
<Sheet>
<SheetTrigger asChild><Button variant="outline">{text().open}</Button></SheetTrigger>
<SheetContent dir={text().dir} side={text().dir === 'rtl' ? 'left' : 'right'}>
<SheetClose class="absolute right-4 top-4" aria-label={text().close}>×</SheetClose>
<SheetHeader><SheetTitle>{text().title}</SheetTitle><SheetDescription>{text().description}</SheetDescription></SheetHeader>
<div class="grid gap-4 py-4">
<Label for="sheet-rtl-name">{text().name}</Label><Input id="sheet-rtl-name" value="Pedro Duarte" />
<Label for="sheet-rtl-username">{text().username}</Label><Input id="sheet-rtl-username" value="peduarte" />
</div>
<SheetFooter><Button type="submit">{text().save}</Button><SheetClose asChild><Button variant="outline">{text().close}</Button></SheetClose></SheetFooter>
</SheetContent>
</Sheet>
</div>
)
}