@getjack/jack 0.1.19 → 0.1.22

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 (105) hide show
  1. package/package.json +5 -2
  2. package/src/commands/down.ts +11 -1
  3. package/src/commands/init.ts +19 -6
  4. package/src/commands/new.ts +56 -4
  5. package/src/commands/publish.ts +1 -1
  6. package/src/lib/agents.ts +3 -1
  7. package/src/lib/auth/ensure-auth.test.ts +3 -3
  8. package/src/lib/control-plane.ts +15 -1
  9. package/src/lib/deploy-upload.ts +26 -1
  10. package/src/lib/hooks.ts +232 -1
  11. package/src/lib/managed-deploy.ts +13 -6
  12. package/src/lib/managed-down.ts +66 -45
  13. package/src/lib/progress.ts +76 -5
  14. package/src/lib/project-list.ts +6 -1
  15. package/src/lib/project-operations.ts +21 -31
  16. package/src/lib/project-resolver.ts +1 -1
  17. package/src/lib/zip-packager.ts +36 -7
  18. package/src/templates/index.ts +1 -1
  19. package/src/templates/types.ts +16 -0
  20. package/templates/CLAUDE.md +172 -5
  21. package/templates/miniapp/.jack.json +1 -3
  22. package/templates/saas/.jack.json +154 -0
  23. package/templates/saas/AGENTS.md +333 -0
  24. package/templates/saas/bun.lock +925 -0
  25. package/templates/saas/components.json +21 -0
  26. package/templates/saas/index.html +12 -0
  27. package/templates/saas/package.json +75 -0
  28. package/templates/saas/public/icon.png +0 -0
  29. package/templates/saas/public/og.png +0 -0
  30. package/templates/saas/schema.sql +73 -0
  31. package/templates/saas/src/auth.ts +77 -0
  32. package/templates/saas/src/client/App.tsx +63 -0
  33. package/templates/saas/src/client/components/ProtectedRoute.tsx +29 -0
  34. package/templates/saas/src/client/components/ThemeToggle.tsx +32 -0
  35. package/templates/saas/src/client/components/ui/accordion.tsx +62 -0
  36. package/templates/saas/src/client/components/ui/alert-dialog.tsx +133 -0
  37. package/templates/saas/src/client/components/ui/alert.tsx +60 -0
  38. package/templates/saas/src/client/components/ui/aspect-ratio.tsx +9 -0
  39. package/templates/saas/src/client/components/ui/avatar.tsx +39 -0
  40. package/templates/saas/src/client/components/ui/badge.tsx +39 -0
  41. package/templates/saas/src/client/components/ui/breadcrumb.tsx +102 -0
  42. package/templates/saas/src/client/components/ui/button-group.tsx +78 -0
  43. package/templates/saas/src/client/components/ui/button.tsx +60 -0
  44. package/templates/saas/src/client/components/ui/card.tsx +75 -0
  45. package/templates/saas/src/client/components/ui/carousel.tsx +228 -0
  46. package/templates/saas/src/client/components/ui/chart.tsx +326 -0
  47. package/templates/saas/src/client/components/ui/checkbox.tsx +29 -0
  48. package/templates/saas/src/client/components/ui/collapsible.tsx +19 -0
  49. package/templates/saas/src/client/components/ui/command.tsx +159 -0
  50. package/templates/saas/src/client/components/ui/context-menu.tsx +224 -0
  51. package/templates/saas/src/client/components/ui/dialog.tsx +127 -0
  52. package/templates/saas/src/client/components/ui/drawer.tsx +124 -0
  53. package/templates/saas/src/client/components/ui/dropdown-menu.tsx +226 -0
  54. package/templates/saas/src/client/components/ui/empty.tsx +94 -0
  55. package/templates/saas/src/client/components/ui/field.tsx +232 -0
  56. package/templates/saas/src/client/components/ui/form.tsx +152 -0
  57. package/templates/saas/src/client/components/ui/hover-card.tsx +38 -0
  58. package/templates/saas/src/client/components/ui/input-group.tsx +158 -0
  59. package/templates/saas/src/client/components/ui/input-otp.tsx +68 -0
  60. package/templates/saas/src/client/components/ui/input.tsx +21 -0
  61. package/templates/saas/src/client/components/ui/item.tsx +172 -0
  62. package/templates/saas/src/client/components/ui/kbd.tsx +28 -0
  63. package/templates/saas/src/client/components/ui/label.tsx +21 -0
  64. package/templates/saas/src/client/components/ui/menubar.tsx +250 -0
  65. package/templates/saas/src/client/components/ui/navigation-menu.tsx +161 -0
  66. package/templates/saas/src/client/components/ui/pagination.tsx +106 -0
  67. package/templates/saas/src/client/components/ui/popover.tsx +42 -0
  68. package/templates/saas/src/client/components/ui/progress.tsx +26 -0
  69. package/templates/saas/src/client/components/ui/radio-group.tsx +45 -0
  70. package/templates/saas/src/client/components/ui/resizable.tsx +46 -0
  71. package/templates/saas/src/client/components/ui/scroll-area.tsx +56 -0
  72. package/templates/saas/src/client/components/ui/select.tsx +173 -0
  73. package/templates/saas/src/client/components/ui/separator.tsx +28 -0
  74. package/templates/saas/src/client/components/ui/sheet.tsx +128 -0
  75. package/templates/saas/src/client/components/ui/sidebar.tsx +694 -0
  76. package/templates/saas/src/client/components/ui/skeleton.tsx +13 -0
  77. package/templates/saas/src/client/components/ui/slider.tsx +58 -0
  78. package/templates/saas/src/client/components/ui/sonner.tsx +38 -0
  79. package/templates/saas/src/client/components/ui/spinner.tsx +16 -0
  80. package/templates/saas/src/client/components/ui/switch.tsx +28 -0
  81. package/templates/saas/src/client/components/ui/table.tsx +90 -0
  82. package/templates/saas/src/client/components/ui/tabs.tsx +54 -0
  83. package/templates/saas/src/client/components/ui/textarea.tsx +18 -0
  84. package/templates/saas/src/client/components/ui/toggle-group.tsx +80 -0
  85. package/templates/saas/src/client/components/ui/toggle.tsx +44 -0
  86. package/templates/saas/src/client/components/ui/tooltip.tsx +57 -0
  87. package/templates/saas/src/client/hooks/use-mobile.ts +19 -0
  88. package/templates/saas/src/client/hooks/useAuth.ts +14 -0
  89. package/templates/saas/src/client/hooks/useSubscription.ts +86 -0
  90. package/templates/saas/src/client/index.css +165 -0
  91. package/templates/saas/src/client/lib/auth-client.ts +7 -0
  92. package/templates/saas/src/client/lib/plans.ts +82 -0
  93. package/templates/saas/src/client/lib/utils.ts +6 -0
  94. package/templates/saas/src/client/main.tsx +15 -0
  95. package/templates/saas/src/client/pages/DashboardPage.tsx +394 -0
  96. package/templates/saas/src/client/pages/ForgotPasswordPage.tsx +153 -0
  97. package/templates/saas/src/client/pages/HomePage.tsx +285 -0
  98. package/templates/saas/src/client/pages/LoginPage.tsx +169 -0
  99. package/templates/saas/src/client/pages/PricingPage.tsx +467 -0
  100. package/templates/saas/src/client/pages/ResetPasswordPage.tsx +200 -0
  101. package/templates/saas/src/client/pages/SignupPage.tsx +192 -0
  102. package/templates/saas/src/index.ts +208 -0
  103. package/templates/saas/tsconfig.json +18 -0
  104. package/templates/saas/vite.config.ts +14 -0
  105. package/templates/saas/wrangler.jsonc +20 -0
@@ -0,0 +1,224 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
5
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
6
+
7
+ import { cn } from "@/lib/utils";
8
+
9
+ function ContextMenu({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
10
+ return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />;
11
+ }
12
+
13
+ function ContextMenuTrigger({
14
+ ...props
15
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {
16
+ return <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} />;
17
+ }
18
+
19
+ function ContextMenuGroup({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {
20
+ return <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />;
21
+ }
22
+
23
+ function ContextMenuPortal({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
24
+ return <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />;
25
+ }
26
+
27
+ function ContextMenuSub({ ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
28
+ return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />;
29
+ }
30
+
31
+ function ContextMenuRadioGroup({
32
+ ...props
33
+ }: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {
34
+ return <ContextMenuPrimitive.RadioGroup data-slot="context-menu-radio-group" {...props} />;
35
+ }
36
+
37
+ function ContextMenuSubTrigger({
38
+ className,
39
+ inset,
40
+ children,
41
+ ...props
42
+ }: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
43
+ inset?: boolean;
44
+ }) {
45
+ return (
46
+ <ContextMenuPrimitive.SubTrigger
47
+ data-slot="context-menu-sub-trigger"
48
+ data-inset={inset}
49
+ className={cn(
50
+ "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
51
+ className,
52
+ )}
53
+ {...props}
54
+ >
55
+ {children}
56
+ <ChevronRightIcon className="ml-auto" />
57
+ </ContextMenuPrimitive.SubTrigger>
58
+ );
59
+ }
60
+
61
+ function ContextMenuSubContent({
62
+ className,
63
+ ...props
64
+ }: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
65
+ return (
66
+ <ContextMenuPrimitive.SubContent
67
+ data-slot="context-menu-sub-content"
68
+ className={cn(
69
+ "bg-popover text-popover-foreground 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
70
+ className,
71
+ )}
72
+ {...props}
73
+ />
74
+ );
75
+ }
76
+
77
+ function ContextMenuContent({
78
+ className,
79
+ ...props
80
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Content>) {
81
+ return (
82
+ <ContextMenuPrimitive.Portal>
83
+ <ContextMenuPrimitive.Content
84
+ data-slot="context-menu-content"
85
+ className={cn(
86
+ "bg-popover text-popover-foreground 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
87
+ className,
88
+ )}
89
+ {...props}
90
+ />
91
+ </ContextMenuPrimitive.Portal>
92
+ );
93
+ }
94
+
95
+ function ContextMenuItem({
96
+ className,
97
+ inset,
98
+ variant = "default",
99
+ ...props
100
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
101
+ inset?: boolean;
102
+ variant?: "default" | "destructive";
103
+ }) {
104
+ return (
105
+ <ContextMenuPrimitive.Item
106
+ data-slot="context-menu-item"
107
+ data-inset={inset}
108
+ data-variant={variant}
109
+ className={cn(
110
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
111
+ className,
112
+ )}
113
+ {...props}
114
+ />
115
+ );
116
+ }
117
+
118
+ function ContextMenuCheckboxItem({
119
+ className,
120
+ children,
121
+ checked,
122
+ ...props
123
+ }: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) {
124
+ return (
125
+ <ContextMenuPrimitive.CheckboxItem
126
+ data-slot="context-menu-checkbox-item"
127
+ className={cn(
128
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
129
+ className,
130
+ )}
131
+ checked={checked}
132
+ {...props}
133
+ >
134
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
135
+ <ContextMenuPrimitive.ItemIndicator>
136
+ <CheckIcon className="size-4" />
137
+ </ContextMenuPrimitive.ItemIndicator>
138
+ </span>
139
+ {children}
140
+ </ContextMenuPrimitive.CheckboxItem>
141
+ );
142
+ }
143
+
144
+ function ContextMenuRadioItem({
145
+ className,
146
+ children,
147
+ ...props
148
+ }: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) {
149
+ return (
150
+ <ContextMenuPrimitive.RadioItem
151
+ data-slot="context-menu-radio-item"
152
+ className={cn(
153
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
154
+ className,
155
+ )}
156
+ {...props}
157
+ >
158
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
159
+ <ContextMenuPrimitive.ItemIndicator>
160
+ <CircleIcon className="size-2 fill-current" />
161
+ </ContextMenuPrimitive.ItemIndicator>
162
+ </span>
163
+ {children}
164
+ </ContextMenuPrimitive.RadioItem>
165
+ );
166
+ }
167
+
168
+ function ContextMenuLabel({
169
+ className,
170
+ inset,
171
+ ...props
172
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
173
+ inset?: boolean;
174
+ }) {
175
+ return (
176
+ <ContextMenuPrimitive.Label
177
+ data-slot="context-menu-label"
178
+ data-inset={inset}
179
+ className={cn("text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className)}
180
+ {...props}
181
+ />
182
+ );
183
+ }
184
+
185
+ function ContextMenuSeparator({
186
+ className,
187
+ ...props
188
+ }: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {
189
+ return (
190
+ <ContextMenuPrimitive.Separator
191
+ data-slot="context-menu-separator"
192
+ className={cn("bg-border -mx-1 my-1 h-px", className)}
193
+ {...props}
194
+ />
195
+ );
196
+ }
197
+
198
+ function ContextMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
199
+ return (
200
+ <span
201
+ data-slot="context-menu-shortcut"
202
+ className={cn("text-muted-foreground ml-auto text-xs tracking-widest", className)}
203
+ {...props}
204
+ />
205
+ );
206
+ }
207
+
208
+ export {
209
+ ContextMenu,
210
+ ContextMenuTrigger,
211
+ ContextMenuContent,
212
+ ContextMenuItem,
213
+ ContextMenuCheckboxItem,
214
+ ContextMenuRadioItem,
215
+ ContextMenuLabel,
216
+ ContextMenuSeparator,
217
+ ContextMenuShortcut,
218
+ ContextMenuGroup,
219
+ ContextMenuPortal,
220
+ ContextMenuSub,
221
+ ContextMenuSubContent,
222
+ ContextMenuSubTrigger,
223
+ ContextMenuRadioGroup,
224
+ };
@@ -0,0 +1,127 @@
1
+ import * as React from "react";
2
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
3
+ import { XIcon } from "lucide-react";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
8
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />;
9
+ }
10
+
11
+ function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
12
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
13
+ }
14
+
15
+ function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
16
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
17
+ }
18
+
19
+ function DialogClose({ ...props }: React.ComponentProps<typeof DialogPrimitive.Close>) {
20
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
21
+ }
22
+
23
+ function DialogOverlay({
24
+ className,
25
+ ...props
26
+ }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
27
+ return (
28
+ <DialogPrimitive.Overlay
29
+ data-slot="dialog-overlay"
30
+ className={cn(
31
+ "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",
32
+ className,
33
+ )}
34
+ {...props}
35
+ />
36
+ );
37
+ }
38
+
39
+ function DialogContent({
40
+ className,
41
+ children,
42
+ showCloseButton = true,
43
+ ...props
44
+ }: React.ComponentProps<typeof DialogPrimitive.Content> & {
45
+ showCloseButton?: boolean;
46
+ }) {
47
+ return (
48
+ <DialogPortal data-slot="dialog-portal">
49
+ <DialogOverlay />
50
+ <DialogPrimitive.Content
51
+ data-slot="dialog-content"
52
+ className={cn(
53
+ "bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
54
+ className,
55
+ )}
56
+ {...props}
57
+ >
58
+ {children}
59
+ {showCloseButton && (
60
+ <DialogPrimitive.Close
61
+ data-slot="dialog-close"
62
+ className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
63
+ >
64
+ <XIcon />
65
+ <span className="sr-only">Close</span>
66
+ </DialogPrimitive.Close>
67
+ )}
68
+ </DialogPrimitive.Content>
69
+ </DialogPortal>
70
+ );
71
+ }
72
+
73
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
74
+ return (
75
+ <div
76
+ data-slot="dialog-header"
77
+ className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
78
+ {...props}
79
+ />
80
+ );
81
+ }
82
+
83
+ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
84
+ return (
85
+ <div
86
+ data-slot="dialog-footer"
87
+ className={cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
88
+ {...props}
89
+ />
90
+ );
91
+ }
92
+
93
+ function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
94
+ return (
95
+ <DialogPrimitive.Title
96
+ data-slot="dialog-title"
97
+ className={cn("text-lg leading-none font-semibold", className)}
98
+ {...props}
99
+ />
100
+ );
101
+ }
102
+
103
+ function DialogDescription({
104
+ className,
105
+ ...props
106
+ }: React.ComponentProps<typeof DialogPrimitive.Description>) {
107
+ return (
108
+ <DialogPrimitive.Description
109
+ data-slot="dialog-description"
110
+ className={cn("text-muted-foreground text-sm", className)}
111
+ {...props}
112
+ />
113
+ );
114
+ }
115
+
116
+ export {
117
+ Dialog,
118
+ DialogClose,
119
+ DialogContent,
120
+ DialogDescription,
121
+ DialogFooter,
122
+ DialogHeader,
123
+ DialogOverlay,
124
+ DialogPortal,
125
+ DialogTitle,
126
+ DialogTrigger,
127
+ };
@@ -0,0 +1,124 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Drawer as DrawerPrimitive } from "vaul";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ function Drawer({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Root>) {
9
+ return <DrawerPrimitive.Root data-slot="drawer" {...props} />;
10
+ }
11
+
12
+ function DrawerTrigger({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
13
+ return <DrawerPrimitive.Trigger data-slot="drawer-trigger" {...props} />;
14
+ }
15
+
16
+ function DrawerPortal({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
17
+ return <DrawerPrimitive.Portal data-slot="drawer-portal" {...props} />;
18
+ }
19
+
20
+ function DrawerClose({ ...props }: React.ComponentProps<typeof DrawerPrimitive.Close>) {
21
+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
22
+ }
23
+
24
+ function DrawerOverlay({
25
+ className,
26
+ ...props
27
+ }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
28
+ return (
29
+ <DrawerPrimitive.Overlay
30
+ data-slot="drawer-overlay"
31
+ className={cn(
32
+ "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",
33
+ className,
34
+ )}
35
+ {...props}
36
+ />
37
+ );
38
+ }
39
+
40
+ function DrawerContent({
41
+ className,
42
+ children,
43
+ ...props
44
+ }: React.ComponentProps<typeof DrawerPrimitive.Content>) {
45
+ return (
46
+ <DrawerPortal data-slot="drawer-portal">
47
+ <DrawerOverlay />
48
+ <DrawerPrimitive.Content
49
+ data-slot="drawer-content"
50
+ className={cn(
51
+ "group/drawer-content bg-background fixed z-50 flex h-auto flex-col",
52
+ "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",
53
+ "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",
54
+ "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",
55
+ "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",
56
+ className,
57
+ )}
58
+ {...props}
59
+ >
60
+ <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" />
61
+ {children}
62
+ </DrawerPrimitive.Content>
63
+ </DrawerPortal>
64
+ );
65
+ }
66
+
67
+ function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
68
+ return (
69
+ <div
70
+ data-slot="drawer-header"
71
+ className={cn(
72
+ "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",
73
+ className,
74
+ )}
75
+ {...props}
76
+ />
77
+ );
78
+ }
79
+
80
+ function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) {
81
+ return (
82
+ <div
83
+ data-slot="drawer-footer"
84
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
85
+ {...props}
86
+ />
87
+ );
88
+ }
89
+
90
+ function DrawerTitle({ className, ...props }: React.ComponentProps<typeof DrawerPrimitive.Title>) {
91
+ return (
92
+ <DrawerPrimitive.Title
93
+ data-slot="drawer-title"
94
+ className={cn("text-foreground font-semibold", className)}
95
+ {...props}
96
+ />
97
+ );
98
+ }
99
+
100
+ function DrawerDescription({
101
+ className,
102
+ ...props
103
+ }: React.ComponentProps<typeof DrawerPrimitive.Description>) {
104
+ return (
105
+ <DrawerPrimitive.Description
106
+ data-slot="drawer-description"
107
+ className={cn("text-muted-foreground text-sm", className)}
108
+ {...props}
109
+ />
110
+ );
111
+ }
112
+
113
+ export {
114
+ Drawer,
115
+ DrawerPortal,
116
+ DrawerOverlay,
117
+ DrawerTrigger,
118
+ DrawerClose,
119
+ DrawerContent,
120
+ DrawerHeader,
121
+ DrawerFooter,
122
+ DrawerTitle,
123
+ DrawerDescription,
124
+ };
@@ -0,0 +1,226 @@
1
+ import * as React from "react";
2
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
3
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
4
+
5
+ import { cn } from "@/lib/utils";
6
+
7
+ function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
8
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
9
+ }
10
+
11
+ function DropdownMenuPortal({
12
+ ...props
13
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
14
+ return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
15
+ }
16
+
17
+ function DropdownMenuTrigger({
18
+ ...props
19
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
20
+ return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
21
+ }
22
+
23
+ function DropdownMenuContent({
24
+ className,
25
+ sideOffset = 4,
26
+ ...props
27
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
28
+ return (
29
+ <DropdownMenuPrimitive.Portal>
30
+ <DropdownMenuPrimitive.Content
31
+ data-slot="dropdown-menu-content"
32
+ sideOffset={sideOffset}
33
+ className={cn(
34
+ "bg-popover text-popover-foreground 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
35
+ className,
36
+ )}
37
+ {...props}
38
+ />
39
+ </DropdownMenuPrimitive.Portal>
40
+ );
41
+ }
42
+
43
+ function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
44
+ return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
45
+ }
46
+
47
+ function DropdownMenuItem({
48
+ className,
49
+ inset,
50
+ variant = "default",
51
+ ...props
52
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
53
+ inset?: boolean;
54
+ variant?: "default" | "destructive";
55
+ }) {
56
+ return (
57
+ <DropdownMenuPrimitive.Item
58
+ data-slot="dropdown-menu-item"
59
+ data-inset={inset}
60
+ data-variant={variant}
61
+ className={cn(
62
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
63
+ className,
64
+ )}
65
+ {...props}
66
+ />
67
+ );
68
+ }
69
+
70
+ function DropdownMenuCheckboxItem({
71
+ className,
72
+ children,
73
+ checked,
74
+ ...props
75
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
76
+ return (
77
+ <DropdownMenuPrimitive.CheckboxItem
78
+ data-slot="dropdown-menu-checkbox-item"
79
+ className={cn(
80
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
81
+ className,
82
+ )}
83
+ checked={checked}
84
+ {...props}
85
+ >
86
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
87
+ <DropdownMenuPrimitive.ItemIndicator>
88
+ <CheckIcon className="size-4" />
89
+ </DropdownMenuPrimitive.ItemIndicator>
90
+ </span>
91
+ {children}
92
+ </DropdownMenuPrimitive.CheckboxItem>
93
+ );
94
+ }
95
+
96
+ function DropdownMenuRadioGroup({
97
+ ...props
98
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
99
+ return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;
100
+ }
101
+
102
+ function DropdownMenuRadioItem({
103
+ className,
104
+ children,
105
+ ...props
106
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
107
+ return (
108
+ <DropdownMenuPrimitive.RadioItem
109
+ data-slot="dropdown-menu-radio-item"
110
+ className={cn(
111
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
112
+ className,
113
+ )}
114
+ {...props}
115
+ >
116
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
117
+ <DropdownMenuPrimitive.ItemIndicator>
118
+ <CircleIcon className="size-2 fill-current" />
119
+ </DropdownMenuPrimitive.ItemIndicator>
120
+ </span>
121
+ {children}
122
+ </DropdownMenuPrimitive.RadioItem>
123
+ );
124
+ }
125
+
126
+ function DropdownMenuLabel({
127
+ className,
128
+ inset,
129
+ ...props
130
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
131
+ inset?: boolean;
132
+ }) {
133
+ return (
134
+ <DropdownMenuPrimitive.Label
135
+ data-slot="dropdown-menu-label"
136
+ data-inset={inset}
137
+ className={cn("px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className)}
138
+ {...props}
139
+ />
140
+ );
141
+ }
142
+
143
+ function DropdownMenuSeparator({
144
+ className,
145
+ ...props
146
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
147
+ return (
148
+ <DropdownMenuPrimitive.Separator
149
+ data-slot="dropdown-menu-separator"
150
+ className={cn("bg-border -mx-1 my-1 h-px", className)}
151
+ {...props}
152
+ />
153
+ );
154
+ }
155
+
156
+ function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
157
+ return (
158
+ <span
159
+ data-slot="dropdown-menu-shortcut"
160
+ className={cn("text-muted-foreground ml-auto text-xs tracking-widest", className)}
161
+ {...props}
162
+ />
163
+ );
164
+ }
165
+
166
+ function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
167
+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
168
+ }
169
+
170
+ function DropdownMenuSubTrigger({
171
+ className,
172
+ inset,
173
+ children,
174
+ ...props
175
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
176
+ inset?: boolean;
177
+ }) {
178
+ return (
179
+ <DropdownMenuPrimitive.SubTrigger
180
+ data-slot="dropdown-menu-sub-trigger"
181
+ data-inset={inset}
182
+ className={cn(
183
+ "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
184
+ className,
185
+ )}
186
+ {...props}
187
+ >
188
+ {children}
189
+ <ChevronRightIcon className="ml-auto size-4" />
190
+ </DropdownMenuPrimitive.SubTrigger>
191
+ );
192
+ }
193
+
194
+ function DropdownMenuSubContent({
195
+ className,
196
+ ...props
197
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
198
+ return (
199
+ <DropdownMenuPrimitive.SubContent
200
+ data-slot="dropdown-menu-sub-content"
201
+ className={cn(
202
+ "bg-popover text-popover-foreground 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-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
203
+ className,
204
+ )}
205
+ {...props}
206
+ />
207
+ );
208
+ }
209
+
210
+ export {
211
+ DropdownMenu,
212
+ DropdownMenuPortal,
213
+ DropdownMenuTrigger,
214
+ DropdownMenuContent,
215
+ DropdownMenuGroup,
216
+ DropdownMenuLabel,
217
+ DropdownMenuItem,
218
+ DropdownMenuCheckboxItem,
219
+ DropdownMenuRadioGroup,
220
+ DropdownMenuRadioItem,
221
+ DropdownMenuSeparator,
222
+ DropdownMenuShortcut,
223
+ DropdownMenuSub,
224
+ DropdownMenuSubTrigger,
225
+ DropdownMenuSubContent,
226
+ };