Skip to content

Commit 3392aa7

Browse files
feat: migrate sheet to drawer (#654)
* feat: migrate sheet to drawer * fix: pointerevent polyfill for vitest
1 parent 017a347 commit 3392aa7

28 files changed

Lines changed: 965 additions & 851 deletions

File tree

apps/www/src/app/examples/page.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
DataTable,
1010
DatePicker,
1111
Dialog,
12+
Drawer,
1213
EmptyState,
1314
Flex,
1415
IconButton,
@@ -21,7 +22,6 @@ import {
2122
ScrollArea,
2223
Search,
2324
Select,
24-
Sheet,
2525
Sidebar,
2626
Spinner,
2727
Text,
@@ -40,7 +40,7 @@ import React, { useState } from 'react';
4040
const Page = () => {
4141
const [dialogOpen, setDialogOpen] = useState(false);
4242
const [nestedDialogOpen, setNestedDialogOpen] = useState(false);
43-
const [dialogSheetOpen, setDialogSheetOpen] = useState(false);
43+
const [dialogDrawerOpen, setDialogDrawerOpen] = useState(false);
4444
const [search1, setSearch1] = useState('');
4545
const [search2, setSearch2] = useState('');
4646
const [search3, setSearch3] = useState('');
@@ -1536,9 +1536,9 @@ const Page = () => {
15361536
</Button>
15371537
<Button
15381538
variant='outline'
1539-
onClick={() => setDialogSheetOpen(true)}
1539+
onClick={() => setDialogDrawerOpen(true)}
15401540
>
1541-
Open Sheet
1541+
Open Drawer
15421542
</Button>
15431543
<Menu>
15441544
<Menu.Trigger render={<Button variant='outline' />}>
@@ -1584,10 +1584,14 @@ const Page = () => {
15841584
</Dialog.Content>
15851585
</Dialog>
15861586

1587-
<Sheet open={dialogSheetOpen} onOpenChange={setDialogSheetOpen}>
1588-
<Sheet.Content side='right' close>
1589-
<Sheet.Title>Sheet Title</Sheet.Title>
1590-
<Text>This is the sheet content. </Text>
1587+
<Drawer
1588+
open={dialogDrawerOpen}
1589+
onOpenChange={setDialogDrawerOpen}
1590+
side='right'
1591+
>
1592+
<Drawer.Content side='right'>
1593+
<Drawer.Title>Drawer Title</Drawer.Title>
1594+
<Text>This is the drawer content. </Text>
15911595
<Flex direction='column' gap={4} style={{ marginTop: '16px' }}>
15921596
<Text size='small'>Team Members:</Text>
15931597
<AvatarGroup>
@@ -1708,8 +1712,8 @@ const Page = () => {
17081712
value={inputValue}
17091713
onChange={e => setInputValue(e.target.value)}
17101714
/>
1711-
</Sheet.Content>
1712-
</Sheet>
1715+
</Drawer.Content>
1716+
</Drawer>
17131717

17141718
<Dialog open={nestedDialogOpen} onOpenChange={setNestedDialogOpen}>
17151719
<Dialog.Content width='500px'>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'use client';
2+
3+
import { Button, Drawer, Flex } from '@raystack/apsara';
4+
import PlaygroundLayout from './playground-layout';
5+
6+
export function DrawerExamples() {
7+
return (
8+
<PlaygroundLayout title='Drawer'>
9+
<Flex gap='medium' wrap='wrap'>
10+
<Drawer side='top'>
11+
<Drawer.Trigger asChild>
12+
<Button>Top Drawer</Button>
13+
</Drawer.Trigger>
14+
<Drawer.Content side='top'>
15+
<Drawer.Title>Top Drawer</Drawer.Title>
16+
<Drawer.Description>Slides in from the Top</Drawer.Description>
17+
</Drawer.Content>
18+
</Drawer>
19+
<Drawer side='right'>
20+
<Drawer.Trigger asChild>
21+
<Button>Right Drawer</Button>
22+
</Drawer.Trigger>
23+
<Drawer.Content side='right'>
24+
<Drawer.Title>Right Drawer</Drawer.Title>
25+
<Drawer.Description>Slides in from the Right</Drawer.Description>
26+
</Drawer.Content>
27+
</Drawer>
28+
<Drawer side='left'>
29+
<Drawer.Trigger asChild>
30+
<Button>Left Drawer</Button>
31+
</Drawer.Trigger>
32+
<Drawer.Content side='left'>
33+
<Drawer.Title>Left Drawer</Drawer.Title>
34+
<Drawer.Description>Slides in from the Left</Drawer.Description>
35+
</Drawer.Content>
36+
</Drawer>
37+
<Drawer side='bottom'>
38+
<Drawer.Trigger asChild>
39+
<Button>Bottom Drawer</Button>
40+
</Drawer.Trigger>
41+
<Drawer.Content side='bottom'>
42+
<Drawer.Title>Bottom Drawer</Drawer.Title>
43+
<Drawer.Description>Slides in from the Bottom</Drawer.Description>
44+
</Drawer.Content>
45+
</Drawer>
46+
</Flex>
47+
</PlaygroundLayout>
48+
);
49+
}

apps/www/src/components/playground/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export * from './command-examples';
1414
export * from './container-examples';
1515
export * from './data-table-examples';
1616
export * from './dialog-examples';
17+
export * from './drawer-examples';
1718
export * from './empty-state-examples';
1819
export * from './filter-chip-examples';
1920
export * from './flex-examples';
@@ -31,7 +32,6 @@ export * from './radio-examples';
3132
export * from './search-examples';
3233
export * from './select-examples';
3334
export * from './separator-examples';
34-
export * from './sheet-examples';
3535
export * from './sidebar-examples';
3636
export * from './skeleton-examples';
3737
export * from './slider-examples';

apps/www/src/components/playground/sheet-examples.tsx

Lines changed: 0 additions & 49 deletions
This file was deleted.

apps/www/src/content/docs/(overview)/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Apsara provides over 50 components organized by function:
2525
| **Navigation** | Navbar, Sidebar, Breadcrumb, Tabs, Link |
2626
| **Data Display** | DataTable, Table, List, Avatar, Badge, Chip, Indicator |
2727
| **Forms** | Button, InputField, TextArea, Select, Combobox, Checkbox, Radio, Switch, Slider, ColorPicker, Calendar |
28-
| **Feedback** | Dialog, Sheet, Popover, Tooltip, Toast, Callout, EmptyState, Skeleton, Spinner |
28+
| **Feedback** | Dialog, Drawer, Popover, Tooltip, Toast, Callout, EmptyState, Skeleton, Spinner |
2929
| **Utilities** | Command, Search, CopyButton, CodeBlock, ScrollArea |
3030

3131
## Theming
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
'use client';
2+
3+
import { getPropsString } from '@/lib/utils';
4+
5+
export const getCode = (props: Record<string, unknown>) => {
6+
return `
7+
<Drawer${getPropsString(props)}>
8+
<Drawer.Trigger>
9+
<Button>Drawer</Button>
10+
</Drawer.Trigger>
11+
<Drawer.Content${getPropsString(props)}>
12+
<Drawer.Header>
13+
<Drawer.Title>Drawer</Drawer.Title>
14+
<Drawer.Description>A simple drawer</Drawer.Description>
15+
</Drawer.Header>
16+
<Drawer.Body>Content goes here</Drawer.Body>
17+
</Drawer.Content>
18+
</Drawer>`;
19+
};
20+
21+
export const playground = {
22+
type: 'playground',
23+
controls: {
24+
side: {
25+
type: 'select',
26+
options: ['top', 'right', 'bottom', 'left'],
27+
defaultValue: 'right'
28+
},
29+
showCloseButton: {
30+
type: 'checkbox',
31+
defaultValue: true
32+
}
33+
},
34+
getCode
35+
};
36+
37+
export const basicDemo = {
38+
type: 'code',
39+
code: `
40+
<Drawer side="right">
41+
<Drawer.Trigger>
42+
<Button>Open Drawer</Button>
43+
</Drawer.Trigger>
44+
<Drawer.Content>
45+
<Drawer.Header>
46+
<Drawer.Title>Drawer Title</Drawer.Title>
47+
<Drawer.Description>Drawer description goes here</Drawer.Description>
48+
</Drawer.Header>
49+
<Drawer.Body>
50+
<span>Main content of the drawer</span>
51+
</Drawer.Body>
52+
</Drawer.Content>
53+
</Drawer>`
54+
};
55+
56+
export const positionDemo = {
57+
type: 'code',
58+
code: `
59+
<Flex gap="medium">
60+
<Drawer side="top">
61+
<Drawer.Trigger>
62+
<Button>Top Drawer</Button>
63+
</Drawer.Trigger>
64+
<Drawer.Content side="top">
65+
<Drawer.Header>
66+
<Drawer.Title>Top Drawer</Drawer.Title>
67+
<Drawer.Description>Slides in from the Top</Drawer.Description>
68+
</Drawer.Header>
69+
<Drawer.Body>Content here</Drawer.Body>
70+
</Drawer.Content>
71+
</Drawer>
72+
<Drawer side="right">
73+
<Drawer.Trigger>
74+
<Button>Right Drawer</Button>
75+
</Drawer.Trigger>
76+
<Drawer.Content side="right">
77+
<Drawer.Header>
78+
<Drawer.Title>Right Drawer</Drawer.Title>
79+
<Drawer.Description>Slides in from the Right</Drawer.Description>
80+
</Drawer.Header>
81+
<Drawer.Body>Content here</Drawer.Body>
82+
</Drawer.Content>
83+
</Drawer>
84+
<Drawer side="left">
85+
<Drawer.Trigger>
86+
<Button>Left Drawer</Button>
87+
</Drawer.Trigger>
88+
<Drawer.Content side="left">
89+
<Drawer.Header>
90+
<Drawer.Title>Left Drawer</Drawer.Title>
91+
<Drawer.Description>Slides in from the Left</Drawer.Description>
92+
</Drawer.Header>
93+
<Drawer.Body>Content here</Drawer.Body>
94+
</Drawer.Content>
95+
</Drawer>
96+
<Drawer side="bottom">
97+
<Drawer.Trigger>
98+
<Button>Bottom Drawer</Button>
99+
</Drawer.Trigger>
100+
<Drawer.Content side="bottom">
101+
<Drawer.Header>
102+
<Drawer.Title>Bottom Drawer</Drawer.Title>
103+
<Drawer.Description>Slides in from the Bottom</Drawer.Description>
104+
</Drawer.Header>
105+
<Drawer.Body>Content here</Drawer.Body>
106+
</Drawer.Content>
107+
</Drawer>
108+
</Flex>`
109+
};
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
---
2+
title: Drawer
3+
description: A panel that slides in from the edge of the screen with swipe-to-dismiss gestures.
4+
source: packages/raystack/components/drawer
5+
---
6+
7+
import { playground, basicDemo, positionDemo } from "./demo.ts";
8+
9+
<Demo data={playground} />
10+
11+
## Anatomy
12+
13+
Import and assemble the component:
14+
15+
```tsx
16+
import { Drawer } from "@raystack/apsara";
17+
18+
<Drawer side="right">
19+
<Drawer.Content>
20+
<Drawer.Header>
21+
<Drawer.Title />
22+
<Drawer.Description />
23+
</Drawer.Header>
24+
<Drawer.Body />
25+
<Drawer.Footer />
26+
</Drawer.Content>
27+
</Drawer>
28+
```
29+
30+
## API Reference
31+
32+
### Root
33+
34+
Groups all parts of the drawer. The `side` prop determines both the slide direction and the swipe-to-dismiss direction.
35+
36+
<auto-type-table path="./props.ts" name="DrawerProps" />
37+
38+
### Content
39+
40+
Renders the drawer panel that slides in from a screen edge.
41+
42+
<auto-type-table path="./props.ts" name="DrawerContentProps" />
43+
44+
### Header
45+
46+
- `children`: React.ReactNode - Content to render inside the header
47+
- `className`: string - Additional CSS class name
48+
49+
### Title
50+
51+
- Inherits all Base UI Drawer.Title props
52+
53+
### Description
54+
55+
- Inherits all Base UI Drawer.Description props
56+
57+
### Body
58+
59+
- Inherits all HTML div element props
60+
61+
### Footer
62+
63+
- Inherits all HTML div element props
64+
65+
## Examples
66+
67+
### Basic
68+
69+
<Demo data={basicDemo} />
70+
71+
### Positioning
72+
73+
The Drawer can slide in from different sides of the screen. Swipe-to-dismiss is automatically configured based on the `side` prop.
74+
75+
<Demo data={positionDemo} />
76+
77+
## Accessibility
78+
79+
- Uses `role="dialog"` with `aria-modal="true"`
80+
- Focus is trapped within the drawer and restored on close
81+
- Supports dismissal with Escape key and swipe gestures
82+
- Title is announced via `aria-labelledby`
83+

0 commit comments

Comments
 (0)