@duro-app/ui 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/components/Checkbox/Checkbox.meta.d.ts.map +1 -1
  2. package/dist/components/Cluster/Cluster.meta.d.ts.map +1 -1
  3. package/dist/components/Dialog/Dialog.d.ts +58 -0
  4. package/dist/components/Dialog/Dialog.d.ts.map +1 -0
  5. package/dist/components/Dialog/Dialog.stories.d.ts +13 -0
  6. package/dist/components/Dialog/Dialog.stories.d.ts.map +1 -0
  7. package/dist/components/Dialog/styles.css.d.ts +140 -0
  8. package/dist/components/Dialog/styles.css.d.ts.map +1 -0
  9. package/dist/components/Drawer/Drawer.d.ts +62 -0
  10. package/dist/components/Drawer/Drawer.d.ts.map +1 -0
  11. package/dist/components/Drawer/Drawer.stories.d.ts +15 -0
  12. package/dist/components/Drawer/Drawer.stories.d.ts.map +1 -0
  13. package/dist/components/Drawer/styles.css.d.ts +184 -0
  14. package/dist/components/Drawer/styles.css.d.ts.map +1 -0
  15. package/dist/components/Drawer/useSwipeDismiss.d.ts +17 -0
  16. package/dist/components/Drawer/useSwipeDismiss.d.ts.map +1 -0
  17. package/dist/components/Field/Field.meta.d.ts.map +1 -1
  18. package/dist/components/Fieldset/Fieldset.meta.d.ts.map +1 -1
  19. package/dist/components/Form/Form.meta.d.ts.map +1 -1
  20. package/dist/components/Grid/Grid.meta.d.ts.map +1 -1
  21. package/dist/components/Inline/Inline.meta.d.ts.map +1 -1
  22. package/dist/components/Input/Input.meta.d.ts.map +1 -1
  23. package/dist/components/LinkButton/LinkButton.meta.d.ts.map +1 -1
  24. package/dist/components/Menu/Menu.meta.d.ts.map +1 -1
  25. package/dist/components/SideNav/SideNav.meta.d.ts.map +1 -1
  26. package/dist/components/Stack/Stack.meta.d.ts.map +1 -1
  27. package/dist/components/Switch/Switch.meta.d.ts.map +1 -1
  28. package/dist/components/Table/Table.d.ts.map +1 -1
  29. package/dist/components/Table/Table.stories.d.ts +1 -0
  30. package/dist/components/Table/Table.stories.d.ts.map +1 -1
  31. package/dist/components/Textarea/Textarea.meta.d.ts.map +1 -1
  32. package/dist/index.css +1 -1
  33. package/dist/index.d.ts +2 -0
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +2848 -1952
  36. package/dist/index.js.map +1 -1
  37. package/package.json +2 -2
  38. package/src/components/Checkbox/Checkbox.meta.ts +4 -1
  39. package/src/components/Cluster/Cluster.meta.ts +2 -6
  40. package/src/components/Dialog/Dialog.stories.tsx +325 -0
  41. package/src/components/Dialog/Dialog.tsx +310 -0
  42. package/src/components/Dialog/styles.css.ts +197 -0
  43. package/src/components/Drawer/Drawer.stories.tsx +364 -0
  44. package/src/components/Drawer/Drawer.tsx +370 -0
  45. package/src/components/Drawer/styles.css.ts +242 -0
  46. package/src/components/Drawer/useSwipeDismiss.ts +295 -0
  47. package/src/components/Field/Field.meta.ts +1 -3
  48. package/src/components/Fieldset/Fieldset.meta.ts +1 -3
  49. package/src/components/Form/Form.meta.ts +1 -4
  50. package/src/components/Grid/Grid.meta.ts +1 -3
  51. package/src/components/Inline/Inline.meta.ts +1 -2
  52. package/src/components/Input/Input.meta.ts +2 -7
  53. package/src/components/LinkButton/LinkButton.meta.ts +1 -3
  54. package/src/components/Menu/Menu.meta.ts +1 -4
  55. package/src/components/SideNav/SideNav.meta.ts +1 -4
  56. package/src/components/Stack/Stack.meta.ts +1 -2
  57. package/src/components/Switch/Switch.meta.ts +4 -1
  58. package/src/components/Table/Table.stories.tsx +107 -0
  59. package/src/components/Table/Table.tsx +0 -1
  60. package/src/components/Textarea/Textarea.meta.ts +2 -6
  61. package/src/index.ts +2 -0
@@ -0,0 +1,197 @@
1
+ import {css} from 'react-strict-dom'
2
+ import {colors} from '@duro-app/tokens/tokens/colors.css'
3
+ import {spacing, radii} from '@duro-app/tokens/tokens/spacing.css'
4
+ import {typography} from '@duro-app/tokens/tokens/typography.css'
5
+ import {shadows} from '@duro-app/tokens/tokens/shadows.css'
6
+
7
+ export const styles = css.create({
8
+ // --- Backdrop ---
9
+ backdrop: {
10
+ position: 'fixed',
11
+ inset: 0,
12
+ backgroundColor: 'rgba(0, 0, 0, 0.4)',
13
+ zIndex: 1000,
14
+ },
15
+ backdropOpen: {
16
+ opacity: {
17
+ default: 1,
18
+ },
19
+ animationName: css.keyframes({
20
+ from: {opacity: 0},
21
+ to: {opacity: 1},
22
+ }),
23
+ animationDuration: '180ms',
24
+ animationTimingFunction: 'ease-out',
25
+ animationFillMode: 'both',
26
+ },
27
+ backdropClosing: {
28
+ opacity: {
29
+ default: 0,
30
+ },
31
+ animationName: css.keyframes({
32
+ from: {opacity: 1},
33
+ to: {opacity: 0},
34
+ }),
35
+ animationDuration: '140ms',
36
+ animationTimingFunction: 'ease-in',
37
+ animationFillMode: 'both',
38
+ },
39
+
40
+ // --- Viewport (centering container) ---
41
+ viewport: {
42
+ position: 'fixed',
43
+ inset: 0,
44
+ zIndex: 1001,
45
+ display: 'flex',
46
+ alignItems: 'center',
47
+ justifyContent: 'center',
48
+ padding: spacing.lg,
49
+ // Allow clicking backdrop through viewport when clicking outside popup
50
+ pointerEvents: 'none',
51
+ },
52
+
53
+ // --- Popup ---
54
+ popup: {
55
+ position: 'relative',
56
+ backgroundColor: colors.bgCard,
57
+ borderRadius: radii.lg,
58
+ boxShadow: shadows.lg,
59
+ width: '100%',
60
+ maxHeight: '85vh',
61
+ display: 'flex',
62
+ flexDirection: 'column',
63
+ overflow: 'hidden',
64
+ pointerEvents: 'auto',
65
+ borderWidth: 1,
66
+ borderStyle: 'solid',
67
+ borderColor: colors.border,
68
+ },
69
+ popupOpen: {
70
+ opacity: {
71
+ default: 1,
72
+ },
73
+ animationName: css.keyframes({
74
+ from: {
75
+ opacity: 0,
76
+ transform: 'scale(0.97) translateY(8px)',
77
+ },
78
+ to: {
79
+ opacity: 1,
80
+ transform: 'scale(1) translateY(0)',
81
+ },
82
+ }),
83
+ animationDuration: '180ms',
84
+ animationTimingFunction: 'ease-out',
85
+ animationFillMode: 'both',
86
+ },
87
+ popupClosing: {
88
+ opacity: {
89
+ default: 0,
90
+ },
91
+ animationName: css.keyframes({
92
+ from: {
93
+ opacity: 1,
94
+ transform: 'scale(1) translateY(0)',
95
+ },
96
+ to: {
97
+ opacity: 0,
98
+ transform: 'scale(0.97) translateY(8px)',
99
+ },
100
+ }),
101
+ animationDuration: '140ms',
102
+ animationTimingFunction: 'ease-in',
103
+ animationFillMode: 'both',
104
+ },
105
+
106
+ // --- Sizes ---
107
+ sm: {maxWidth: 400},
108
+ md: {maxWidth: 520},
109
+ lg: {maxWidth: 680},
110
+
111
+ // --- Header ---
112
+ header: {
113
+ display: 'flex',
114
+ alignItems: 'flex-start',
115
+ justifyContent: 'space-between',
116
+ paddingTop: spacing.lg,
117
+ paddingLeft: spacing.lg,
118
+ paddingRight: spacing.lg,
119
+ paddingBottom: spacing.md,
120
+ gap: spacing.md,
121
+ },
122
+
123
+ // --- Title ---
124
+ title: {
125
+ fontSize: typography.fontSizeLg,
126
+ fontWeight: typography.fontWeightSemibold,
127
+ lineHeight: typography.lineHeight,
128
+ color: colors.text,
129
+ margin: 0,
130
+ },
131
+
132
+ // --- Description ---
133
+ description: {
134
+ fontSize: typography.fontSizeSm,
135
+ lineHeight: typography.lineHeight,
136
+ color: colors.textMuted,
137
+ margin: 0,
138
+ paddingLeft: spacing.lg,
139
+ paddingRight: spacing.lg,
140
+ paddingBottom: spacing.md,
141
+ },
142
+
143
+ // --- Body ---
144
+ body: {
145
+ paddingLeft: spacing.lg,
146
+ paddingRight: spacing.lg,
147
+ paddingBottom: spacing.lg,
148
+ overflowY: 'auto',
149
+ flex: 1,
150
+ },
151
+
152
+ // --- Footer ---
153
+ footer: {
154
+ display: 'flex',
155
+ alignItems: 'center',
156
+ justifyContent: 'flex-end',
157
+ gap: spacing.sm,
158
+ paddingTop: spacing.md,
159
+ paddingLeft: spacing.lg,
160
+ paddingRight: spacing.lg,
161
+ paddingBottom: spacing.lg,
162
+ borderTopWidth: 1,
163
+ borderTopStyle: 'solid',
164
+ borderTopColor: colors.border,
165
+ },
166
+
167
+ // --- Close button ---
168
+ closeButton: {
169
+ display: 'flex',
170
+ alignItems: 'center',
171
+ justifyContent: 'center',
172
+ width: 32,
173
+ height: 32,
174
+ borderRadius: radii.sm,
175
+ borderWidth: 0,
176
+ backgroundColor: {
177
+ default: 'transparent',
178
+ ':hover': colors.bgCardHover,
179
+ },
180
+ color: colors.textMuted,
181
+ cursor: 'pointer',
182
+ padding: 0,
183
+ flexShrink: 0,
184
+ transitionProperty: 'background-color',
185
+ transitionDuration: '150ms',
186
+ },
187
+
188
+ // --- Inline wrapper (for Trigger / Close children) ---
189
+ inlineWrapper: {
190
+ display: 'inline-flex',
191
+ },
192
+
193
+ // --- Reduced motion ---
194
+ reducedMotion: {
195
+ animationDuration: '1ms',
196
+ },
197
+ })
@@ -0,0 +1,364 @@
1
+ import type {Meta, StoryObj} from '@storybook/react'
2
+ import {expect, within} from 'storybook/test'
3
+ import {useState} from 'react'
4
+ import {Drawer} from './Drawer'
5
+ import {Button} from '../Button/Button'
6
+ import {Text} from '../Text/Text'
7
+ import {Inline} from '../Inline/Inline'
8
+ import {Stack} from '../Stack/Stack'
9
+ import {Input} from '../Input/Input'
10
+ import {Field} from '../Field/Field'
11
+
12
+ const meta: Meta = {
13
+ title: 'Components/Drawer',
14
+ }
15
+
16
+ export default meta
17
+ type Story = StoryObj
18
+
19
+ // --- Default (right anchor) ---
20
+
21
+ export const Default: Story = {
22
+ render: () => (
23
+ <Drawer.Root>
24
+ <Drawer.Trigger>
25
+ <Button>Open drawer</Button>
26
+ </Drawer.Trigger>
27
+ <Drawer.Portal>
28
+ <Drawer.Header>
29
+ <Drawer.Title>Details</Drawer.Title>
30
+ <Drawer.Close />
31
+ </Drawer.Header>
32
+ <Drawer.Body>
33
+ <Text>
34
+ This drawer slides in from the right edge. It's useful for showing details,
35
+ settings panels, or supplementary content without leaving the current page.
36
+ </Text>
37
+ </Drawer.Body>
38
+ <Drawer.Footer>
39
+ <Drawer.Close>
40
+ <Button variant="secondary">Cancel</Button>
41
+ </Drawer.Close>
42
+ <Button>Save</Button>
43
+ </Drawer.Footer>
44
+ </Drawer.Portal>
45
+ </Drawer.Root>
46
+ ),
47
+ play: async ({canvasElement}) => {
48
+ const canvas = within(canvasElement)
49
+ await expect(canvas.getByText('Open drawer')).toBeInTheDocument()
50
+ expect(canvasElement.ownerDocument.querySelector('[role="dialog"]')).toBeNull()
51
+ },
52
+ }
53
+
54
+ // --- Left anchor ---
55
+
56
+ export const LeftAnchor: Story = {
57
+ render: () => (
58
+ <Drawer.Root anchor="left">
59
+ <Drawer.Trigger>
60
+ <Button variant="secondary">Open left drawer</Button>
61
+ </Drawer.Trigger>
62
+ <Drawer.Portal>
63
+ <Drawer.Header>
64
+ <Drawer.Title>Navigation</Drawer.Title>
65
+ <Drawer.Close />
66
+ </Drawer.Header>
67
+ <Drawer.Body>
68
+ <Stack gap="md">
69
+ <Text variant="label">Menu</Text>
70
+ <Text>Dashboard</Text>
71
+ <Text>Users</Text>
72
+ <Text>Settings</Text>
73
+ <Text>Certificates</Text>
74
+ </Stack>
75
+ </Drawer.Body>
76
+ </Drawer.Portal>
77
+ </Drawer.Root>
78
+ ),
79
+ }
80
+
81
+ // --- Bottom anchor (mobile-style) ---
82
+
83
+ export const BottomAnchor: Story = {
84
+ render: () => (
85
+ <Drawer.Root anchor="bottom">
86
+ <Drawer.Trigger>
87
+ <Button variant="secondary">Open bottom drawer</Button>
88
+ </Drawer.Trigger>
89
+ <Drawer.Portal>
90
+ <Drawer.Header>
91
+ <Drawer.Title>Actions</Drawer.Title>
92
+ <Drawer.Close />
93
+ </Drawer.Header>
94
+ <Drawer.Body>
95
+ <Stack gap="sm">
96
+ <Button variant="secondary" fullWidth>Share</Button>
97
+ <Button variant="secondary" fullWidth>Duplicate</Button>
98
+ <Button variant="danger" fullWidth>Delete</Button>
99
+ </Stack>
100
+ </Drawer.Body>
101
+ </Drawer.Portal>
102
+ </Drawer.Root>
103
+ ),
104
+ }
105
+
106
+ // --- Sizes ---
107
+
108
+ function SizesDemo() {
109
+ const [active, setActive] = useState<'sm' | 'md' | 'lg' | null>(null)
110
+
111
+ return (
112
+ <Inline gap="sm">
113
+ {(['sm', 'md', 'lg'] as const).map((s) => (
114
+ <Drawer.Root
115
+ key={s}
116
+ open={active === s}
117
+ onOpenChange={(open) => setActive(open ? s : null)}
118
+ >
119
+ <Drawer.Trigger>
120
+ <Button variant="secondary">{s.toUpperCase()}</Button>
121
+ </Drawer.Trigger>
122
+ <Drawer.Portal size={s}>
123
+ <Drawer.Header>
124
+ <Drawer.Title>Size: {s}</Drawer.Title>
125
+ <Drawer.Close />
126
+ </Drawer.Header>
127
+ <Drawer.Body>
128
+ <Text>This drawer uses the {s} size preset ({s === 'sm' ? '360' : s === 'md' ? '480' : '640'}px max-width).</Text>
129
+ </Drawer.Body>
130
+ <Drawer.Footer>
131
+ <Drawer.Close>
132
+ <Button variant="secondary">Close</Button>
133
+ </Drawer.Close>
134
+ </Drawer.Footer>
135
+ </Drawer.Portal>
136
+ </Drawer.Root>
137
+ ))}
138
+ </Inline>
139
+ )
140
+ }
141
+
142
+ export const Sizes: Story = {
143
+ render: () => <SizesDemo />,
144
+ play: async ({canvasElement}) => {
145
+ const canvas = within(canvasElement)
146
+ await expect(canvas.getByText('SM')).toBeInTheDocument()
147
+ await expect(canvas.getByText('MD')).toBeInTheDocument()
148
+ await expect(canvas.getByText('LG')).toBeInTheDocument()
149
+ },
150
+ }
151
+
152
+ // --- With Form ---
153
+
154
+ function FormDrawerDemo() {
155
+ const [open, setOpen] = useState(false)
156
+
157
+ return (
158
+ <Drawer.Root open={open} onOpenChange={setOpen}>
159
+ <Drawer.Trigger>
160
+ <Button>Edit profile</Button>
161
+ </Drawer.Trigger>
162
+ <Drawer.Portal size="md">
163
+ <Drawer.Header>
164
+ <Drawer.Title>Edit profile</Drawer.Title>
165
+ <Drawer.Close />
166
+ </Drawer.Header>
167
+ <Drawer.Description>
168
+ Update your account information below.
169
+ </Drawer.Description>
170
+ <Drawer.Body>
171
+ <Stack gap="md">
172
+ <Field.Root>
173
+ <Field.Label>Display name</Field.Label>
174
+ <Input placeholder="Enter your name" />
175
+ </Field.Root>
176
+ <Field.Root>
177
+ <Field.Label>Email</Field.Label>
178
+ <Input type="email" placeholder="you@example.com" />
179
+ </Field.Root>
180
+ <Field.Root>
181
+ <Field.Label>Bio</Field.Label>
182
+ <Input placeholder="Tell us about yourself" />
183
+ </Field.Root>
184
+ </Stack>
185
+ </Drawer.Body>
186
+ <Drawer.Footer>
187
+ <Drawer.Close>
188
+ <Button variant="secondary">Cancel</Button>
189
+ </Drawer.Close>
190
+ <Button onClick={() => setOpen(false)}>Save changes</Button>
191
+ </Drawer.Footer>
192
+ </Drawer.Portal>
193
+ </Drawer.Root>
194
+ )
195
+ }
196
+
197
+ export const WithForm: Story = {
198
+ render: () => <FormDrawerDemo />,
199
+ }
200
+
201
+ // --- Right anchor (use case) ---
202
+
203
+ function CertDetailDemo() {
204
+ const [open, setOpen] = useState(false)
205
+
206
+ return (
207
+ <Drawer.Root open={open} onOpenChange={setOpen}>
208
+ <Drawer.Trigger>
209
+ <Button variant="secondary">View certificate</Button>
210
+ </Drawer.Trigger>
211
+ <Drawer.Portal size="md">
212
+ <Drawer.Header>
213
+ <Drawer.Title>Certificate details</Drawer.Title>
214
+ <Drawer.Close />
215
+ </Drawer.Header>
216
+ <Drawer.Body>
217
+ <Stack gap="md">
218
+ <Stack gap="xs">
219
+ <Text variant="label">Subject</Text>
220
+ <Text variant="bodySm">web-server-prod-2024.pem</Text>
221
+ </Stack>
222
+ <Stack gap="xs">
223
+ <Text variant="label">Issuer</Text>
224
+ <Text variant="bodySm">Duro Internal CA</Text>
225
+ </Stack>
226
+ <Stack gap="xs">
227
+ <Text variant="label">Serial number</Text>
228
+ <Text variant="code">3A:F2:9B:C1:DE:04:71:88</Text>
229
+ </Stack>
230
+ <Stack gap="xs">
231
+ <Text variant="label">Valid from</Text>
232
+ <Text variant="bodySm">Jan 15, 2024</Text>
233
+ </Stack>
234
+ <Stack gap="xs">
235
+ <Text variant="label">Expires</Text>
236
+ <Text variant="bodySm">Jan 15, 2025</Text>
237
+ </Stack>
238
+ </Stack>
239
+ </Drawer.Body>
240
+ <Drawer.Footer>
241
+ <Drawer.Close>
242
+ <Button variant="secondary">Close</Button>
243
+ </Drawer.Close>
244
+ <Button variant="danger" onClick={() => setOpen(false)}>
245
+ Revoke
246
+ </Button>
247
+ </Drawer.Footer>
248
+ </Drawer.Portal>
249
+ </Drawer.Root>
250
+ )
251
+ }
252
+
253
+ export const RightAnchor: Story = {
254
+ render: () => <CertDetailDemo />,
255
+ }
256
+
257
+ // --- Non-dismissable ---
258
+
259
+ export const NonDismissable: Story = {
260
+ render: () => (
261
+ <Drawer.Root dismissable={false}>
262
+ <Drawer.Trigger>
263
+ <Button>Open non-dismissable drawer</Button>
264
+ </Drawer.Trigger>
265
+ <Drawer.Portal>
266
+ <Drawer.Header>
267
+ <Drawer.Title>Required action</Drawer.Title>
268
+ </Drawer.Header>
269
+ <Drawer.Body>
270
+ <Stack gap="md">
271
+ <Text>
272
+ This drawer cannot be dismissed by clicking the backdrop or pressing Escape.
273
+ You must use the explicit close action below.
274
+ </Text>
275
+ <Text variant="bodySm" color="muted">
276
+ Try clicking outside or pressing Escape — nothing happens.
277
+ </Text>
278
+ </Stack>
279
+ </Drawer.Body>
280
+ <Drawer.Footer>
281
+ <Drawer.Close>
282
+ <Button>I understand, close</Button>
283
+ </Drawer.Close>
284
+ </Drawer.Footer>
285
+ </Drawer.Portal>
286
+ </Drawer.Root>
287
+ ),
288
+ }
289
+
290
+ // --- No swipe dismiss ---
291
+
292
+ export const NoSwipeDismiss: Story = {
293
+ render: () => (
294
+ <Drawer.Root swipeDismiss={false} anchor="bottom">
295
+ <Drawer.Trigger>
296
+ <Button variant="secondary">Open (swipe disabled)</Button>
297
+ </Drawer.Trigger>
298
+ <Drawer.Portal>
299
+ <Drawer.Header>
300
+ <Drawer.Title>Swipe disabled</Drawer.Title>
301
+ <Drawer.Close />
302
+ </Drawer.Header>
303
+ <Drawer.Body>
304
+ <Text>
305
+ This bottom drawer has swipe-to-dismiss disabled. On mobile, you cannot
306
+ swipe down to close it — use the close button or tap the backdrop instead.
307
+ </Text>
308
+ </Drawer.Body>
309
+ </Drawer.Portal>
310
+ </Drawer.Root>
311
+ ),
312
+ }
313
+
314
+ // --- Swipe dismiss demo (bottom) ---
315
+
316
+ export const SwipeDismiss: Story = {
317
+ render: () => (
318
+ <Drawer.Root anchor="bottom">
319
+ <Drawer.Trigger>
320
+ <Button variant="secondary">Open bottom (swipeable)</Button>
321
+ </Drawer.Trigger>
322
+ <Drawer.Portal>
323
+ <Drawer.Header>
324
+ <Drawer.Title>Swipe to dismiss</Drawer.Title>
325
+ <Drawer.Close />
326
+ </Drawer.Header>
327
+ <Drawer.Body>
328
+ <Stack gap="md">
329
+ <Text>
330
+ Drag this drawer downward to dismiss it. The backdrop fades as you drag.
331
+ </Text>
332
+ <Text variant="bodySm" color="muted">
333
+ Swipe past 30% of the panel height or flick quickly to dismiss.
334
+ Otherwise it snaps back.
335
+ </Text>
336
+ </Stack>
337
+ </Drawer.Body>
338
+ </Drawer.Portal>
339
+ </Drawer.Root>
340
+ ),
341
+ }
342
+
343
+ // --- Swipe dismiss demo (right) ---
344
+
345
+ export const SwipeDismissRight: Story = {
346
+ render: () => (
347
+ <Drawer.Root anchor="right">
348
+ <Drawer.Trigger>
349
+ <Button variant="secondary">Open right (swipeable)</Button>
350
+ </Drawer.Trigger>
351
+ <Drawer.Portal size="sm">
352
+ <Drawer.Header>
353
+ <Drawer.Title>Swipe right to dismiss</Drawer.Title>
354
+ <Drawer.Close />
355
+ </Drawer.Header>
356
+ <Drawer.Body>
357
+ <Text>
358
+ Drag this panel to the right to dismiss it. Works with touch and mouse.
359
+ </Text>
360
+ </Drawer.Body>
361
+ </Drawer.Portal>
362
+ </Drawer.Root>
363
+ ),
364
+ }