@chesapeake/stack 0.1.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 (82) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/LICENSE +190 -0
  3. package/README.md +440 -0
  4. package/components/ui/avatar.tsx +87 -0
  5. package/components/ui/badge.tsx +49 -0
  6. package/components/ui/badgeVariants.ts +26 -0
  7. package/components/ui/button-group.tsx +67 -0
  8. package/components/ui/button.tsx +70 -0
  9. package/components/ui/buttonGroupVariants.ts +23 -0
  10. package/components/ui/buttonVariants.ts +38 -0
  11. package/components/ui/calendar.tsx +220 -0
  12. package/components/ui/card.tsx +162 -0
  13. package/components/ui/dialog.tsx +290 -0
  14. package/components/ui/drawer.tsx +227 -0
  15. package/components/ui/dropdown-menu.tsx +260 -0
  16. package/components/ui/index.ts +86 -0
  17. package/components/ui/input.tsx +59 -0
  18. package/components/ui/label.tsx +47 -0
  19. package/components/ui/location-input.md +194 -0
  20. package/components/ui/location-input.tsx +384 -0
  21. package/components/ui/popover.tsx +110 -0
  22. package/components/ui/select.tsx +306 -0
  23. package/components/ui/separator.tsx +50 -0
  24. package/components/ui/sonner-api.ts +8 -0
  25. package/components/ui/sonner.tsx +69 -0
  26. package/components/ui/textarea.tsx +53 -0
  27. package/components/ui/tooltip.tsx +125 -0
  28. package/components/ux/ActivityAnimation.tsx +135 -0
  29. package/components/ux/EmptyState.tsx +22 -0
  30. package/components/ux/FeedbackDrawer.tsx +141 -0
  31. package/components/ux/FireAnimation.tsx +189 -0
  32. package/components/ux/FriendList.tsx +111 -0
  33. package/components/ux/FriendRequestList.tsx +104 -0
  34. package/components/ux/GifSearch.tsx +264 -0
  35. package/components/ux/ImageUpload.tsx +829 -0
  36. package/components/ux/SelectedUsersList.tsx +91 -0
  37. package/components/ux/StandardLayout/PageLayout.tsx +107 -0
  38. package/components/ux/StandardLayout/ProfilePage.tsx +452 -0
  39. package/components/ux/StandardLayout/StandardLayout.tsx +345 -0
  40. package/components/ux/StandardLayout/index.ts +22 -0
  41. package/components/ux/TutorialCarousel.tsx +128 -0
  42. package/components/ux/UserSearch.tsx +324 -0
  43. package/components/ux/UserSelector.tsx +116 -0
  44. package/components/ux/auth/AuthSelector.tsx +154 -0
  45. package/components/ux/auth/MultiAuthProvider.tsx +196 -0
  46. package/components/ux/auth/README.md +317 -0
  47. package/components/ux/auth/context.ts +46 -0
  48. package/components/ux/auth/github/AuthCallback.tsx +177 -0
  49. package/components/ux/auth/github/AuthProvider.tsx +111 -0
  50. package/components/ux/auth/github/README.md +208 -0
  51. package/components/ux/auth/github/client.ts +127 -0
  52. package/components/ux/auth/github/config.ts +68 -0
  53. package/components/ux/auth/github/context.ts +34 -0
  54. package/components/ux/auth/github/default-client.ts +74 -0
  55. package/components/ux/auth/github/index.ts +43 -0
  56. package/components/ux/auth/github/types.ts +44 -0
  57. package/components/ux/auth/github/useGitHubAuth.ts +16 -0
  58. package/components/ux/auth/google/AuthCallback.tsx +170 -0
  59. package/components/ux/auth/google/AuthProvider.tsx +108 -0
  60. package/components/ux/auth/google/README.md +450 -0
  61. package/components/ux/auth/google/client.ts +109 -0
  62. package/components/ux/auth/google/config.ts +65 -0
  63. package/components/ux/auth/google/context.ts +34 -0
  64. package/components/ux/auth/google/default-client.ts +69 -0
  65. package/components/ux/auth/google/index.ts +43 -0
  66. package/components/ux/auth/google/types.ts +42 -0
  67. package/components/ux/auth/google/useGoogleAuth.ts +16 -0
  68. package/components/ux/auth/index.ts +72 -0
  69. package/components/ux/auth/useAuth.ts +16 -0
  70. package/components/ux/index.ts +46 -0
  71. package/components/ux/theme-provider-context.ts +20 -0
  72. package/components/ux/theme-provider.tsx +114 -0
  73. package/components/ux/useTheme.ts +42 -0
  74. package/index.ts +29 -0
  75. package/lib/utils.ts +57 -0
  76. package/package.json +170 -0
  77. package/styles/base.css +38 -0
  78. package/styles/components.css +54 -0
  79. package/styles/index.css +7 -0
  80. package/styles/tokens.css +189 -0
  81. package/tailwind.preset.ts +169 -0
  82. package/types/user.ts +69 -0
@@ -0,0 +1,162 @@
1
+ // Copyright 2026 Chesapeake Computing
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import React from "react";
5
+
6
+ import { cn } from "../../lib/utils";
7
+
8
+ /**
9
+ * The main Card container component.
10
+ * Provides consistent card styling with background, border, and shadow.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * <Card>
15
+ * <CardHeader>
16
+ * <CardTitle>Card Title</CardTitle>
17
+ * <CardDescription>Card description goes here</CardDescription>
18
+ * </CardHeader>
19
+ * <CardContent>
20
+ * <p>Main content of the card</p>
21
+ * </CardContent>
22
+ * <CardFooter>
23
+ * <Button>Action</Button>
24
+ * </CardFooter>
25
+ * </Card>
26
+ * ```
27
+ */
28
+ function Card({ className, ...props }: React.ComponentPropsWithoutRef<"div">) {
29
+ return (
30
+ <div
31
+ data-slot="card"
32
+ className={cn(
33
+ "bg-card text-card-foreground flex flex-col rounded-xl border shadow-sm",
34
+ className
35
+ )}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ /**
42
+ * Header section of the Card.
43
+ * Contains the title and description with proper spacing.
44
+ */
45
+ function CardHeader({
46
+ className,
47
+ ...props
48
+ }: React.ComponentPropsWithoutRef<"div">) {
49
+ return (
50
+ <div
51
+ data-slot="card-header"
52
+ className={cn(
53
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-4 sm:px-6 pt-4 sm:pt-6 pb-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
54
+ className
55
+ )}
56
+ {...props}
57
+ />
58
+ );
59
+ }
60
+
61
+ /**
62
+ * Title element for the Card header.
63
+ */
64
+ function CardTitle({
65
+ className,
66
+ ...props
67
+ }: React.ComponentPropsWithoutRef<"div">) {
68
+ return (
69
+ <div
70
+ data-slot="card-title"
71
+ className={cn(
72
+ "leading-tight font-semibold text-base sm:text-lg",
73
+ className
74
+ )}
75
+ {...props}
76
+ />
77
+ );
78
+ }
79
+
80
+ /**
81
+ * Description text for the Card header.
82
+ * Typically rendered below the title with muted styling.
83
+ */
84
+ function CardDescription({
85
+ className,
86
+ ...props
87
+ }: React.ComponentPropsWithoutRef<"div">) {
88
+ return (
89
+ <div
90
+ data-slot="card-description"
91
+ className={cn("text-muted-foreground text-sm", className)}
92
+ {...props}
93
+ />
94
+ );
95
+ }
96
+
97
+ /**
98
+ * Action element positioned in the top-right of the Card header.
99
+ * Useful for dropdown menus, buttons, or other actions.
100
+ */
101
+ function CardAction({
102
+ className,
103
+ ...props
104
+ }: React.ComponentPropsWithoutRef<"div">) {
105
+ return (
106
+ <div
107
+ data-slot="card-action"
108
+ className={cn(
109
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
110
+ className
111
+ )}
112
+ {...props}
113
+ />
114
+ );
115
+ }
116
+
117
+ /**
118
+ * Main content area of the Card.
119
+ * Contains the primary content with consistent padding.
120
+ */
121
+ function CardContent({
122
+ className,
123
+ ...props
124
+ }: React.ComponentPropsWithoutRef<"div">) {
125
+ return (
126
+ <div
127
+ data-slot="card-content"
128
+ className={cn("px-4 sm:px-6 pb-4 sm:pb-6", className)}
129
+ {...props}
130
+ />
131
+ );
132
+ }
133
+
134
+ /**
135
+ * Footer section of the Card.
136
+ * Typically contains actions or additional information.
137
+ */
138
+ function CardFooter({
139
+ className,
140
+ ...props
141
+ }: React.ComponentPropsWithoutRef<"div">) {
142
+ return (
143
+ <div
144
+ data-slot="card-footer"
145
+ className={cn(
146
+ "flex items-center px-4 sm:px-6 py-4 sm:py-6 [.border-t]:pt-6",
147
+ className
148
+ )}
149
+ {...props}
150
+ />
151
+ );
152
+ }
153
+
154
+ export {
155
+ Card,
156
+ CardAction,
157
+ CardContent,
158
+ CardDescription,
159
+ CardFooter,
160
+ CardHeader,
161
+ CardTitle,
162
+ };
@@ -0,0 +1,290 @@
1
+ // Copyright 2026 Chesapeake Computing
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import React from "react";
5
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
6
+
7
+ import { cn } from "../../lib/utils";
8
+
9
+ /**
10
+ * Dialog component root. Provides the context for dialog components.
11
+ * Built on top of Radix UI Dialog primitives for accessibility.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle } from "@shared/components/ui/dialog"
16
+ * import { Button } from "@shared/components/ui/button"
17
+ *
18
+ * function MyDialog() {
19
+ * return (
20
+ * <Dialog>
21
+ * <DialogTrigger asChild>
22
+ * <Button>Open Dialog</Button>
23
+ * </DialogTrigger>
24
+ * <DialogContent>
25
+ * <DialogHeader>
26
+ * <DialogTitle>Dialog Title</DialogTitle>
27
+ * </DialogHeader>
28
+ * <p>Dialog content here</p>
29
+ * </DialogContent>
30
+ * </Dialog>
31
+ * )
32
+ * }
33
+ * ```
34
+ */
35
+ function Dialog({
36
+ ...props
37
+ }: React.ComponentProps<typeof DialogPrimitive.Root>) {
38
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
39
+ }
40
+
41
+ /**
42
+ * DialogTrigger component. Wraps the element that opens the dialog.
43
+ * When clicked, the dialog will open.
44
+ *
45
+ * @example
46
+ * ```tsx
47
+ * <Dialog>
48
+ * <DialogTrigger asChild>
49
+ * <Button>Open</Button>
50
+ * </DialogTrigger>
51
+ * <DialogContent>...</DialogContent>
52
+ * </Dialog>
53
+ * ```
54
+ */
55
+ function DialogTrigger({
56
+ ...props
57
+ }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
58
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
59
+ }
60
+
61
+ /**
62
+ * DialogPortal component. Portals the dialog content outside the DOM hierarchy.
63
+ * Usually used internally by DialogContent.
64
+ */
65
+ function DialogPortal({
66
+ ...props
67
+ }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
68
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
69
+ }
70
+
71
+ /**
72
+ * DialogClose component. Closes the dialog when clicked.
73
+ *
74
+ * @example
75
+ * ```tsx
76
+ * <DialogContent>
77
+ * <DialogHeader>
78
+ * <DialogTitle>Title</DialogTitle>
79
+ * </DialogHeader>
80
+ * <p>Content</p>
81
+ * <DialogFooter>
82
+ * <DialogClose asChild>
83
+ * <Button variant="outline">Close</Button>
84
+ * </DialogClose>
85
+ * </DialogFooter>
86
+ * </DialogContent>
87
+ * ```
88
+ */
89
+ function DialogClose({
90
+ ...props
91
+ }: React.ComponentProps<typeof DialogPrimitive.Close>) {
92
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
93
+ }
94
+
95
+ /**
96
+ * DialogOverlay component. The backdrop behind the dialog content.
97
+ * Usually used internally by DialogContent.
98
+ */
99
+ function DialogOverlay({
100
+ className,
101
+ ...props
102
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
103
+ return (
104
+ <DialogPrimitive.Overlay
105
+ data-slot="dialog-overlay"
106
+ className={cn(
107
+ "fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
108
+ className
109
+ )}
110
+ {...props}
111
+ />
112
+ );
113
+ }
114
+
115
+ /**
116
+ * DialogContent component. The main content container for the dialog.
117
+ * Includes the overlay and portal by default.
118
+ *
119
+ * @example
120
+ * ```tsx
121
+ * <Dialog>
122
+ * <DialogTrigger>Open</DialogTrigger>
123
+ * <DialogContent className="max-w-md">
124
+ * <DialogHeader>
125
+ * <DialogTitle>Confirm Action</DialogTitle>
126
+ * <DialogDescription>
127
+ * Are you sure you want to proceed?
128
+ * </DialogDescription>
129
+ * </DialogHeader>
130
+ * <DialogFooter>
131
+ * <Button>Confirm</Button>
132
+ * <DialogClose asChild>
133
+ * <Button variant="outline">Cancel</Button>
134
+ * </DialogClose>
135
+ * </DialogFooter>
136
+ * </DialogContent>
137
+ * </Dialog>
138
+ * ```
139
+ */
140
+ function DialogContent({
141
+ className,
142
+ children,
143
+ ...props
144
+ }: React.ComponentProps<typeof DialogPrimitive.Content>) {
145
+ return (
146
+ <DialogPortal>
147
+ <DialogOverlay />
148
+ <DialogPrimitive.Content
149
+ data-slot="dialog-content"
150
+ className={cn(
151
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
152
+ className
153
+ )}
154
+ {...props}
155
+ >
156
+ {children}
157
+ </DialogPrimitive.Content>
158
+ </DialogPortal>
159
+ );
160
+ }
161
+
162
+ /**
163
+ * DialogHeader component. Container for the dialog title and description.
164
+ * Provides consistent spacing and layout.
165
+ *
166
+ * @example
167
+ * ```tsx
168
+ * <DialogContent>
169
+ * <DialogHeader>
170
+ * <DialogTitle>Dialog Title</DialogTitle>
171
+ * <DialogDescription>Optional description text</DialogDescription>
172
+ * </DialogHeader>
173
+ * </DialogContent>
174
+ * ```
175
+ */
176
+ function DialogHeader({
177
+ className,
178
+ ...props
179
+ }: React.ComponentPropsWithoutRef<"div">) {
180
+ return (
181
+ <div
182
+ data-slot="dialog-header"
183
+ className={cn(
184
+ "flex flex-col space-y-1.5 text-center sm:text-left",
185
+ className
186
+ )}
187
+ {...props}
188
+ />
189
+ );
190
+ }
191
+
192
+ /**
193
+ * DialogFooter component. Container for dialog action buttons.
194
+ * Provides consistent spacing and responsive layout.
195
+ *
196
+ * @example
197
+ * ```tsx
198
+ * <DialogContent>
199
+ * <DialogHeader>...</DialogHeader>
200
+ * <DialogFooter>
201
+ * <Button>Save</Button>
202
+ * <DialogClose asChild>
203
+ * <Button variant="outline">Cancel</Button>
204
+ * </DialogClose>
205
+ * </DialogFooter>
206
+ * </DialogContent>
207
+ * ```
208
+ */
209
+ function DialogFooter({
210
+ className,
211
+ ...props
212
+ }: React.ComponentPropsWithoutRef<"div">) {
213
+ return (
214
+ <div
215
+ data-slot="dialog-footer"
216
+ className={cn(
217
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
218
+ className
219
+ )}
220
+ {...props}
221
+ />
222
+ );
223
+ }
224
+
225
+ /**
226
+ * DialogTitle component. The accessible title for the dialog.
227
+ * Required for screen readers and accessibility.
228
+ *
229
+ * @example
230
+ * ```tsx
231
+ * <DialogHeader>
232
+ * <DialogTitle>Delete Item</DialogTitle>
233
+ * </DialogHeader>
234
+ * ```
235
+ */
236
+ function DialogTitle({
237
+ className,
238
+ ...props
239
+ }: React.ComponentProps<typeof DialogPrimitive.Title>) {
240
+ return (
241
+ <DialogPrimitive.Title
242
+ data-slot="dialog-title"
243
+ className={cn(
244
+ "text-lg font-semibold leading-none tracking-tight",
245
+ className
246
+ )}
247
+ {...props}
248
+ />
249
+ );
250
+ }
251
+
252
+ /**
253
+ * DialogDescription component. Optional descriptive text for the dialog.
254
+ * Helps provide context to users.
255
+ *
256
+ * @example
257
+ * ```tsx
258
+ * <DialogHeader>
259
+ * <DialogTitle>Confirm Deletion</DialogTitle>
260
+ * <DialogDescription>
261
+ * This action cannot be undone. This will permanently delete the item.
262
+ * </DialogDescription>
263
+ * </DialogHeader>
264
+ * ```
265
+ */
266
+ function DialogDescription({
267
+ className,
268
+ ...props
269
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
270
+ return (
271
+ <DialogPrimitive.Description
272
+ data-slot="dialog-description"
273
+ className={cn("text-sm text-muted-foreground", className)}
274
+ {...props}
275
+ />
276
+ );
277
+ }
278
+
279
+ export {
280
+ Dialog,
281
+ DialogClose,
282
+ DialogContent,
283
+ DialogDescription,
284
+ DialogFooter,
285
+ DialogHeader,
286
+ DialogOverlay,
287
+ DialogPortal,
288
+ DialogTitle,
289
+ DialogTrigger,
290
+ };
@@ -0,0 +1,227 @@
1
+ // Copyright 2026 Chesapeake Computing
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ "use client";
5
+
6
+ import React from "react";
7
+ import { Drawer as DrawerPrimitive } from "vaul";
8
+
9
+ import { cn } from "../../lib/utils";
10
+
11
+ /**
12
+ * Drawer component root. Manages the open/closed state of the drawer.
13
+ * Built on top of Vaul for smooth interactions and animations.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * <Drawer>
18
+ * <DrawerTrigger asChild>
19
+ * <Button>Open Drawer</Button>
20
+ * </DrawerTrigger>
21
+ * <DrawerContent>
22
+ * <DrawerHeader>
23
+ * <DrawerTitle>Drawer Title</DrawerTitle>
24
+ * <DrawerDescription>Description text</DrawerDescription>
25
+ * </DrawerHeader>
26
+ * <DrawerFooter>
27
+ * <DrawerClose asChild>
28
+ * <Button>Close</Button>
29
+ * </DrawerClose>
30
+ * </DrawerFooter>
31
+ * </DrawerContent>
32
+ * </Drawer>
33
+ * ```
34
+ */
35
+ function Drawer({
36
+ ...props
37
+ }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
38
+ return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
39
+ }
40
+
41
+ /**
42
+ * Trigger element that opens/closes the drawer.
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * <DrawerTrigger asChild>
47
+ * <Button>Open</Button>
48
+ * </DrawerTrigger>
49
+ * ```
50
+ */
51
+ function DrawerTrigger({
52
+ ...props
53
+ }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
54
+ return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
55
+ }
56
+
57
+ /**
58
+ * Portal for rendering drawer content outside the DOM hierarchy.
59
+ */
60
+ function DrawerPortal({
61
+ ...props
62
+ }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
63
+ return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
64
+ }
65
+
66
+ /**
67
+ * Closes the drawer when clicked.
68
+ *
69
+ * @example
70
+ * ```tsx
71
+ * <DrawerClose asChild>
72
+ * <Button variant="outline">Cancel</Button>
73
+ * </DrawerClose>
74
+ * ```
75
+ */
76
+ function DrawerClose({
77
+ ...props
78
+ }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
79
+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
80
+ }
81
+
82
+ /**
83
+ * Backdrop overlay behind the drawer content.
84
+ */
85
+ function DrawerOverlay({
86
+ className,
87
+ ...props
88
+ }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
89
+ return (
90
+ <DrawerPrimitive.Overlay
91
+ data-slot="drawer-overlay"
92
+ className={cn(
93
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
94
+ className
95
+ )}
96
+ {...props}
97
+ />
98
+ );
99
+ }
100
+
101
+ /**
102
+ * Main content container for the drawer.
103
+ * Supports multiple directions: top, bottom, left, right (default: bottom).
104
+ *
105
+ * @example
106
+ * ```tsx
107
+ * // Bottom drawer (default)
108
+ * <DrawerContent>
109
+ * <p>Content here</p>
110
+ * </DrawerContent>
111
+ *
112
+ * // Side drawer
113
+ * <Drawer direction="right">
114
+ * <DrawerContent>
115
+ * <p>Side content</p>
116
+ * </DrawerContent>
117
+ * </Drawer>
118
+ * ```
119
+ */
120
+ function DrawerContent({
121
+ className,
122
+ children,
123
+ ...props
124
+ }: React.ComponentProps<typeof DrawerPrimitive.Content>) {
125
+ return (
126
+ <DrawerPortal data-slot="drawer-portal">
127
+ <DrawerOverlay />
128
+ <DrawerPrimitive.Content
129
+ data-slot="drawer-content"
130
+ className={cn(
131
+ "group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
132
+ "data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg data-[vaul-drawer-direction=top]:border-b",
133
+ "data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg data-[vaul-drawer-direction=bottom]:border-t",
134
+ "data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=right]:sm:max-w-sm",
135
+ "data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=left]:sm:max-w-sm",
136
+ className
137
+ )}
138
+ {...props}
139
+ >
140
+ <div className="bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
141
+ {children}
142
+ </DrawerPrimitive.Content>
143
+ </DrawerPortal>
144
+ );
145
+ }
146
+
147
+ /**
148
+ * Header section of the drawer.
149
+ * Contains title and description with proper spacing.
150
+ */
151
+ function DrawerHeader({
152
+ className,
153
+ ...props
154
+ }: React.ComponentPropsWithoutRef<"div">) {
155
+ return (
156
+ <div
157
+ data-slot="drawer-header"
158
+ className={cn(
159
+ "flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left",
160
+ className
161
+ )}
162
+ {...props}
163
+ />
164
+ );
165
+ }
166
+
167
+ /**
168
+ * Footer section of the drawer.
169
+ * Typically contains action buttons.
170
+ */
171
+ function DrawerFooter({
172
+ className,
173
+ ...props
174
+ }: React.ComponentPropsWithoutRef<"div">) {
175
+ return (
176
+ <div
177
+ data-slot="drawer-footer"
178
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
179
+ {...props}
180
+ />
181
+ );
182
+ }
183
+
184
+ /**
185
+ * Title element for the drawer header.
186
+ */
187
+ function DrawerTitle({
188
+ className,
189
+ ...props
190
+ }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
191
+ return (
192
+ <DrawerPrimitive.Title
193
+ data-slot="drawer-title"
194
+ className={cn("text-foreground font-semibold", className)}
195
+ {...props}
196
+ />
197
+ );
198
+ }
199
+
200
+ /**
201
+ * Description text for the drawer header.
202
+ */
203
+ function DrawerDescription({
204
+ className,
205
+ ...props
206
+ }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
207
+ return (
208
+ <DrawerPrimitive.Description
209
+ data-slot="drawer-description"
210
+ className={cn("text-muted-foreground text-sm", className)}
211
+ {...props}
212
+ />
213
+ );
214
+ }
215
+
216
+ export {
217
+ Drawer,
218
+ DrawerClose,
219
+ DrawerContent,
220
+ DrawerDescription,
221
+ DrawerFooter,
222
+ DrawerHeader,
223
+ DrawerOverlay,
224
+ DrawerPortal,
225
+ DrawerTitle,
226
+ DrawerTrigger,
227
+ };