aural-ui 2.0.5 → 2.0.8

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.
@@ -0,0 +1,1166 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import React, { useState } from "react"
3
+ import { Button } from "@components/button"
4
+ import { Checkbox } from "@components/checkbox"
5
+ import Input from "@components/input"
6
+ import { Label } from "@components/label"
7
+ import {
8
+ Select,
9
+ SelectContent,
10
+ SelectItem,
11
+ SelectTrigger,
12
+ SelectValue,
13
+ } from "@components/select"
14
+ import Textarea from "@components/textarea"
15
+ import { CrossIcon } from "@icons/cross-icon"
16
+ import { EditBigIcon } from "@icons/edit-big-icon"
17
+ import type { Meta, StoryObj } from "@storybook/react"
18
+
19
+ import {
20
+ Drawer,
21
+ DrawerClose,
22
+ DrawerContent,
23
+ DrawerDescription,
24
+ DrawerFooter,
25
+ DrawerHeader,
26
+ DrawerTitle,
27
+ DrawerTrigger,
28
+ } from "."
29
+
30
+ const meta: Meta<typeof DrawerContent> = {
31
+ title: "Components/UI/Drawer",
32
+ component: Drawer,
33
+ parameters: {
34
+ layout: "centered",
35
+ backgrounds: {
36
+ default: "dark",
37
+ values: [
38
+ { name: "dark", value: "#0a0a0a" },
39
+ { name: "light", value: "#ffffff" },
40
+ ],
41
+ },
42
+ docs: {
43
+ description: {
44
+ component: `
45
+ # Drawer Component
46
+
47
+ A slide-out drawer component built on Vaul primitives with support for multiple directions, smooth animations, customizable content sections, and enhanced accessibility features.
48
+
49
+ ## Features
50
+
51
+ - **Multiple Directions**: Top, bottom, left, and right slide directions
52
+ - **Multiple Variants**: Neutral and gradient styles with custom styling
53
+ - **Custom Overlays**: Configurable opacity, glass effect, and noise texture
54
+ - **Smooth Animations**: Fade in/out animations for overlay and content with customizable duration
55
+ - **Accessible**: Full keyboard navigation, screen reader support, and focus management
56
+ - **Flexible Content**: Header, footer, and body sections with custom styling
57
+ - **Portal Rendering**: Renders outside normal DOM hierarchy for proper layering
58
+ - **Focus Management**: Automatic focus trapping and restoration
59
+ - **Responsive Design**: Adapts to different screen sizes with mobile-first approach
60
+ - **Drag Support**: Built-in drag gestures for intuitive interaction
61
+ - **Nested Support**: Support for nested drawers with proper z-index management
62
+
63
+ ## Directions
64
+
65
+ The drawer can slide from different directions using the \`direction\` prop:
66
+
67
+ - **Bottom** (default): Slides up from the bottom - ideal for mobile navigation
68
+ - **Top**: Slides down from the top - useful for notifications or quick actions
69
+ - **Left**: Slides in from the left - perfect for navigation menus
70
+ - **Right**: Slides in from the right - great for settings or detail panels
71
+
72
+ ## Usage Examples
73
+
74
+ ### Basic Drawer with Direction Configuration
75
+ \`\`\`tsx
76
+ import { Drawer, DrawerContent, DrawerTrigger } from '@/components/drawer'
77
+
78
+ // Bottom drawer (default)
79
+ <Drawer>
80
+ <DrawerTrigger asChild>
81
+ <Button>Open Bottom Drawer</Button>
82
+ </DrawerTrigger>
83
+ <DrawerContent>
84
+ Content slides up from bottom
85
+ </DrawerContent>
86
+ </Drawer>
87
+
88
+ // Top drawer
89
+ <Drawer>
90
+ <DrawerTrigger asChild>
91
+ <Button>Open Top Drawer</Button>
92
+ </DrawerTrigger>
93
+ <DrawerContent>
94
+ Content slides down from top
95
+ </DrawerContent>
96
+ </Drawer>
97
+
98
+ // Left drawer
99
+ <Drawer>
100
+ <DrawerTrigger asChild>
101
+ <Button>Open Left Drawer</Button>
102
+ </DrawerTrigger>
103
+ <DrawerContent>
104
+ Content slides in from left
105
+ </DrawerContent>
106
+ </Drawer>
107
+
108
+ // Right drawer
109
+ <Drawer>
110
+ <DrawerTrigger asChild>
111
+ <Button>Open Right Drawer</Button>
112
+ </DrawerTrigger>
113
+ <DrawerContent>
114
+ Content slides in from right
115
+ </DrawerContent>
116
+ </Drawer>
117
+
118
+ ### Drawer with Custom Overlay Effects
119
+ \`\`\`tsx
120
+ <Drawer>
121
+ <DrawerTrigger asChild>
122
+ <Button>Open Drawer with Custom Overlay</Button>
123
+ </DrawerTrigger>
124
+ <DrawerContent
125
+ noise="high"
126
+ opacity="medium"
127
+ glass="low"
128
+ >
129
+ Content with custom overlay effects
130
+ </DrawerContent>
131
+ </Drawer>
132
+ \`\`\`
133
+
134
+ ### Overlay Configuration
135
+
136
+ The drawer supports configurable overlay effects through the \`opacity\`, \`glass\`, and \`noise\` props:
137
+
138
+ - **opacity**: Controls background dimming level
139
+ - \`"high"\` - Strong dimming (80%)
140
+ - \`"medium"\` - Balanced dimming (60%) - default
141
+ - \`"low"\` - Subtle dimming (40%)
142
+ - \`"none"\` - No dimming (100% coverage)
143
+
144
+ - **glass**: Controls backdrop blur effect
145
+ - \`"high"\` - Strong blur effect
146
+ - \`"medium"\` - Balanced blur effect
147
+ - \`"low"\` - Subtle blur effect - default
148
+ - \`"none"\` - No blur effect
149
+
150
+ - **noise**: Controls texture overlay
151
+ - \`"high"\` - Strong texture pattern
152
+ - \`"medium"\` - Balanced texture pattern
153
+ - \`"low"\` - Subtle texture pattern - default
154
+ - \`"none"\` - No texture pattern
155
+
156
+ ### Usage Examples
157
+
158
+ \`\`\`tsx
159
+ // High opacity with glass effect
160
+ <DrawerContent opacity="high" glass="medium" noise="low">
161
+ Content with strong background dimming
162
+ </DrawerContent>
163
+
164
+ // Subtle overlay with texture
165
+ <DrawerContent opacity="low" glass="none" noise="high">
166
+ Content with subtle dimming and strong texture
167
+ </DrawerContent>
168
+
169
+ // No overlay effects
170
+ <DrawerContent opacity="none" glass="none" noise="none">
171
+ Content without any overlay effects
172
+ </DrawerContent>
173
+ \`\`\`
174
+ \`\`\`
175
+
176
+ ### Drawer with Form Elements
177
+ \`\`\`tsx
178
+ <Drawer>
179
+ <DrawerTrigger asChild>
180
+ <Button>Open Form Drawer</Button>
181
+ </DrawerTrigger>
182
+ <DrawerContent>
183
+ <DrawerHeader>
184
+ <DrawerTitle>Contact Form</DrawerTitle>
185
+ <DrawerDescription>Fill out the form below to get in touch.</DrawerDescription>
186
+ </DrawerHeader>
187
+ <div className="space-y-4 p-4">
188
+ <Input placeholder="Name" />
189
+ <Input placeholder="Email" type="email" />
190
+ <Textarea placeholder="Message" rows={4} />
191
+ </div>
192
+ <DrawerFooter>
193
+ <Button>Submit</Button>
194
+ <DrawerClose asChild>
195
+ <Button variant="outline">Cancel</Button>
196
+ </DrawerClose>
197
+ </DrawerFooter>
198
+ </DrawerContent>
199
+ </Drawer>
200
+ \`\`\`
201
+ `,
202
+ },
203
+ },
204
+ },
205
+ argTypes: {
206
+ variant: {
207
+ control: { type: "select" },
208
+ options: ["neutral", "gradient"],
209
+ description: "Drawer variant for different use cases",
210
+ },
211
+ showOverlay: {
212
+ control: { type: "boolean" },
213
+ description: "Whether to show the overlay backdrop",
214
+ },
215
+ showSwipeButton: {
216
+ control: { type: "boolean" },
217
+ description: "Whether to show the swipe indicator button",
218
+ },
219
+ opacity: {
220
+ control: { type: "select" },
221
+ options: ["high", "medium", "low", "none"],
222
+ description: "Overlay opacity level",
223
+ },
224
+ glass: {
225
+ control: { type: "select" },
226
+ options: ["high", "medium", "low", "none"],
227
+ description: "Overlay glass effect level",
228
+ },
229
+ noise: {
230
+ control: { type: "select" },
231
+ options: ["high", "medium", "low", "none"],
232
+ description: "Overlay noise texture level",
233
+ },
234
+ },
235
+ tags: ["autodocs"],
236
+ }
237
+
238
+ export default meta
239
+ type Story = StoryObj<typeof Drawer>
240
+
241
+ // 1. Direction Examples
242
+ export const DirectionExamples: Story = {
243
+ render: () => {
244
+ const DirectionDrawer = ({ direction, title, description }: any) => {
245
+ return (
246
+ <Drawer direction={direction}>
247
+ <DrawerTrigger asChild>
248
+ <Button variant="outline" size="sm">
249
+ {title}
250
+ </Button>
251
+ </DrawerTrigger>
252
+ <DrawerContent>
253
+ <DrawerHeader>
254
+ <DrawerTitle>{title}</DrawerTitle>
255
+ <DrawerDescription>{description}</DrawerDescription>
256
+ </DrawerHeader>
257
+ <div className="p-2 py-4">
258
+ <div className="rounded-lg border border-white/10 bg-white/5 p-3">
259
+ <div className="space-y-1 text-xs text-white/60">
260
+ <div>
261
+ Direction: <span className="text-white">{direction}</span>
262
+ </div>
263
+ <div>
264
+ Content:{" "}
265
+ <span className="text-white">Slides from {direction}</span>
266
+ </div>
267
+ </div>
268
+ </div>
269
+ </div>
270
+ <DrawerFooter>
271
+ <DrawerClose asChild>
272
+ <Button>Close</Button>
273
+ </DrawerClose>
274
+ </DrawerFooter>
275
+ </DrawerContent>
276
+ </Drawer>
277
+ )
278
+ }
279
+
280
+ return (
281
+ <div className="space-y-8">
282
+ <div className="text-center">
283
+ <h3 className="mb-2 font-medium text-white">Drawer Directions</h3>
284
+ <p className="text-sm text-white/60">
285
+ Explore different slide directions for various use cases
286
+ </p>
287
+ </div>
288
+
289
+ <div className="space-y-6">
290
+ {/* Bottom Direction */}
291
+ <div className="space-y-4">
292
+ <h4 className="text-sm font-medium text-white/80">
293
+ Bottom Direction (Default)
294
+ </h4>
295
+ <div className="flex flex-wrap gap-2">
296
+ <DirectionDrawer
297
+ direction="bottom"
298
+ title="Bottom Drawer"
299
+ description="Slides up from the bottom - ideal for mobile navigation"
300
+ />
301
+ </div>
302
+ </div>
303
+
304
+ {/* Top Direction */}
305
+ <div className="space-y-4">
306
+ <h4 className="text-sm font-medium text-white/80">Top Direction</h4>
307
+ <div className="flex flex-wrap gap-2">
308
+ <DirectionDrawer
309
+ direction="top"
310
+ title="Top Drawer"
311
+ description="Slides down from the top - useful for notifications"
312
+ />
313
+ </div>
314
+ </div>
315
+
316
+ {/* Left Direction */}
317
+ <div className="space-y-4">
318
+ <h4 className="text-sm font-medium text-white/80">
319
+ Left Direction
320
+ </h4>
321
+ <div className="flex flex-wrap gap-2">
322
+ <DirectionDrawer
323
+ direction="left"
324
+ title="Left Drawer"
325
+ description="Slides in from the left - perfect for navigation menus"
326
+ />
327
+ </div>
328
+ </div>
329
+
330
+ {/* Right Direction */}
331
+ <div className="space-y-4">
332
+ <h4 className="text-sm font-medium text-white/80">
333
+ Right Direction
334
+ </h4>
335
+ <div className="flex flex-wrap gap-2">
336
+ <DirectionDrawer
337
+ direction="right"
338
+ title="Right Drawer"
339
+ description="Slides in from the right - great for settings panels"
340
+ />
341
+ </div>
342
+ </div>
343
+ </div>
344
+ </div>
345
+ )
346
+ },
347
+ parameters: {
348
+ docs: {
349
+ description: {
350
+ story:
351
+ "Comprehensive showcase of all drawer direction options with descriptions of their use cases.",
352
+ },
353
+ },
354
+ },
355
+ }
356
+
357
+ // 6. Basic Drawer
358
+ export const Basic: Story = {
359
+ render: () => (
360
+ <Drawer>
361
+ <DrawerTrigger asChild>
362
+ <Button>Open Drawer</Button>
363
+ </DrawerTrigger>
364
+ <DrawerContent>
365
+ <DrawerHeader>
366
+ <DrawerTitle>Basic Drawer</DrawerTitle>
367
+ <DrawerDescription>
368
+ This is a basic drawer example with header, content, and footer.
369
+ </DrawerDescription>
370
+ </DrawerHeader>
371
+ <p className="p-4 text-sm">
372
+ This is the main content area of the drawer. You can put any content
373
+ here.
374
+ </p>
375
+ <DrawerFooter>
376
+ <Button>Save Changes</Button>
377
+ <DrawerClose asChild>
378
+ <Button variant="outline">Cancel</Button>
379
+ </DrawerClose>
380
+ </DrawerFooter>
381
+ </DrawerContent>
382
+ </Drawer>
383
+ ),
384
+ parameters: {
385
+ docs: {
386
+ description: {
387
+ story:
388
+ "Basic drawer example demonstrating the fundamental structure with header, content, and footer sections.",
389
+ },
390
+ },
391
+ },
392
+ }
393
+ // 2. Form Drawer with Select Components
394
+ export const FormDrawer: Story = {
395
+ render: () => {
396
+ const [formData, setFormData] = useState({
397
+ name: "",
398
+ email: "",
399
+ category: "",
400
+ priority: "",
401
+ message: "",
402
+ newsletter: false,
403
+ })
404
+
405
+ const handleSubmit = (e: React.FormEvent) => {
406
+ e.preventDefault()
407
+ console.log("Form submitted:", formData)
408
+ }
409
+
410
+ return (
411
+ <Drawer>
412
+ <DrawerTrigger asChild>
413
+ <Button className="gap-2">
414
+ <EditBigIcon className="h-4 w-4" />
415
+ Contact Form
416
+ </Button>
417
+ </DrawerTrigger>
418
+ <DrawerContent>
419
+ <DrawerHeader>
420
+ <DrawerTitle>Contact Form</DrawerTitle>
421
+ <DrawerDescription>
422
+ Fill out the form below to get in touch with us.
423
+ </DrawerDescription>
424
+ </DrawerHeader>
425
+ <form onSubmit={handleSubmit} className="space-y-4 overflow-auto p-4">
426
+ <div className="space-y-2">
427
+ <Label htmlFor="name">Name</Label>
428
+ <Input
429
+ id="name"
430
+ value={formData.name}
431
+ onChange={(e) =>
432
+ setFormData({ ...formData, name: e.target.value })
433
+ }
434
+ placeholder="Enter your name"
435
+ />
436
+ </div>
437
+ <div className="space-y-2">
438
+ <Label htmlFor="email">Email</Label>
439
+ <Input
440
+ id="email"
441
+ type="email"
442
+ value={formData.email}
443
+ onChange={(e) =>
444
+ setFormData({ ...formData, email: e.target.value })
445
+ }
446
+ placeholder="Enter your email"
447
+ />
448
+ </div>
449
+ <div className="space-y-2">
450
+ <Label htmlFor="category">Category</Label>
451
+ <Select
452
+ value={formData.category}
453
+ onValueChange={(value) =>
454
+ setFormData({ ...formData, category: value })
455
+ }
456
+ >
457
+ <SelectTrigger>
458
+ <SelectValue placeholder="Select a category" />
459
+ </SelectTrigger>
460
+ <SelectContent>
461
+ <SelectItem value="general">General Inquiry</SelectItem>
462
+ <SelectItem value="support">Technical Support</SelectItem>
463
+ <SelectItem value="sales">Sales Question</SelectItem>
464
+ <SelectItem value="feedback">Feedback</SelectItem>
465
+ </SelectContent>
466
+ </Select>
467
+ </div>
468
+ <div className="space-y-2">
469
+ <Label htmlFor="priority">Priority</Label>
470
+ <Select
471
+ value={formData.priority}
472
+ onValueChange={(value) =>
473
+ setFormData({ ...formData, priority: value })
474
+ }
475
+ >
476
+ <SelectTrigger>
477
+ <SelectValue placeholder="Select priority level" />
478
+ </SelectTrigger>
479
+ <SelectContent>
480
+ <SelectItem value="low">Low</SelectItem>
481
+ <SelectItem value="medium">Medium</SelectItem>
482
+ <SelectItem value="high">High</SelectItem>
483
+ <SelectItem value="urgent">Urgent</SelectItem>
484
+ </SelectContent>
485
+ </Select>
486
+ </div>
487
+ <div className="space-y-2">
488
+ <Label htmlFor="message">Message</Label>
489
+ <Textarea
490
+ id="message"
491
+ value={formData.message}
492
+ onChange={(e) =>
493
+ setFormData({ ...formData, message: e.target.value })
494
+ }
495
+ placeholder="Enter your message"
496
+ rows={4}
497
+ />
498
+ </div>
499
+ <div className="flex items-center space-x-2">
500
+ <Checkbox
501
+ id="newsletter"
502
+ checked={formData.newsletter}
503
+ onCheckedChange={(checked) =>
504
+ setFormData({ ...formData, newsletter: !!checked })
505
+ }
506
+ />
507
+ <Label htmlFor="newsletter" className="text-sm">
508
+ Subscribe to newsletter
509
+ </Label>
510
+ </div>
511
+ </form>
512
+ <DrawerFooter>
513
+ <Button
514
+ disabled={!formData.name || !formData.email || !formData.message}
515
+ onClick={handleSubmit}
516
+ >
517
+ Submit
518
+ </Button>
519
+ <DrawerClose asChild>
520
+ <Button variant="outline">Cancel</Button>
521
+ </DrawerClose>
522
+ </DrawerFooter>
523
+ </DrawerContent>
524
+ </Drawer>
525
+ )
526
+ },
527
+ parameters: {
528
+ docs: {
529
+ description: {
530
+ story:
531
+ "Form drawer with select components instead of native select tags, demonstrating proper form handling and validation.",
532
+ },
533
+ },
534
+ },
535
+ }
536
+
537
+ // 3. Settings Drawer with Select Components
538
+ export const SettingsDrawer: Story = {
539
+ render: () => {
540
+ const [settings, setSettings] = useState({
541
+ theme: "dark",
542
+ notifications: "important",
543
+ language: "en",
544
+ timezone: "utc",
545
+ })
546
+
547
+ return (
548
+ <Drawer>
549
+ <DrawerTrigger asChild>
550
+ <Button>Open Settings</Button>
551
+ </DrawerTrigger>
552
+ <DrawerContent>
553
+ <DrawerHeader>
554
+ <DrawerTitle>Settings</DrawerTitle>
555
+ <DrawerDescription>
556
+ Configure your application preferences.
557
+ </DrawerDescription>
558
+ </DrawerHeader>
559
+ <div className="space-y-6 overflow-auto p-4">
560
+ <div className="space-y-4">
561
+ <h3 className="text-sm font-medium">Appearance</h3>
562
+ <div className="space-y-2">
563
+ <Label htmlFor="theme">Theme</Label>
564
+ <Select
565
+ value={settings.theme}
566
+ onValueChange={(value) =>
567
+ setSettings({ ...settings, theme: value })
568
+ }
569
+ >
570
+ <SelectTrigger>
571
+ <SelectValue />
572
+ </SelectTrigger>
573
+ <SelectContent>
574
+ <SelectItem value="light">Light</SelectItem>
575
+ <SelectItem value="dark">Dark</SelectItem>
576
+ <SelectItem value="system">System</SelectItem>
577
+ </SelectContent>
578
+ </Select>
579
+ </div>
580
+ </div>
581
+ <div className="space-y-4">
582
+ <h3 className="text-sm font-medium">Notifications</h3>
583
+ <div className="space-y-2">
584
+ <Label htmlFor="notifications">Email Notifications</Label>
585
+ <Select
586
+ value={settings.notifications}
587
+ onValueChange={(value) =>
588
+ setSettings({ ...settings, notifications: value })
589
+ }
590
+ >
591
+ <SelectTrigger>
592
+ <SelectValue />
593
+ </SelectTrigger>
594
+ <SelectContent>
595
+ <SelectItem value="all">All notifications</SelectItem>
596
+ <SelectItem value="important">Important only</SelectItem>
597
+ <SelectItem value="none">None</SelectItem>
598
+ </SelectContent>
599
+ </Select>
600
+ </div>
601
+ </div>
602
+ <div className="space-y-4">
603
+ <h3 className="text-sm font-medium">Regional</h3>
604
+ <div className="space-y-2">
605
+ <Label htmlFor="language">Language</Label>
606
+ <Select
607
+ value={settings.language}
608
+ onValueChange={(value) =>
609
+ setSettings({ ...settings, language: value })
610
+ }
611
+ >
612
+ <SelectTrigger>
613
+ <SelectValue />
614
+ </SelectTrigger>
615
+ <SelectContent>
616
+ <SelectItem value="en">English</SelectItem>
617
+ <SelectItem value="es">Spanish</SelectItem>
618
+ <SelectItem value="fr">French</SelectItem>
619
+ <SelectItem value="de">German</SelectItem>
620
+ </SelectContent>
621
+ </Select>
622
+ </div>
623
+ <div className="space-y-2">
624
+ <Label htmlFor="timezone">Timezone</Label>
625
+ <Select
626
+ value={settings.timezone}
627
+ onValueChange={(value) =>
628
+ setSettings({ ...settings, timezone: value })
629
+ }
630
+ >
631
+ <SelectTrigger>
632
+ <SelectValue />
633
+ </SelectTrigger>
634
+ <SelectContent>
635
+ <SelectItem value="utc">UTC</SelectItem>
636
+ <SelectItem value="est">Eastern Time</SelectItem>
637
+ <SelectItem value="pst">Pacific Time</SelectItem>
638
+ <SelectItem value="gmt">GMT</SelectItem>
639
+ </SelectContent>
640
+ </Select>
641
+ </div>
642
+ </div>
643
+ </div>
644
+ <DrawerFooter>
645
+ <Button onClick={() => console.log("Settings saved:", settings)}>
646
+ Save Settings
647
+ </Button>
648
+ <DrawerClose asChild>
649
+ <Button variant="outline">Cancel</Button>
650
+ </DrawerClose>
651
+ </DrawerFooter>
652
+ </DrawerContent>
653
+ </Drawer>
654
+ )
655
+ },
656
+ parameters: {
657
+ docs: {
658
+ description: {
659
+ story:
660
+ "Settings drawer showcasing select components for configuration options with proper state management.",
661
+ },
662
+ },
663
+ },
664
+ }
665
+
666
+ // 4. Nested Drawers
667
+ export const NestedDrawers: Story = {
668
+ render: () => {
669
+ const [isNestedOpen, setIsNestedOpen] = useState(false)
670
+
671
+ return (
672
+ <Drawer>
673
+ <DrawerTrigger asChild>
674
+ <Button>Open Parent Drawer</Button>
675
+ </DrawerTrigger>
676
+ <DrawerContent>
677
+ <DrawerHeader>
678
+ <DrawerTitle>Parent Drawer</DrawerTitle>
679
+ <DrawerDescription>
680
+ This drawer contains a nested drawer example.
681
+ </DrawerDescription>
682
+ </DrawerHeader>
683
+ <div className="space-y-4 p-4">
684
+ <p className="text-muted-foreground text-sm">
685
+ This is the parent drawer content.
686
+ </p>
687
+ <Drawer open={isNestedOpen} onOpenChange={setIsNestedOpen}>
688
+ <DrawerTrigger asChild>
689
+ <Button variant="outline">Open Nested Drawer</Button>
690
+ </DrawerTrigger>
691
+ <DrawerContent>
692
+ <DrawerHeader>
693
+ <DrawerTitle>Nested Drawer</DrawerTitle>
694
+ <DrawerDescription>
695
+ This is a nested drawer within the parent drawer.
696
+ </DrawerDescription>
697
+ </DrawerHeader>
698
+ <div className="p-4">
699
+ <p className="text-muted-foreground text-sm">
700
+ Nested drawer content goes here.
701
+ </p>
702
+ </div>
703
+ <DrawerFooter>
704
+ <Button onClick={() => setIsNestedOpen(false)}>
705
+ Close Nested
706
+ </Button>
707
+ <DrawerClose asChild>
708
+ <Button variant="outline">Cancel</Button>
709
+ </DrawerClose>
710
+ </DrawerFooter>
711
+ </DrawerContent>
712
+ </Drawer>
713
+ </div>
714
+ <DrawerFooter>
715
+ <Button>Save</Button>
716
+ <DrawerClose asChild>
717
+ <Button variant="outline">Close Parent</Button>
718
+ </DrawerClose>
719
+ </DrawerFooter>
720
+ </DrawerContent>
721
+ </Drawer>
722
+ )
723
+ },
724
+ parameters: {
725
+ docs: {
726
+ description: {
727
+ story:
728
+ "Nested drawer example demonstrating proper modal stacking and z-index management.",
729
+ },
730
+ },
731
+ },
732
+ }
733
+
734
+ // 5. Custom Close Button
735
+ export const CustomCloseButton: Story = {
736
+ render: () => (
737
+ <Drawer>
738
+ <DrawerTrigger asChild>
739
+ <Button>Open Drawer with Custom Close</Button>
740
+ </DrawerTrigger>
741
+ <DrawerContent>
742
+ <DrawerHeader>
743
+ <div className="flex items-center justify-between">
744
+ <div className="flex-1 text-center">
745
+ <DrawerTitle>Custom Close Button</DrawerTitle>
746
+ <DrawerDescription>
747
+ This drawer has a custom close button in the header.
748
+ </DrawerDescription>
749
+ </div>
750
+ <DrawerClose asChild>
751
+ <Button variant="text" size="sm" className="ml-4">
752
+ <CrossIcon className="h-4 w-4" />
753
+ </Button>
754
+ </DrawerClose>
755
+ </div>
756
+ </DrawerHeader>
757
+
758
+ <DrawerFooter>
759
+ <Button>Confirm</Button>
760
+ <DrawerClose asChild>
761
+ <Button variant="outline">Cancel</Button>
762
+ </DrawerClose>
763
+ </DrawerFooter>
764
+ </DrawerContent>
765
+ </Drawer>
766
+ ),
767
+ parameters: {
768
+ docs: {
769
+ description: {
770
+ story:
771
+ "Drawer with custom close button positioned in the header for enhanced user experience.",
772
+ },
773
+ },
774
+ },
775
+ }
776
+
777
+ // 6. Accessibility Example
778
+ export const AccessibilityExample: Story = {
779
+ render: () => {
780
+ return (
781
+ <Drawer>
782
+ <DrawerTrigger asChild>
783
+ <Button>Accessible Drawer Demo</Button>
784
+ </DrawerTrigger>
785
+ <DrawerContent>
786
+ <DrawerHeader>
787
+ <DrawerTitle>Accessibility Features</DrawerTitle>
788
+ <DrawerDescription>
789
+ This drawer demonstrates proper accessibility implementation.
790
+ </DrawerDescription>
791
+ </DrawerHeader>
792
+
793
+ <div className="space-y-4 overflow-auto p-4">
794
+ <div className="rounded-lg border border-white/10 bg-white/5 p-4">
795
+ <h4 className="mb-2 font-medium text-white">
796
+ Keyboard Navigation
797
+ </h4>
798
+ <ul className="space-y-1 text-sm text-white/80">
799
+ <li>
800
+ • <kbd className="rounded bg-white/10 px-1">Tab</kbd> -
801
+ Navigate between elements
802
+ </li>
803
+ <li>
804
+ • <kbd className="rounded bg-white/10 px-1">Escape</kbd> -
805
+ Close drawer
806
+ </li>
807
+ <li>
808
+ • <kbd className="rounded bg-white/10 px-1">Enter</kbd> -
809
+ Activate buttons
810
+ </li>
811
+ <li>
812
+ • <kbd className="rounded bg-white/10 px-1">Space</kbd> -
813
+ Activate buttons
814
+ </li>
815
+ </ul>
816
+ </div>
817
+
818
+ <div className="rounded-lg border border-white/10 bg-white/5 p-4">
819
+ <h4 className="mb-2 font-medium text-white">Focus Management</h4>
820
+ <ul className="space-y-1 text-sm text-white/80">
821
+ <li>• Automatic focus trapping within drawer</li>
822
+ <li>• Focus restoration when drawer closes</li>
823
+ <li>• Proper focus order for all interactive elements</li>
824
+ <li>• Screen reader announcements for state changes</li>
825
+ </ul>
826
+ </div>
827
+
828
+ <div className="rounded-lg border border-white/10 bg-white/5 p-4">
829
+ <h4 className="mb-2 font-medium text-white">Drag Support</h4>
830
+ <ul className="space-y-1 text-sm text-white/80">
831
+ <li>• Drag to close functionality</li>
832
+ <li>• Smooth animations during drag</li>
833
+ <li>• Haptic feedback on mobile devices</li>
834
+ <li>• Proper gesture recognition</li>
835
+ </ul>
836
+ </div>
837
+ </div>
838
+
839
+ <DrawerFooter>
840
+ <DrawerClose asChild>
841
+ <Button>Close Drawer</Button>
842
+ </DrawerClose>
843
+ </DrawerFooter>
844
+ </DrawerContent>
845
+ </Drawer>
846
+ )
847
+ },
848
+ parameters: {
849
+ docs: {
850
+ description: {
851
+ story:
852
+ "Comprehensive accessibility example showcasing keyboard navigation, focus management, and drag support features.",
853
+ },
854
+ },
855
+ },
856
+ }
857
+
858
+ // 7. Drawer without Overlay
859
+ export const DrawerWithoutOverlay: Story = {
860
+ render: () => (
861
+ <div>
862
+ <Drawer>
863
+ <DrawerTrigger asChild>
864
+ <Button className="relative z-10">Open Drawer (No Overlay)</Button>
865
+ </DrawerTrigger>
866
+ <DrawerContent showOverlay={false}>
867
+ <DrawerHeader>
868
+ <DrawerTitle>Drawer Without Overlay</DrawerTitle>
869
+ <DrawerDescription>
870
+ This drawer opens without a backdrop overlay, allowing background
871
+ content to remain visible.
872
+ </DrawerDescription>
873
+ </DrawerHeader>
874
+ <DrawerFooter>
875
+ <Button>Apply Changes</Button>
876
+ <DrawerClose asChild>
877
+ <Button variant="outline">Cancel</Button>
878
+ </DrawerClose>
879
+ </DrawerFooter>
880
+ </DrawerContent>
881
+ </Drawer>
882
+ </div>
883
+ ),
884
+ parameters: {
885
+ docs: {
886
+ description: {
887
+ story:
888
+ "Drawer example with `showOverlay={false}` demonstrating how to create non-modal drawer interactions that don't obscure background content.",
889
+ },
890
+ },
891
+ },
892
+ }
893
+
894
+ // 8. Overlay Variations
895
+ export const OverlayVariations: Story = {
896
+ render: () => {
897
+ const OverlayDrawer = ({
898
+ opacity,
899
+ glass,
900
+ noise,
901
+ title,
902
+ description,
903
+ }: any) => {
904
+ return (
905
+ <Drawer>
906
+ <DrawerTrigger asChild>
907
+ <Button variant="outline" size="sm">
908
+ {title}
909
+ </Button>
910
+ </DrawerTrigger>
911
+ <DrawerContent
912
+ opacity={opacity}
913
+ glass={glass}
914
+ noise={noise}
915
+ variant="neutral"
916
+ >
917
+ <DrawerHeader>
918
+ <DrawerTitle>{title}</DrawerTitle>
919
+ <DrawerDescription>{description}</DrawerDescription>
920
+ </DrawerHeader>
921
+ <div className="py-4">
922
+ <div className="grid grid-cols-2 gap-2 text-xs text-white/60">
923
+ <div>
924
+ Opacity:{" "}
925
+ <span className="text-white">{opacity || "default"}</span>
926
+ </div>
927
+ <div>
928
+ Glass:{" "}
929
+ <span className="text-white">{glass || "default"}</span>
930
+ </div>
931
+ <div>
932
+ Noise: <span className="text-white">{noise || "none"}</span>
933
+ </div>
934
+ <div>
935
+ Variant: <span className="text-white">neutral</span>
936
+ </div>
937
+ </div>
938
+ </div>
939
+ <DrawerFooter>
940
+ <DrawerClose asChild>
941
+ <Button>Close</Button>
942
+ </DrawerClose>
943
+ </DrawerFooter>
944
+ </DrawerContent>
945
+ </Drawer>
946
+ )
947
+ }
948
+
949
+ return (
950
+ <div className="space-y-8">
951
+ <div className="text-center">
952
+ <h3 className="mb-2 font-medium text-white">Overlay Variations</h3>
953
+ <p className="text-sm text-white/60">
954
+ Different overlay effects for various visual styles
955
+ </p>
956
+ </div>
957
+
958
+ <div className="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4">
959
+ <OverlayDrawer
960
+ opacity="low"
961
+ title="Low Opacity"
962
+ description="Subtle background dimming (40%)"
963
+ />
964
+ <OverlayDrawer
965
+ opacity="medium"
966
+ title="Medium Opacity"
967
+ description="Balanced background dimming (60%)"
968
+ />
969
+ <OverlayDrawer
970
+ opacity="high"
971
+ title="High Opacity"
972
+ description="Strong background dimming (80%)"
973
+ />
974
+ <OverlayDrawer
975
+ opacity="none"
976
+ title="Full Opacity"
977
+ description="Complete background coverage (100%)"
978
+ />
979
+ <OverlayDrawer
980
+ glass="low"
981
+ title="Low Glass"
982
+ description="Subtle backdrop blur effect"
983
+ />
984
+ <OverlayDrawer
985
+ glass="medium"
986
+ title="Medium Glass"
987
+ description="Balanced backdrop blur effect"
988
+ />
989
+ <OverlayDrawer
990
+ glass="high"
991
+ title="High Glass"
992
+ description="Strong backdrop blur effect"
993
+ />
994
+ <OverlayDrawer
995
+ noise="low"
996
+ title="Low Noise"
997
+ description="Subtle texture pattern"
998
+ />
999
+ <OverlayDrawer
1000
+ noise="medium"
1001
+ title="Medium Noise"
1002
+ description="Balanced texture pattern"
1003
+ />
1004
+ <OverlayDrawer
1005
+ noise="high"
1006
+ title="High Noise"
1007
+ description="Strong texture pattern"
1008
+ />
1009
+ <OverlayDrawer
1010
+ opacity="medium"
1011
+ glass="medium"
1012
+ noise="medium"
1013
+ title="Balanced"
1014
+ description="All effects at medium level"
1015
+ />
1016
+ </div>
1017
+ </div>
1018
+ )
1019
+ },
1020
+ parameters: {
1021
+ docs: {
1022
+ description: {
1023
+ story:
1024
+ "Showcase of different overlay configurations demonstrating various opacity, glass, and noise effects.",
1025
+ },
1026
+ },
1027
+ },
1028
+ }
1029
+
1030
+ // 9. Variant Examples
1031
+ export const VariantExamples: Story = {
1032
+ render: () => {
1033
+ return (
1034
+ <div className="space-y-8">
1035
+ <div className="text-center">
1036
+ <h3 className="mb-2 font-medium text-white">Drawer Variants</h3>
1037
+ <p className="text-sm text-white/60">
1038
+ Compare the neutral (default) and gradient variants
1039
+ </p>
1040
+ </div>
1041
+
1042
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-2">
1043
+ <div className="space-y-4">
1044
+ <h4 className="text-center font-medium text-white">
1045
+ Neutral Variant (Default)
1046
+ </h4>
1047
+ <Drawer>
1048
+ <DrawerTrigger asChild>
1049
+ <Button variant="outline" className="w-full">
1050
+ Open Neutral Drawer
1051
+ </Button>
1052
+ </DrawerTrigger>
1053
+ <DrawerContent variant="neutral">
1054
+ <DrawerHeader>
1055
+ <DrawerTitle>Neutral Variant</DrawerTitle>
1056
+ <DrawerDescription>
1057
+ This is the default neutral variant with frosted glass
1058
+ background and border.
1059
+ </DrawerDescription>
1060
+ </DrawerHeader>
1061
+ <div className="py-4">
1062
+ <div className="rounded-lg border border-white/10 bg-white/5 p-4">
1063
+ <h5 className="mb-2 font-medium text-white">Features:</h5>
1064
+ <ul className="space-y-1 text-sm text-white/70">
1065
+ <li>• Frosted glass background</li>
1066
+ <li>• Subtle border</li>
1067
+ <li>• Backdrop blur effect</li>
1068
+ <li>• Semi-transparent appearance</li>
1069
+ </ul>
1070
+ </div>
1071
+ </div>
1072
+ <DrawerFooter>
1073
+ <DrawerClose asChild>
1074
+ <Button>Close</Button>
1075
+ </DrawerClose>
1076
+ </DrawerFooter>
1077
+ </DrawerContent>
1078
+ </Drawer>
1079
+ </div>
1080
+
1081
+ <div className="space-y-4">
1082
+ <h4 className="text-center font-medium text-white">
1083
+ Gradient Variant
1084
+ </h4>
1085
+ <Drawer>
1086
+ <DrawerTrigger asChild>
1087
+ <Button variant="outline" className="w-full">
1088
+ Open Gradient Drawer
1089
+ </Button>
1090
+ </DrawerTrigger>
1091
+ <DrawerContent variant="gradient">
1092
+ <DrawerHeader>
1093
+ <DrawerTitle>Gradient Variant</DrawerTitle>
1094
+ <DrawerDescription>
1095
+ This is the gradient variant with white gradient background
1096
+ and no border.
1097
+ </DrawerDescription>
1098
+ </DrawerHeader>
1099
+ <div className="py-4">
1100
+ <div className="rounded-lg border border-white/10 bg-white/5 p-4">
1101
+ <h5 className="mb-2 font-medium text-white">Features:</h5>
1102
+ <ul className="space-y-1 text-sm text-white/70">
1103
+ <li>• White gradient background</li>
1104
+ <li>• No border</li>
1105
+ <li>• Clean, modern appearance</li>
1106
+ <li>• Solid background effect</li>
1107
+ </ul>
1108
+ </div>
1109
+ </div>
1110
+ <DrawerFooter>
1111
+ <DrawerClose asChild>
1112
+ <Button>Close</Button>
1113
+ </DrawerClose>
1114
+ </DrawerFooter>
1115
+ </DrawerContent>
1116
+ </Drawer>
1117
+ </div>
1118
+ </div>
1119
+ </div>
1120
+ )
1121
+ },
1122
+ parameters: {
1123
+ docs: {
1124
+ description: {
1125
+ story:
1126
+ "Simple comparison of the two drawer variants: neutral (default) with frosted glass background and gradient with white gradient background.",
1127
+ },
1128
+ },
1129
+ },
1130
+ }
1131
+
1132
+ export const DrawerWithSwipeButton: Story = {
1133
+ render: () => (
1134
+ <div>
1135
+ <Drawer>
1136
+ <DrawerTrigger asChild>
1137
+ <Button className="relative z-10">
1138
+ Open Drawer (With Swipe button)
1139
+ </Button>
1140
+ </DrawerTrigger>
1141
+ <DrawerContent showSwipeButton={true}>
1142
+ <DrawerHeader>
1143
+ <DrawerTitle>Drawer with Swipe button</DrawerTitle>
1144
+ <DrawerDescription>
1145
+ This drawer opens with a swipe button.
1146
+ </DrawerDescription>
1147
+ </DrawerHeader>
1148
+ <DrawerFooter>
1149
+ <Button>Apply Changes</Button>
1150
+ <DrawerClose asChild>
1151
+ <Button variant="outline">Cancel</Button>
1152
+ </DrawerClose>
1153
+ </DrawerFooter>
1154
+ </DrawerContent>
1155
+ </Drawer>
1156
+ </div>
1157
+ ),
1158
+ parameters: {
1159
+ docs: {
1160
+ description: {
1161
+ story:
1162
+ "Drawer example with `showSwipeButton={true}` showing a swipe indicator button",
1163
+ },
1164
+ },
1165
+ },
1166
+ }