Docs
Drawer
Drawer
A drawer component for Svelte.
Loading...
<script lang="ts">
import { Button } from "$lib/components/ui/button";
import * as Drawer from "$lib/components/ui/drawer";
import { Minus, Plus } from "radix-icons-svelte";
import { VisXYContainer, VisGroupedBar } from "@unovis/svelte";
const data = [
{
id: 1,
goal: 400
},
{
id: 2,
goal: 300
},
{
id: 3,
goal: 200
},
{
id: 4,
goal: 300
},
{
id: 5,
goal: 200
},
{
id: 6,
goal: 278
},
{
id: 7,
goal: 189
},
{
id: 8,
goal: 239
},
{
id: 9,
goal: 300
},
{
id: 10,
goal: 200
},
{
id: 11,
goal: 278
},
{
id: 12,
goal: 189
},
{
id: 13,
goal: 349
}
];
const x = (d: { goal: number; id: number }) => d.id;
const y = (d: { goal: number; id: number }) => d.goal;
let goal = 350;
function handleClick(adjustment: number) {
goal = Math.max(200, Math.min(400, goal + adjustment));
}
</script>
<Drawer.Root>
<Drawer.Trigger asChild let:builder>
<Button builders={[builder]} variant="outline">Open Drawer</Button>
</Drawer.Trigger>
<Drawer.Content>
<div class="mx-auto w-full max-w-sm">
<Drawer.Header>
<Drawer.Title>Move Goal</Drawer.Title>
<Drawer.Description>Set your daily activity goal.</Drawer.Description>
</Drawer.Header>
<div class="p-4 pb-0">
<div class="flex items-center justify-center space-x-2">
<Button
variant="outline"
size="icon"
class="h-8 w-8 shrink-0 rounded-full"
on:click={() => handleClick(-10)}
disabled={goal <= 200}
>
<Minus class="h-4 w-4" />
<span class="sr-only">Decrease</span>
</Button>
<div class="flex-1 text-center">
<div class="text-7xl font-bold tracking-tighter">
{goal}
</div>
<div class="text-[0.70rem] uppercase text-muted-foreground">
Calories/day
</div>
</div>
<Button
variant="outline"
size="icon"
class="h-8 w-8 shrink-0 rounded-full"
on:click={() => handleClick(10)}
>
<Plus class="h-4 w-4" />
<span class="sr-only">Increase</span>
</Button>
</div>
<div class="mt-3 h-[120px]">
<VisXYContainer {data} height={60}>
<VisGroupedBar {x} {y} color={"hsl(var(--primary) / 0.2)"} />
</VisXYContainer>
</div>
</div>
<Drawer.Footer>
<Button>Submit</Button>
<Drawer.Close asChild let:builder>
<Button builders={[builder]} variant="outline">Cancel</Button>
</Drawer.Close>
</Drawer.Footer>
</div>
</Drawer.Content>
</Drawer.Root>
<script lang="ts">
import { Button } from "$lib/components/ui/button";
import * as Drawer from "$lib/components/ui/drawer";
import { Minus, Plus } from "lucide-svelte";
import { VisXYContainer, VisGroupedBar } from "@unovis/svelte";
const data = [
{
id: 1,
goal: 400
},
{
id: 2,
goal: 300
},
{
id: 3,
goal: 200
},
{
id: 4,
goal: 300
},
{
id: 5,
goal: 200
},
{
id: 6,
goal: 278
},
{
id: 7,
goal: 189
},
{
id: 8,
goal: 239
},
{
id: 9,
goal: 300
},
{
id: 10,
goal: 200
},
{
id: 11,
goal: 278
},
{
id: 12,
goal: 189
},
{
id: 13,
goal: 349
}
];
const x = (d: { goal: number; id: number }) => d.id;
const y = (d: { goal: number; id: number }) => d.goal;
let goal = 350;
function handleClick(adjustment: number) {
goal = Math.max(200, Math.min(400, goal + adjustment));
}
</script>
<Drawer.Root>
<Drawer.Trigger asChild let:builder>
<Button builders={[builder]} variant="outline">Open Drawer</Button>
</Drawer.Trigger>
<Drawer.Content>
<div class="mx-auto w-full max-w-sm">
<Drawer.Header>
<Drawer.Title>Move Goal</Drawer.Title>
<Drawer.Description>Set your daily activity goal.</Drawer.Description>
</Drawer.Header>
<div class="p-4 pb-0">
<div class="flex items-center justify-center space-x-2">
<Button
variant="outline"
size="icon"
class="h-8 w-8 shrink-0 rounded-full"
on:click={() => handleClick(-10)}
disabled={goal <= 200}
>
<Minus class="h-4 w-4" />
<span class="sr-only">Decrease</span>
</Button>
<div class="flex-1 text-center">
<div class="text-7xl font-bold tracking-tighter">
{goal}
</div>
<div class="text-[0.70rem] uppercase text-muted-foreground">
Calories/day
</div>
</div>
<Button
variant="outline"
size="icon"
class="h-8 w-8 shrink-0 rounded-full"
on:click={() => handleClick(10)}
disabled={goal >= 400}
>
<Plus class="h-4 w-4" />
<span class="sr-only">Increase</span>
</Button>
</div>
<div class="mt-3 h-[120px]">
<VisXYContainer {data} height={60}>
<VisGroupedBar {x} {y} color={"hsl(var(--primary) / 0.2)"} />
</VisXYContainer>
</div>
</div>
<Drawer.Footer>
<Button>Submit</Button>
<Drawer.Close asChild let:builder>
<Button builders={[builder]} variant="outline">Cancel</Button>
</Drawer.Close>
</Drawer.Footer>
</div>
</Drawer.Content>
</Drawer.Root>
About
Drawer is built on top of Vaul Svelte, which is a Svelte port of Vaul by Emil Kowalski.
Installation
npx shadcn-svelte@latest add drawer
Usage
<script lang="ts">
import * as Drawer from "$lib/components/ui/drawer";
</script>
<Drawer.Root>
<Drawer.Trigger>Open</Drawer.Trigger>
<Drawer.Content>
<Drawer.Header>
<Drawer.Title>Are you sure absolutely sure?</Drawer.Title>
<Drawer.Description>This action cannot be undone.</Drawer.Description>
</Drawer.Header>
<Drawer.Footer>
<Button>Submit</Button>
<Drawer.Close>Cancel</Drawer.Close>
</Drawer.Footer>
</Drawer.Content>
</Drawer.Root>
Examples
Responsive Dialog
You can combine the Dialog
and Drawer
components to create a responsive dialog. This renders a Dialog
on desktop and a Drawer
on mobile.
Loading...
<script lang="ts">
import * as Dialog from "$lib/components/ui/dialog";
import * as Drawer from "$lib/components/ui/drawer";
import { Input } from "$lib/components/ui/input";
import { Label } from "$lib/components/ui/label";
import { Button } from "$lib/components/ui/button";
import { mediaQuery } from "svelte-legos";
let open = false;
const isDesktop = mediaQuery("(min-width: 768px)");
</script>
{#if $isDesktop}
<Dialog.Root bind:open>
<Dialog.Trigger asChild let:builder>
<Button variant="outline" builders={[builder]}>Edit Profile</Button>
</Dialog.Trigger>
<Dialog.Content class="sm:max-w-[425px]">
<Dialog.Header>
<Dialog.Title>Edit profile</Dialog.Title>
<Dialog.Description>
Make changes to your profile here. Click save when you're done.
</Dialog.Description>
</Dialog.Header>
<form class="grid items-start gap-4">
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input type="email" id="email" value="shadcn@example.com" />
</div>
<div class="grid gap-2">
<Label for="username">Username</Label>
<Input id="username" value="@shadcn" />
</div>
<Button type="submit">Save changes</Button>
</form>
</Dialog.Content>
</Dialog.Root>
{:else}
<Drawer.Root bind:open>
<Drawer.Trigger asChild let:builder>
<Button variant="outline" builders={[builder]}>Edit Profile</Button>
</Drawer.Trigger>
<Drawer.Content>
<Drawer.Header class="text-left">
<Drawer.Title>Edit profile</Drawer.Title>
<Drawer.Description>
Make changes to your profile here. Click save when you're done.
</Drawer.Description>
</Drawer.Header>
<form class="grid items-start gap-4 px-4">
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input type="email" id="email" value="shadcn@example.com" />
</div>
<div class="grid gap-2">
<Label for="username">Username</Label>
<Input id="username" value="@shadcn" />
</div>
<Button type="submit">Save changes</Button>
</form>
<Drawer.Footer class="pt-2">
<Drawer.Close asChild let:builder>
<Button variant="outline" builders={[builder]}>Cancel</Button>
</Drawer.Close>
</Drawer.Footer>
</Drawer.Content>
</Drawer.Root>
{/if}
<script lang="ts">
import * as Dialog from "$lib/components/ui/dialog";
import * as Drawer from "$lib/components/ui/drawer";
import { Input } from "$lib/components/ui/input";
import { Label } from "$lib/components/ui/label";
import { Button } from "$lib/components/ui/button";
import { mediaQuery } from "svelte-legos";
let open = false;
const isDesktop = mediaQuery("(min-width: 768px)");
</script>
{#if $isDesktop}
<Dialog.Root bind:open>
<Dialog.Trigger asChild let:builder>
<Button variant="outline" builders={[builder]}>Edit Profile</Button>
</Dialog.Trigger>
<Dialog.Content class="sm:max-w-[425px]">
<Dialog.Header>
<Dialog.Title>Edit profile</Dialog.Title>
<Dialog.Description>
Make changes to your profile here. Click save when you're done.
</Dialog.Description>
</Dialog.Header>
<form class="grid items-start gap-4">
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input type="email" id="email" value="shadcn@example.com" />
</div>
<div class="grid gap-2">
<Label for="username">Username</Label>
<Input id="username" value="@shadcn" />
</div>
<Button type="submit">Save changes</Button>
</form>
</Dialog.Content>
</Dialog.Root>
{:else}
<Drawer.Root bind:open>
<Drawer.Trigger asChild let:builder>
<Button variant="outline" builders={[builder]}>Edit Profile</Button>
</Drawer.Trigger>
<Drawer.Content>
<Drawer.Header class="text-left">
<Drawer.Title>Edit profile</Drawer.Title>
<Drawer.Description>
Make changes to your profile here. Click save when you're done.
</Drawer.Description>
</Drawer.Header>
<form class="grid items-start gap-4 px-4">
<div class="grid gap-2">
<Label for="email">Email</Label>
<Input type="email" id="email" value="shadcn@example.com" />
</div>
<div class="grid gap-2">
<Label for="username">Username</Label>
<Input id="username" value="@shadcn" />
</div>
<Button type="submit">Save changes</Button>
</form>
<Drawer.Footer class="pt-2">
<Drawer.Close asChild let:builder>
<Button variant="outline" builders={[builder]}>Cancel</Button>
</Drawer.Close>
</Drawer.Footer>
</Drawer.Content>
</Drawer.Root>
{/if}
On This Page