create-ab-app 0.1.1

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 (87) hide show
  1. package/README.md +60 -0
  2. package/index.js +380 -0
  3. package/package.json +36 -0
  4. package/template/.env.example +8 -0
  5. package/template/.oxlintrc.json +8 -0
  6. package/template/README.md +196 -0
  7. package/template/_gitignore +24 -0
  8. package/template/components.json +25 -0
  9. package/template/index.html +13 -0
  10. package/template/package-lock.json +6096 -0
  11. package/template/package.json +40 -0
  12. package/template/public/_redirects +1 -0
  13. package/template/public/favicon.svg +1 -0
  14. package/template/public/icons.svg +24 -0
  15. package/template/src/App.tsx +60 -0
  16. package/template/src/assets/hero.png +0 -0
  17. package/template/src/assets/vite.svg +1 -0
  18. package/template/src/components/app-sidebar.tsx +53 -0
  19. package/template/src/components/credit-balance.tsx +31 -0
  20. package/template/src/components/docs/doc-primitives.tsx +125 -0
  21. package/template/src/components/full-page-loader.tsx +14 -0
  22. package/template/src/components/header-user.tsx +64 -0
  23. package/template/src/components/layouts/app-layout.tsx +80 -0
  24. package/template/src/components/missing-env.tsx +24 -0
  25. package/template/src/components/nav-main.tsx +87 -0
  26. package/template/src/components/nav-user.tsx +69 -0
  27. package/template/src/components/theme-toggle.tsx +39 -0
  28. package/template/src/components/ui/avatar.tsx +106 -0
  29. package/template/src/components/ui/breadcrumb.tsx +124 -0
  30. package/template/src/components/ui/button.tsx +57 -0
  31. package/template/src/components/ui/card.tsx +102 -0
  32. package/template/src/components/ui/collapsible.tsx +19 -0
  33. package/template/src/components/ui/dropdown-menu.tsx +267 -0
  34. package/template/src/components/ui/field.tsx +238 -0
  35. package/template/src/components/ui/input.tsx +19 -0
  36. package/template/src/components/ui/label.tsx +19 -0
  37. package/template/src/components/ui/separator.tsx +22 -0
  38. package/template/src/components/ui/sheet.tsx +136 -0
  39. package/template/src/components/ui/sidebar.tsx +721 -0
  40. package/template/src/components/ui/skeleton.tsx +13 -0
  41. package/template/src/components/ui/sonner.tsx +47 -0
  42. package/template/src/components/ui/tooltip.tsx +65 -0
  43. package/template/src/components/user-menu.tsx +96 -0
  44. package/template/src/config/env.ts +14 -0
  45. package/template/src/config/navigation.ts +52 -0
  46. package/template/src/hooks/use-mobile.ts +19 -0
  47. package/template/src/hooks/use-user-identity.ts +27 -0
  48. package/template/src/index.css +134 -0
  49. package/template/src/lib/http.ts +40 -0
  50. package/template/src/lib/sso/account-settings.tsx +53 -0
  51. package/template/src/lib/sso/auth-context.ts +16 -0
  52. package/template/src/lib/sso/auth-provider.tsx +42 -0
  53. package/template/src/lib/sso/auth-screens.tsx +152 -0
  54. package/template/src/lib/sso/config.ts +13 -0
  55. package/template/src/lib/sso/entitlements.ts +101 -0
  56. package/template/src/lib/sso/index.ts +33 -0
  57. package/template/src/lib/sso/protected-route.tsx +72 -0
  58. package/template/src/lib/sso/queries.ts +55 -0
  59. package/template/src/lib/sso/require-plan.tsx +19 -0
  60. package/template/src/lib/sso/token-store.ts +62 -0
  61. package/template/src/lib/utils.ts +1 -0
  62. package/template/src/main.tsx +42 -0
  63. package/template/src/routes/app/example.tsx +125 -0
  64. package/template/src/routes/auth/forgot-password.tsx +5 -0
  65. package/template/src/routes/auth/login.tsx +5 -0
  66. package/template/src/routes/auth/signup.tsx +5 -0
  67. package/template/src/routes/auth/verify-email.tsx +5 -0
  68. package/template/src/routes/docs/api.tsx +66 -0
  69. package/template/src/routes/docs/auth.tsx +145 -0
  70. package/template/src/routes/docs/billing.tsx +103 -0
  71. package/template/src/routes/docs/configuration.tsx +113 -0
  72. package/template/src/routes/docs/overview.tsx +97 -0
  73. package/template/src/routes/docs/routing.tsx +126 -0
  74. package/template/src/routes/docs/structure.tsx +150 -0
  75. package/template/src/routes/docs/theming.tsx +88 -0
  76. package/template/src/routes/not-found.tsx +12 -0
  77. package/template/src/styles/aas-theme.css +21 -0
  78. package/template/src/vite-env.d.ts +12 -0
  79. package/template/tsconfig.app.json +30 -0
  80. package/template/tsconfig.json +12 -0
  81. package/template/tsconfig.node.json +23 -0
  82. package/template/vercel.json +3 -0
  83. package/template/vite.config.ts +17 -0
  84. package/variants/minimal/src/App.tsx +43 -0
  85. package/variants/minimal/src/components/app-sidebar.tsx +50 -0
  86. package/variants/minimal/src/config/navigation.ts +19 -0
  87. package/variants/minimal/src/routes/app/home.tsx +45 -0
@@ -0,0 +1,267 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Menu as MenuPrimitive } from "@base-ui/react/menu"
5
+ import { cn } from "cn"
6
+ import { ChevronRightIcon, CheckIcon } from "lucide-react"
7
+
8
+ function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
9
+ return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
10
+ }
11
+
12
+ function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
13
+ return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
14
+ }
15
+
16
+ function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
17
+ return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
18
+ }
19
+
20
+ function DropdownMenuContent({
21
+ align = "start",
22
+ alignOffset = 0,
23
+ side = "bottom",
24
+ sideOffset = 4,
25
+ className,
26
+ ...props
27
+ }: MenuPrimitive.Popup.Props &
28
+ Pick<
29
+ MenuPrimitive.Positioner.Props,
30
+ "align" | "alignOffset" | "side" | "sideOffset"
31
+ >) {
32
+ return (
33
+ <MenuPrimitive.Portal>
34
+ <MenuPrimitive.Positioner
35
+ className="isolate z-50 outline-none"
36
+ align={align}
37
+ alignOffset={alignOffset}
38
+ side={side}
39
+ sideOffset={sideOffset}
40
+ >
41
+ <MenuPrimitive.Popup
42
+ data-slot="dropdown-menu-content"
43
+ className={cn("z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className )}
44
+ {...props}
45
+ />
46
+ </MenuPrimitive.Positioner>
47
+ </MenuPrimitive.Portal>
48
+ )
49
+ }
50
+
51
+ function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
52
+ return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
53
+ }
54
+
55
+ function DropdownMenuLabel({
56
+ className,
57
+ inset,
58
+ ...props
59
+ }: MenuPrimitive.GroupLabel.Props & {
60
+ inset?: boolean
61
+ }) {
62
+ return (
63
+ <MenuPrimitive.GroupLabel
64
+ data-slot="dropdown-menu-label"
65
+ data-inset={inset}
66
+ className={cn(
67
+ "px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7",
68
+ className
69
+ )}
70
+ {...props}
71
+ />
72
+ )
73
+ }
74
+
75
+ function DropdownMenuItem({
76
+ className,
77
+ inset,
78
+ variant = "default",
79
+ ...props
80
+ }: MenuPrimitive.Item.Props & {
81
+ inset?: boolean
82
+ variant?: "default" | "destructive"
83
+ }) {
84
+ return (
85
+ <MenuPrimitive.Item
86
+ data-slot="dropdown-menu-item"
87
+ data-inset={inset}
88
+ data-variant={variant}
89
+ className={cn(
90
+ "group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
91
+ className
92
+ )}
93
+ {...props}
94
+ />
95
+ )
96
+ }
97
+
98
+ function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
99
+ return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />
100
+ }
101
+
102
+ function DropdownMenuSubTrigger({
103
+ className,
104
+ inset,
105
+ children,
106
+ ...props
107
+ }: MenuPrimitive.SubmenuTrigger.Props & {
108
+ inset?: boolean
109
+ }) {
110
+ return (
111
+ <MenuPrimitive.SubmenuTrigger
112
+ data-slot="dropdown-menu-sub-trigger"
113
+ data-inset={inset}
114
+ className={cn(
115
+ "flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
116
+ className
117
+ )}
118
+ {...props}
119
+ >
120
+ {children}
121
+ <ChevronRightIcon className="ml-auto" />
122
+ </MenuPrimitive.SubmenuTrigger>
123
+ )
124
+ }
125
+
126
+ function DropdownMenuSubContent({
127
+ align = "start",
128
+ alignOffset = -3,
129
+ side = "right",
130
+ sideOffset = 0,
131
+ className,
132
+ ...props
133
+ }: React.ComponentProps<typeof DropdownMenuContent>) {
134
+ return (
135
+ <DropdownMenuContent
136
+ data-slot="dropdown-menu-sub-content"
137
+ className={cn("w-auto min-w-[96px] rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
138
+ align={align}
139
+ alignOffset={alignOffset}
140
+ side={side}
141
+ sideOffset={sideOffset}
142
+ {...props}
143
+ />
144
+ )
145
+ }
146
+
147
+ function DropdownMenuCheckboxItem({
148
+ className,
149
+ children,
150
+ checked,
151
+ inset,
152
+ ...props
153
+ }: MenuPrimitive.CheckboxItem.Props & {
154
+ inset?: boolean
155
+ }) {
156
+ return (
157
+ <MenuPrimitive.CheckboxItem
158
+ data-slot="dropdown-menu-checkbox-item"
159
+ data-inset={inset}
160
+ className={cn(
161
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
162
+ className
163
+ )}
164
+ checked={checked}
165
+ {...props}
166
+ >
167
+ <span
168
+ className="pointer-events-none absolute right-2 flex items-center justify-center"
169
+ data-slot="dropdown-menu-checkbox-item-indicator"
170
+ >
171
+ <MenuPrimitive.CheckboxItemIndicator>
172
+ <CheckIcon
173
+ />
174
+ </MenuPrimitive.CheckboxItemIndicator>
175
+ </span>
176
+ {children}
177
+ </MenuPrimitive.CheckboxItem>
178
+ )
179
+ }
180
+
181
+ function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
182
+ return (
183
+ <MenuPrimitive.RadioGroup
184
+ data-slot="dropdown-menu-radio-group"
185
+ {...props}
186
+ />
187
+ )
188
+ }
189
+
190
+ function DropdownMenuRadioItem({
191
+ className,
192
+ children,
193
+ inset,
194
+ ...props
195
+ }: MenuPrimitive.RadioItem.Props & {
196
+ inset?: boolean
197
+ }) {
198
+ return (
199
+ <MenuPrimitive.RadioItem
200
+ data-slot="dropdown-menu-radio-item"
201
+ data-inset={inset}
202
+ className={cn(
203
+ "relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
204
+ className
205
+ )}
206
+ {...props}
207
+ >
208
+ <span
209
+ className="pointer-events-none absolute right-2 flex items-center justify-center"
210
+ data-slot="dropdown-menu-radio-item-indicator"
211
+ >
212
+ <MenuPrimitive.RadioItemIndicator>
213
+ <CheckIcon
214
+ />
215
+ </MenuPrimitive.RadioItemIndicator>
216
+ </span>
217
+ {children}
218
+ </MenuPrimitive.RadioItem>
219
+ )
220
+ }
221
+
222
+ function DropdownMenuSeparator({
223
+ className,
224
+ ...props
225
+ }: MenuPrimitive.Separator.Props) {
226
+ return (
227
+ <MenuPrimitive.Separator
228
+ data-slot="dropdown-menu-separator"
229
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
230
+ {...props}
231
+ />
232
+ )
233
+ }
234
+
235
+ function DropdownMenuShortcut({
236
+ className,
237
+ ...props
238
+ }: React.ComponentProps<"span">) {
239
+ return (
240
+ <span
241
+ data-slot="dropdown-menu-shortcut"
242
+ className={cn(
243
+ "ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground",
244
+ className
245
+ )}
246
+ {...props}
247
+ />
248
+ )
249
+ }
250
+
251
+ export {
252
+ DropdownMenu,
253
+ DropdownMenuPortal,
254
+ DropdownMenuTrigger,
255
+ DropdownMenuContent,
256
+ DropdownMenuGroup,
257
+ DropdownMenuLabel,
258
+ DropdownMenuItem,
259
+ DropdownMenuCheckboxItem,
260
+ DropdownMenuRadioGroup,
261
+ DropdownMenuRadioItem,
262
+ DropdownMenuSeparator,
263
+ DropdownMenuShortcut,
264
+ DropdownMenuSub,
265
+ DropdownMenuSubTrigger,
266
+ DropdownMenuSubContent,
267
+ }
@@ -0,0 +1,238 @@
1
+ "use client"
2
+
3
+ import { useMemo } from "react"
4
+ import { cva, type VariantProps } from "class-variance-authority"
5
+ import { cn } from "cn"
6
+
7
+ import { Label } from "@/components/ui/label"
8
+ import { Separator } from "@/components/ui/separator"
9
+
10
+ function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
11
+ return (
12
+ <fieldset
13
+ data-slot="field-set"
14
+ className={cn(
15
+ "flex flex-col gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
16
+ className
17
+ )}
18
+ {...props}
19
+ />
20
+ )
21
+ }
22
+
23
+ function FieldLegend({
24
+ className,
25
+ variant = "legend",
26
+ ...props
27
+ }: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
28
+ return (
29
+ <legend
30
+ data-slot="field-legend"
31
+ data-variant={variant}
32
+ className={cn(
33
+ "mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
34
+ className
35
+ )}
36
+ {...props}
37
+ />
38
+ )
39
+ }
40
+
41
+ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
42
+ return (
43
+ <div
44
+ data-slot="field-group"
45
+ className={cn(
46
+ "group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
47
+ className
48
+ )}
49
+ {...props}
50
+ />
51
+ )
52
+ }
53
+
54
+ const fieldVariants = cva(
55
+ "group/field flex w-full gap-2 data-[invalid=true]:text-destructive",
56
+ {
57
+ variants: {
58
+ orientation: {
59
+ vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
60
+ horizontal:
61
+ "flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
62
+ responsive:
63
+ "flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
64
+ },
65
+ },
66
+ defaultVariants: {
67
+ orientation: "vertical",
68
+ },
69
+ }
70
+ )
71
+
72
+ function Field({
73
+ className,
74
+ orientation = "vertical",
75
+ ...props
76
+ }: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
77
+ return (
78
+ <div
79
+ role="group"
80
+ data-slot="field"
81
+ data-orientation={orientation}
82
+ className={cn(fieldVariants({ orientation }), className)}
83
+ {...props}
84
+ />
85
+ )
86
+ }
87
+
88
+ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
89
+ return (
90
+ <div
91
+ data-slot="field-content"
92
+ className={cn(
93
+ "group/field-content flex flex-1 flex-col gap-0.5 leading-snug",
94
+ className
95
+ )}
96
+ {...props}
97
+ />
98
+ )
99
+ }
100
+
101
+ function FieldLabel({
102
+ className,
103
+ ...props
104
+ }: React.ComponentProps<typeof Label>) {
105
+ return (
106
+ <Label
107
+ data-slot="field-label"
108
+ className={cn(
109
+ "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-muted/50 has-[>[data-slot=field]]:has-[:focus-visible]:border-ring has-[>[data-slot=field]]:has-[:focus-visible]:ring-3 has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50 *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10",
110
+ "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
111
+ className
112
+ )}
113
+ {...props}
114
+ />
115
+ )
116
+ }
117
+
118
+ function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
119
+ return (
120
+ <div
121
+ data-slot="field-label"
122
+ className={cn(
123
+ "flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50",
124
+ className
125
+ )}
126
+ {...props}
127
+ />
128
+ )
129
+ }
130
+
131
+ function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
132
+ return (
133
+ <p
134
+ data-slot="field-description"
135
+ className={cn(
136
+ "text-left text-sm leading-normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5",
137
+ "last:mt-0 nth-last-2:-mt-1",
138
+ "[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
139
+ className
140
+ )}
141
+ {...props}
142
+ />
143
+ )
144
+ }
145
+
146
+ function FieldSeparator({
147
+ children,
148
+ className,
149
+ ...props
150
+ }: React.ComponentProps<"div"> & {
151
+ children?: React.ReactNode
152
+ }) {
153
+ return (
154
+ <div
155
+ data-slot="field-separator"
156
+ data-content={!!children}
157
+ className={cn(
158
+ "relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
159
+ className
160
+ )}
161
+ {...props}
162
+ >
163
+ <Separator className="absolute inset-0 top-1/2" />
164
+ {children && (
165
+ <span
166
+ className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
167
+ data-slot="field-separator-content"
168
+ >
169
+ {children}
170
+ </span>
171
+ )}
172
+ </div>
173
+ )
174
+ }
175
+
176
+ function FieldError({
177
+ className,
178
+ children,
179
+ errors,
180
+ ...props
181
+ }: React.ComponentProps<"div"> & {
182
+ errors?: Array<{ message?: string } | undefined>
183
+ }) {
184
+ const content = useMemo(() => {
185
+ if (children) {
186
+ return children
187
+ }
188
+
189
+ if (!errors?.length) {
190
+ return null
191
+ }
192
+
193
+ const uniqueErrors = [
194
+ ...new Map(errors.map((error) => [error?.message, error])).values(),
195
+ ]
196
+
197
+ if (uniqueErrors?.length == 1) {
198
+ return uniqueErrors[0]?.message
199
+ }
200
+
201
+ return (
202
+ <ul className="ml-4 flex list-disc flex-col gap-1">
203
+ {uniqueErrors.map(
204
+ (error, index) =>
205
+ error?.message && <li key={index}>{error.message}</li>
206
+ )}
207
+ </ul>
208
+ )
209
+ }, [children, errors])
210
+
211
+ if (!content) {
212
+ return null
213
+ }
214
+
215
+ return (
216
+ <div
217
+ role="alert"
218
+ data-slot="field-error"
219
+ className={cn("text-sm font-normal text-destructive", className)}
220
+ {...props}
221
+ >
222
+ {content}
223
+ </div>
224
+ )
225
+ }
226
+
227
+ export {
228
+ Field,
229
+ FieldLabel,
230
+ FieldDescription,
231
+ FieldError,
232
+ FieldGroup,
233
+ FieldLegend,
234
+ FieldSeparator,
235
+ FieldSet,
236
+ FieldContent,
237
+ FieldTitle,
238
+ }
@@ -0,0 +1,19 @@
1
+ import * as React from "react"
2
+ import { Input as InputPrimitive } from "@base-ui/react/input"
3
+ import { cn } from "cn"
4
+
5
+ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6
+ return (
7
+ <InputPrimitive
8
+ type={type}
9
+ data-slot="input"
10
+ className={cn(
11
+ "h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
12
+ className
13
+ )}
14
+ {...props}
15
+ />
16
+ )
17
+ }
18
+
19
+ export { Input }
@@ -0,0 +1,19 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "cn"
5
+
6
+ function Label({ className, ...props }: React.ComponentProps<"label">) {
7
+ return (
8
+ <label
9
+ data-slot="label"
10
+ className={cn(
11
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
12
+ className
13
+ )}
14
+ {...props}
15
+ />
16
+ )
17
+ }
18
+
19
+ export { Label }
@@ -0,0 +1,22 @@
1
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
2
+ import { cn } from "cn"
3
+
4
+ function Separator({
5
+ className,
6
+ orientation = "horizontal",
7
+ ...props
8
+ }: SeparatorPrimitive.Props) {
9
+ return (
10
+ <SeparatorPrimitive
11
+ data-slot="separator"
12
+ orientation={orientation}
13
+ className={cn(
14
+ "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
15
+ className
16
+ )}
17
+ {...props}
18
+ />
19
+ )
20
+ }
21
+
22
+ export { Separator }
@@ -0,0 +1,136 @@
1
+ import * as React from "react"
2
+ import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"
3
+ import { cn } from "cn"
4
+
5
+ import { Button } from "@/components/ui/button"
6
+ import { XIcon } from "lucide-react"
7
+
8
+ function Sheet({ ...props }: SheetPrimitive.Root.Props) {
9
+ return <SheetPrimitive.Root data-slot="sheet" {...props} />
10
+ }
11
+
12
+ function SheetTrigger({ ...props }: SheetPrimitive.Trigger.Props) {
13
+ return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
14
+ }
15
+
16
+ function SheetClose({ ...props }: SheetPrimitive.Close.Props) {
17
+ return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
18
+ }
19
+
20
+ function SheetPortal({ ...props }: SheetPrimitive.Portal.Props) {
21
+ return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
22
+ }
23
+
24
+ function SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) {
25
+ return (
26
+ <SheetPrimitive.Backdrop
27
+ data-slot="sheet-overlay"
28
+ className={cn(
29
+ "fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs",
30
+ className
31
+ )}
32
+ {...props}
33
+ />
34
+ )
35
+ }
36
+
37
+ function SheetContent({
38
+ className,
39
+ children,
40
+ side = "right",
41
+ showCloseButton = true,
42
+ ...props
43
+ }: SheetPrimitive.Popup.Props & {
44
+ side?: "top" | "right" | "bottom" | "left"
45
+ showCloseButton?: boolean
46
+ }) {
47
+ return (
48
+ <SheetPortal>
49
+ <SheetOverlay />
50
+ <SheetPrimitive.Popup
51
+ data-slot="sheet-content"
52
+ data-side={side}
53
+ className={cn(
54
+ "fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm",
55
+ className
56
+ )}
57
+ {...props}
58
+ >
59
+ {children}
60
+ {showCloseButton && (
61
+ <SheetPrimitive.Close
62
+ data-slot="sheet-close"
63
+ render={
64
+ <Button
65
+ variant="ghost"
66
+ className="absolute top-3 right-3"
67
+ size="icon-sm"
68
+ />
69
+ }
70
+ >
71
+ <XIcon
72
+ />
73
+ <span className="sr-only">Close</span>
74
+ </SheetPrimitive.Close>
75
+ )}
76
+ </SheetPrimitive.Popup>
77
+ </SheetPortal>
78
+ )
79
+ }
80
+
81
+ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
82
+ return (
83
+ <div
84
+ data-slot="sheet-header"
85
+ className={cn("flex flex-col gap-0.5 p-4", className)}
86
+ {...props}
87
+ />
88
+ )
89
+ }
90
+
91
+ function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
92
+ return (
93
+ <div
94
+ data-slot="sheet-footer"
95
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
96
+ {...props}
97
+ />
98
+ )
99
+ }
100
+
101
+ function SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) {
102
+ return (
103
+ <SheetPrimitive.Title
104
+ data-slot="sheet-title"
105
+ className={cn(
106
+ "font-heading text-base font-medium text-foreground",
107
+ className
108
+ )}
109
+ {...props}
110
+ />
111
+ )
112
+ }
113
+
114
+ function SheetDescription({
115
+ className,
116
+ ...props
117
+ }: SheetPrimitive.Description.Props) {
118
+ return (
119
+ <SheetPrimitive.Description
120
+ data-slot="sheet-description"
121
+ className={cn("text-sm text-muted-foreground", className)}
122
+ {...props}
123
+ />
124
+ )
125
+ }
126
+
127
+ export {
128
+ Sheet,
129
+ SheetTrigger,
130
+ SheetClose,
131
+ SheetContent,
132
+ SheetHeader,
133
+ SheetFooter,
134
+ SheetTitle,
135
+ SheetDescription,
136
+ }