create-vexcms 0.0.3

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 (116) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +2409 -0
  3. package/package.json +37 -0
  4. package/templates/base-nextjs/.prettierignore +26 -0
  5. package/templates/base-nextjs/.prettierrc +7 -0
  6. package/templates/base-nextjs/README.md +256 -0
  7. package/templates/base-nextjs/_gitignore +2 -0
  8. package/templates/base-nextjs/components.json +24 -0
  9. package/templates/base-nextjs/convex/_generated/api.d.ts +125 -0
  10. package/templates/base-nextjs/convex/_generated/api.js +23 -0
  11. package/templates/base-nextjs/convex/_generated/dataModel.d.ts +60 -0
  12. package/templates/base-nextjs/convex/_generated/server.d.ts +143 -0
  13. package/templates/base-nextjs/convex/_generated/server.js +93 -0
  14. package/templates/base-nextjs/convex/auth/adapter/index.ts +230 -0
  15. package/templates/base-nextjs/convex/auth/adapter/utils.ts +547 -0
  16. package/templates/base-nextjs/convex/auth/api.ts +8 -0
  17. package/templates/base-nextjs/convex/auth/config.ts +10 -0
  18. package/templates/base-nextjs/convex/auth/db.ts +303 -0
  19. package/templates/base-nextjs/convex/auth/index.ts +14 -0
  20. package/templates/base-nextjs/convex/auth/options.ts +63 -0
  21. package/templates/base-nextjs/convex/auth/plugins/index.ts +20 -0
  22. package/templates/base-nextjs/convex/auth/sessions.ts +60 -0
  23. package/templates/base-nextjs/convex/auth.config.ts +7 -0
  24. package/templates/base-nextjs/convex/convex.config.ts +5 -0
  25. package/templates/base-nextjs/convex/http.ts +28 -0
  26. package/templates/base-nextjs/convex/schema.ts +3 -0
  27. package/templates/base-nextjs/convex/vex/auth.ts +38 -0
  28. package/templates/base-nextjs/convex/vex/collections.ts +321 -0
  29. package/templates/base-nextjs/convex/vex/firstUser.ts +134 -0
  30. package/templates/base-nextjs/convex/vex/helpers.ts +33 -0
  31. package/templates/base-nextjs/convex/vex/impersonation.ts +51 -0
  32. package/templates/base-nextjs/convex/vex/media.ts +246 -0
  33. package/templates/base-nextjs/convex/vex/migrate.ts +74 -0
  34. package/templates/base-nextjs/convex/vex/model/collections.ts +196 -0
  35. package/templates/base-nextjs/convex/vex/model/media.ts +87 -0
  36. package/templates/base-nextjs/convex/vex/model/versions.ts +254 -0
  37. package/templates/base-nextjs/convex/vex/previewSnapshot.ts +53 -0
  38. package/templates/base-nextjs/convex/vex/versions.ts +588 -0
  39. package/templates/base-nextjs/eslint.config.mjs +164 -0
  40. package/templates/base-nextjs/next.config.ts +10 -0
  41. package/templates/base-nextjs/package.json +67 -0
  42. package/templates/base-nextjs/postcss.config.mjs +7 -0
  43. package/templates/base-nextjs/public/favicons/favicon.ico +0 -0
  44. package/templates/base-nextjs/public/file.svg +1 -0
  45. package/templates/base-nextjs/public/globe.svg +1 -0
  46. package/templates/base-nextjs/public/next.svg +1 -0
  47. package/templates/base-nextjs/public/vercel.svg +1 -0
  48. package/templates/base-nextjs/public/window.svg +1 -0
  49. package/templates/base-nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/page.tsx +9 -0
  50. package/templates/base-nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/view.tsx +23 -0
  51. package/templates/base-nextjs/src/app/(frontend)/@auth/default.tsx +3 -0
  52. package/templates/base-nextjs/src/app/(frontend)/@auth/page.tsx +3 -0
  53. package/templates/base-nextjs/src/app/(frontend)/auth/[pathname]/page.tsx +9 -0
  54. package/templates/base-nextjs/src/app/(frontend)/auth/[pathname]/view.tsx +11 -0
  55. package/templates/base-nextjs/src/app/(frontend)/layout.tsx +14 -0
  56. package/templates/base-nextjs/src/app/(frontend)/page.tsx +116 -0
  57. package/templates/base-nextjs/src/app/admin/AdminLayoutWrapper.tsx +33 -0
  58. package/templates/base-nextjs/src/app/admin/AdminPageWrapper.tsx +38 -0
  59. package/templates/base-nextjs/src/app/admin/RichTextFieldWithMedia.tsx +101 -0
  60. package/templates/base-nextjs/src/app/admin/[[...path]]/page.tsx +15 -0
  61. package/templates/base-nextjs/src/app/admin/layout.tsx +33 -0
  62. package/templates/base-nextjs/src/app/api/auth/[...all]/route.ts +6 -0
  63. package/templates/base-nextjs/src/app/favicon.ico +0 -0
  64. package/templates/base-nextjs/src/app/globals.css +130 -0
  65. package/templates/base-nextjs/src/app/layout.tsx +30 -0
  66. package/templates/base-nextjs/src/auth/client.tsx +41 -0
  67. package/templates/base-nextjs/src/auth/permissions.ts +102 -0
  68. package/templates/base-nextjs/src/auth/server.ts +16 -0
  69. package/templates/base-nextjs/src/auth/serverUtils.ts +64 -0
  70. package/templates/base-nextjs/src/auth/types.ts +3 -0
  71. package/templates/base-nextjs/src/components/component-example.tsx +474 -0
  72. package/templates/base-nextjs/src/components/example.tsx +52 -0
  73. package/templates/base-nextjs/src/components/providers/client.tsx +9 -0
  74. package/templates/base-nextjs/src/components/providers/convex.tsx +32 -0
  75. package/templates/base-nextjs/src/components/providers/server.tsx +15 -0
  76. package/templates/base-nextjs/src/components/providers/theme.tsx +11 -0
  77. package/templates/base-nextjs/src/components/ui/alert-dialog.tsx +162 -0
  78. package/templates/base-nextjs/src/components/ui/badge.tsx +52 -0
  79. package/templates/base-nextjs/src/components/ui/button.tsx +60 -0
  80. package/templates/base-nextjs/src/components/ui/card.tsx +92 -0
  81. package/templates/base-nextjs/src/components/ui/combobox.tsx +271 -0
  82. package/templates/base-nextjs/src/components/ui/dialog.tsx +135 -0
  83. package/templates/base-nextjs/src/components/ui/dropdown-menu.tsx +246 -0
  84. package/templates/base-nextjs/src/components/ui/field.tsx +224 -0
  85. package/templates/base-nextjs/src/components/ui/input-group.tsx +146 -0
  86. package/templates/base-nextjs/src/components/ui/input.tsx +20 -0
  87. package/templates/base-nextjs/src/components/ui/label.tsx +20 -0
  88. package/templates/base-nextjs/src/components/ui/select.tsx +189 -0
  89. package/templates/base-nextjs/src/components/ui/separator.tsx +21 -0
  90. package/templates/base-nextjs/src/components/ui/textarea.tsx +18 -0
  91. package/templates/base-nextjs/src/components/ui/theme-toggle.tsx +67 -0
  92. package/templates/base-nextjs/src/db/constants/auth.ts +8 -0
  93. package/templates/base-nextjs/src/db/constants/index.ts +44 -0
  94. package/templates/base-nextjs/src/db/types.ts +6 -0
  95. package/templates/base-nextjs/src/env.mjs +48 -0
  96. package/templates/base-nextjs/src/lib/utils.ts +6 -0
  97. package/templates/base-nextjs/src/proxy.ts +23 -0
  98. package/templates/base-nextjs/src/vexcms/access.ts +28 -0
  99. package/templates/base-nextjs/src/vexcms/auth.ts +4 -0
  100. package/templates/base-nextjs/src/vexcms/collections/index.ts +1 -0
  101. package/templates/base-nextjs/src/vexcms/collections/media.ts +15 -0
  102. package/templates/base-nextjs/src/vexcms/collections/users.ts +46 -0
  103. package/templates/base-nextjs/tsconfig.json +60 -0
  104. package/templates/base-nextjs/vex.config.ts +23 -0
  105. package/templates/marketing-site/convex/pages.ts +46 -0
  106. package/templates/marketing-site/src/app/(frontend)/[slug]/page.tsx +58 -0
  107. package/templates/marketing-site/src/app/(frontend)/preview/[slug]/page.tsx +69 -0
  108. package/templates/marketing-site/src/app/admin/AdminLayoutWrapper.tsx +71 -0
  109. package/templates/marketing-site/src/db/constants/index.ts +52 -0
  110. package/templates/marketing-site/src/vexcms/collections/footers.ts +28 -0
  111. package/templates/marketing-site/src/vexcms/collections/headers.ts +35 -0
  112. package/templates/marketing-site/src/vexcms/collections/index.ts +7 -0
  113. package/templates/marketing-site/src/vexcms/collections/pages.ts +40 -0
  114. package/templates/marketing-site/src/vexcms/collections/site_settings.ts +31 -0
  115. package/templates/marketing-site/src/vexcms/collections/themes.ts +37 -0
  116. package/templates/marketing-site/vex.config.ts +32 -0
@@ -0,0 +1,162 @@
1
+ "use client"
2
+
3
+ import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"
4
+ import * as React from "react"
5
+
6
+ import { Button } from "~/components/ui/button"
7
+ import { cn } from "~/lib/utils"
8
+
9
+ function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
10
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
11
+ }
12
+
13
+ function AlertDialogAction({ className, ...props }: React.ComponentProps<typeof Button>) {
14
+ return <Button className={cn(className)} data-slot="alert-dialog-action" {...props} />
15
+ }
16
+
17
+ function AlertDialogCancel({
18
+ className,
19
+ size = "default",
20
+ variant = "outline",
21
+ ...props
22
+ }: AlertDialogPrimitive.Close.Props &
23
+ Pick<React.ComponentProps<typeof Button>, "size" | "variant">) {
24
+ return (
25
+ <AlertDialogPrimitive.Close
26
+ className={cn(className)}
27
+ data-slot="alert-dialog-cancel"
28
+ render={<Button size={size} variant={variant} />}
29
+ {...props}
30
+ />
31
+ )
32
+ }
33
+
34
+ function AlertDialogContent({
35
+ className,
36
+ size = "default",
37
+ ...props
38
+ }: AlertDialogPrimitive.Popup.Props & {
39
+ size?: "default" | "sm"
40
+ }) {
41
+ return (
42
+ <AlertDialogPortal>
43
+ <AlertDialogOverlay />
44
+ <AlertDialogPrimitive.Popup
45
+ className={cn(
46
+ "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 bg-background ring-foreground/10 gap-6 rounded-xl p-6 ring-1 duration-100 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-lg group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 outline-none",
47
+ className
48
+ )}
49
+ data-size={size}
50
+ data-slot="alert-dialog-content"
51
+ {...props}
52
+ />
53
+ </AlertDialogPortal>
54
+ )
55
+ }
56
+
57
+ function AlertDialogDescription({
58
+ className,
59
+ ...props
60
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
61
+ return (
62
+ <AlertDialogPrimitive.Description
63
+ className={cn(
64
+ "text-muted-foreground *:[a]:hover:text-foreground text-sm text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3",
65
+ className
66
+ )}
67
+ data-slot="alert-dialog-description"
68
+ {...props}
69
+ />
70
+ )
71
+ }
72
+
73
+ function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">) {
74
+ return (
75
+ <div
76
+ className={cn(
77
+ "flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
78
+ className
79
+ )}
80
+ data-slot="alert-dialog-footer"
81
+ {...props}
82
+ />
83
+ )
84
+ }
85
+
86
+ function AlertDialogHeader({ className, ...props }: React.ComponentProps<"div">) {
87
+ return (
88
+ <div
89
+ className={cn(
90
+ "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
91
+ className
92
+ )}
93
+ data-slot="alert-dialog-header"
94
+ {...props}
95
+ />
96
+ )
97
+ }
98
+
99
+ function AlertDialogMedia({ className, ...props }: React.ComponentProps<"div">) {
100
+ return (
101
+ <div
102
+ className={cn(
103
+ "bg-muted mb-2 inline-flex size-16 items-center justify-center rounded-md sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
104
+ className
105
+ )}
106
+ data-slot="alert-dialog-media"
107
+ {...props}
108
+ />
109
+ )
110
+ }
111
+
112
+ function AlertDialogOverlay({ className, ...props }: AlertDialogPrimitive.Backdrop.Props) {
113
+ return (
114
+ <AlertDialogPrimitive.Backdrop
115
+ className={cn(
116
+ "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50",
117
+ className
118
+ )}
119
+ data-slot="alert-dialog-overlay"
120
+ {...props}
121
+ />
122
+ )
123
+ }
124
+
125
+ function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
126
+ return <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
127
+ }
128
+
129
+ function AlertDialogTitle({
130
+ className,
131
+ ...props
132
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
133
+ return (
134
+ <AlertDialogPrimitive.Title
135
+ className={cn(
136
+ "text-lg font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
137
+ className
138
+ )}
139
+ data-slot="alert-dialog-title"
140
+ {...props}
141
+ />
142
+ )
143
+ }
144
+
145
+ function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
146
+ return <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
147
+ }
148
+
149
+ export {
150
+ AlertDialog,
151
+ AlertDialogAction,
152
+ AlertDialogCancel,
153
+ AlertDialogContent,
154
+ AlertDialogDescription,
155
+ AlertDialogFooter,
156
+ AlertDialogHeader,
157
+ AlertDialogMedia,
158
+ AlertDialogOverlay,
159
+ AlertDialogPortal,
160
+ AlertDialogTitle,
161
+ AlertDialogTrigger,
162
+ }
@@ -0,0 +1,52 @@
1
+ import { mergeProps } from "@base-ui/react/merge-props"
2
+ import { useRender } from "@base-ui/react/use-render"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "~/lib/utils"
6
+
7
+ const badgeVariants = cva(
8
+ "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
13
+ secondary:
14
+ "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
15
+ destructive:
16
+ "bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
17
+ outline:
18
+ "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
19
+ ghost:
20
+ "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ },
23
+ },
24
+ defaultVariants: {
25
+ variant: "default",
26
+ },
27
+ }
28
+ )
29
+
30
+ function Badge({
31
+ className,
32
+ variant = "default",
33
+ render,
34
+ ...props
35
+ }: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
36
+ return useRender({
37
+ defaultTagName: "span",
38
+ props: mergeProps<"span">(
39
+ {
40
+ className: cn(badgeVariants({ variant }), className),
41
+ },
42
+ props
43
+ ),
44
+ render,
45
+ state: {
46
+ slot: "badge",
47
+ variant,
48
+ },
49
+ })
50
+ }
51
+
52
+ export { Badge, badgeVariants }
@@ -0,0 +1,60 @@
1
+ "use client"
2
+
3
+ import { Button as ButtonPrimitive } from "@base-ui/react/button"
4
+ import { cva, type VariantProps } from "class-variance-authority"
5
+
6
+ import { cn } from "~/lib/utils"
7
+
8
+ const buttonVariants = cva(
9
+ "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-md border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none",
10
+ {
11
+ defaultVariants: {
12
+ size: "default",
13
+ variant: "default",
14
+ },
15
+ variants: {
16
+ size: {
17
+ default:
18
+ "h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
19
+ icon: "size-9",
20
+ "icon-lg": "size-10",
21
+ "icon-sm":
22
+ "size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md",
23
+ "icon-xs":
24
+ "size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3",
25
+ lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
26
+ sm: "h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
27
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
28
+ },
29
+ variant: {
30
+ default: "bg-primary text-primary-foreground hover:bg-primary/80",
31
+ destructive:
32
+ "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
33
+ ghost:
34
+ "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
35
+ link: "text-primary underline-offset-4 hover:underline",
36
+ outline:
37
+ "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground shadow-xs",
38
+ secondary:
39
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
40
+ },
41
+ },
42
+ }
43
+ )
44
+
45
+ function Button({
46
+ className,
47
+ size = "default",
48
+ variant = "default",
49
+ ...props
50
+ }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
51
+ return (
52
+ <ButtonPrimitive
53
+ className={cn(buttonVariants({ className, size, variant }))}
54
+ data-slot="button"
55
+ {...props}
56
+ />
57
+ )
58
+ }
59
+
60
+ export { Button, buttonVariants }
@@ -0,0 +1,92 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "~/lib/utils"
4
+
5
+ function Card({
6
+ className,
7
+ size = "default",
8
+ ...props
9
+ }: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
10
+ return (
11
+ <div
12
+ className={cn(
13
+ "ring-foreground/10 bg-card text-card-foreground gap-6 overflow-hidden rounded-xl py-6 text-sm shadow-xs ring-1 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col",
14
+ className
15
+ )}
16
+ data-size={size}
17
+ data-slot="card"
18
+ {...props}
19
+ />
20
+ )
21
+ }
22
+
23
+ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
24
+ return (
25
+ <div
26
+ className={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
27
+ data-slot="card-action"
28
+ {...props}
29
+ />
30
+ )
31
+ }
32
+
33
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
34
+ return (
35
+ <div
36
+ className={cn("px-6 group-data-[size=sm]/card:px-4", className)}
37
+ data-slot="card-content"
38
+ {...props}
39
+ />
40
+ )
41
+ }
42
+
43
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
44
+ return (
45
+ <div
46
+ className={cn("text-muted-foreground text-sm", className)}
47
+ data-slot="card-description"
48
+ {...props}
49
+ />
50
+ )
51
+ }
52
+
53
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
54
+ return (
55
+ <div
56
+ className={cn(
57
+ "rounded-b-xl px-6 group-data-[size=sm]/card:px-4 [.border-t]:pt-6 group-data-[size=sm]/card:[.border-t]:pt-4 flex items-center",
58
+ className
59
+ )}
60
+ data-slot="card-footer"
61
+ {...props}
62
+ />
63
+ )
64
+ }
65
+
66
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
67
+ return (
68
+ <div
69
+ className={cn(
70
+ "gap-1 rounded-t-xl px-6 group-data-[size=sm]/card:px-4 [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]",
71
+ className
72
+ )}
73
+ data-slot="card-header"
74
+ {...props}
75
+ />
76
+ )
77
+ }
78
+
79
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
80
+ return (
81
+ <div
82
+ className={cn(
83
+ "text-base leading-normal font-medium group-data-[size=sm]/card:text-sm",
84
+ className
85
+ )}
86
+ data-slot="card-title"
87
+ {...props}
88
+ />
89
+ )
90
+ }
91
+
92
+ export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }
@@ -0,0 +1,271 @@
1
+ "use client"
2
+
3
+ import { Combobox as ComboboxPrimitive } from "@base-ui/react"
4
+ import { CheckIcon, ChevronDownIcon, XIcon } from "lucide-react"
5
+ import * as React from "react"
6
+
7
+ import { Button } from "~/components/ui/button"
8
+ import {
9
+ InputGroup,
10
+ InputGroupAddon,
11
+ InputGroupButton,
12
+ InputGroupInput,
13
+ } from "~/components/ui/input-group"
14
+ import { cn } from "~/lib/utils"
15
+
16
+ const Combobox = ComboboxPrimitive.Root
17
+
18
+ function ComboboxChip({
19
+ children,
20
+ className,
21
+ showRemove = true,
22
+ ...props
23
+ }: ComboboxPrimitive.Chip.Props & {
24
+ showRemove?: boolean
25
+ }) {
26
+ return (
27
+ <ComboboxPrimitive.Chip
28
+ className={cn(
29
+ "bg-muted text-foreground flex h-[calc(--spacing(5.5))] w-fit items-center justify-center gap-1 rounded-sm px-1.5 text-xs font-medium whitespace-nowrap has-data-[slot=combobox-chip-remove]:pr-0 has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50",
30
+ className
31
+ )}
32
+ data-slot="combobox-chip"
33
+ {...props}
34
+ >
35
+ {children}
36
+ {showRemove && (
37
+ <ComboboxPrimitive.ChipRemove
38
+ className="-ml-1 opacity-50 hover:opacity-100"
39
+ data-slot="combobox-chip-remove"
40
+ render={<Button size="icon-xs" variant="ghost" />}
41
+ >
42
+ <XIcon className="pointer-events-none" />
43
+ </ComboboxPrimitive.ChipRemove>
44
+ )}
45
+ </ComboboxPrimitive.Chip>
46
+ )
47
+ }
48
+
49
+ function ComboboxChips({
50
+ className,
51
+ ...props
52
+ }: ComboboxPrimitive.Chips.Props & React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips>) {
53
+ return (
54
+ <ComboboxPrimitive.Chips
55
+ className={cn(
56
+ "dark:bg-input/30 border-input focus-within:border-ring focus-within:ring-ring/50 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive dark:has-aria-invalid:border-destructive/50 flex min-h-9 flex-wrap items-center gap-1.5 rounded-md border bg-transparent bg-clip-padding px-2.5 py-1.5 text-sm shadow-xs transition-[color,box-shadow] focus-within:ring-[3px] has-aria-invalid:ring-[3px] has-data-[slot=combobox-chip]:px-1.5",
57
+ className
58
+ )}
59
+ data-slot="combobox-chips"
60
+ {...props}
61
+ />
62
+ )
63
+ }
64
+
65
+ function ComboboxChipsInput({ className, ...props }: ComboboxPrimitive.Input.Props) {
66
+ return (
67
+ <ComboboxPrimitive.Input
68
+ className={cn("min-w-16 flex-1 outline-none", className)}
69
+ data-slot="combobox-chip-input"
70
+ {...props}
71
+ />
72
+ )
73
+ }
74
+
75
+ function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
76
+ return (
77
+ <ComboboxPrimitive.Clear
78
+ className={cn(className)}
79
+ data-slot="combobox-clear"
80
+ render={<InputGroupButton size="icon-xs" variant="ghost" />}
81
+ {...props}
82
+ >
83
+ <XIcon className="pointer-events-none" />
84
+ </ComboboxPrimitive.Clear>
85
+ )
86
+ }
87
+
88
+ function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
89
+ return <ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} />
90
+ }
91
+
92
+ function ComboboxContent({
93
+ align = "start",
94
+ alignOffset = 0,
95
+ anchor,
96
+ className,
97
+ side = "bottom",
98
+ sideOffset = 6,
99
+ ...props
100
+ }: ComboboxPrimitive.Popup.Props &
101
+ Pick<
102
+ ComboboxPrimitive.Positioner.Props,
103
+ "align" | "alignOffset" | "anchor" | "side" | "sideOffset"
104
+ >) {
105
+ return (
106
+ <ComboboxPrimitive.Portal>
107
+ <ComboboxPrimitive.Positioner
108
+ align={align}
109
+ alignOffset={alignOffset}
110
+ anchor={anchor}
111
+ className="isolate z-50"
112
+ side={side}
113
+ sideOffset={sideOffset}
114
+ >
115
+ <ComboboxPrimitive.Popup
116
+ className={cn(
117
+ "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-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 ring-foreground/10 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:border-input/30 max-h-72 min-w-36 overflow-hidden rounded-md shadow-md ring-1 duration-100 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:shadow-none group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) data-[chips=true]:min-w-(--anchor-width)",
118
+ className
119
+ )}
120
+ data-chips={!!anchor}
121
+ data-slot="combobox-content"
122
+ {...props}
123
+ />
124
+ </ComboboxPrimitive.Positioner>
125
+ </ComboboxPrimitive.Portal>
126
+ )
127
+ }
128
+
129
+ function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
130
+ return (
131
+ <ComboboxPrimitive.Empty
132
+ className={cn(
133
+ "text-muted-foreground hidden w-full justify-center py-2 text-center text-sm group-data-empty/combobox-content:flex",
134
+ className
135
+ )}
136
+ data-slot="combobox-empty"
137
+ {...props}
138
+ />
139
+ )
140
+ }
141
+
142
+ function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
143
+ return <ComboboxPrimitive.Group className={cn(className)} data-slot="combobox-group" {...props} />
144
+ }
145
+
146
+ function ComboboxInput({
147
+ children,
148
+ className,
149
+ disabled = false,
150
+ showClear = false,
151
+ showTrigger = true,
152
+ ...props
153
+ }: ComboboxPrimitive.Input.Props & {
154
+ showClear?: boolean
155
+ showTrigger?: boolean
156
+ }) {
157
+ return (
158
+ <InputGroup className={cn("w-auto", className)}>
159
+ <ComboboxPrimitive.Input render={<InputGroupInput disabled={disabled} />} {...props} />
160
+ <InputGroupAddon align="inline-end">
161
+ {showTrigger && (
162
+ <InputGroupButton
163
+ className="group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent"
164
+ data-slot="input-group-button"
165
+ disabled={disabled}
166
+ render={<ComboboxTrigger />}
167
+ size="icon-xs"
168
+ variant="ghost"
169
+ />
170
+ )}
171
+ {showClear && <ComboboxClear disabled={disabled} />}
172
+ </InputGroupAddon>
173
+ {children}
174
+ </InputGroup>
175
+ )
176
+ }
177
+
178
+ function ComboboxItem({ children, className, ...props }: ComboboxPrimitive.Item.Props) {
179
+ return (
180
+ <ComboboxPrimitive.Item
181
+ className={cn(
182
+ "data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex w-full cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
183
+ className
184
+ )}
185
+ data-slot="combobox-item"
186
+ {...props}
187
+ >
188
+ {children}
189
+ <ComboboxPrimitive.ItemIndicator
190
+ render={
191
+ <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
192
+ }
193
+ >
194
+ <CheckIcon className="pointer-events-none" />
195
+ </ComboboxPrimitive.ItemIndicator>
196
+ </ComboboxPrimitive.Item>
197
+ )
198
+ }
199
+
200
+ function ComboboxLabel({ className, ...props }: ComboboxPrimitive.GroupLabel.Props) {
201
+ return (
202
+ <ComboboxPrimitive.GroupLabel
203
+ className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
204
+ data-slot="combobox-label"
205
+ {...props}
206
+ />
207
+ )
208
+ }
209
+
210
+ function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
211
+ return (
212
+ <ComboboxPrimitive.List
213
+ className={cn(
214
+ "no-scrollbar max-h-[min(calc(--spacing(72)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto p-1 data-empty:p-0 overflow-y-auto overscroll-contain",
215
+ className
216
+ )}
217
+ data-slot="combobox-list"
218
+ {...props}
219
+ />
220
+ )
221
+ }
222
+
223
+ function ComboboxSeparator({ className, ...props }: ComboboxPrimitive.Separator.Props) {
224
+ return (
225
+ <ComboboxPrimitive.Separator
226
+ className={cn("bg-border -mx-1 my-1 h-px", className)}
227
+ data-slot="combobox-separator"
228
+ {...props}
229
+ />
230
+ )
231
+ }
232
+
233
+ function ComboboxTrigger({ children, className, ...props }: ComboboxPrimitive.Trigger.Props) {
234
+ return (
235
+ <ComboboxPrimitive.Trigger
236
+ className={cn("[&_svg:not([class*='size-'])]:size-4", className)}
237
+ data-slot="combobox-trigger"
238
+ {...props}
239
+ >
240
+ {children}
241
+ <ChevronDownIcon className="text-muted-foreground size-4 pointer-events-none" />
242
+ </ComboboxPrimitive.Trigger>
243
+ )
244
+ }
245
+
246
+ function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
247
+ return <ComboboxPrimitive.Value data-slot="combobox-value" {...props} />
248
+ }
249
+
250
+ function useComboboxAnchor() {
251
+ return React.useRef<HTMLDivElement | null>(null)
252
+ }
253
+
254
+ export {
255
+ Combobox,
256
+ ComboboxChip,
257
+ ComboboxChips,
258
+ ComboboxChipsInput,
259
+ ComboboxCollection,
260
+ ComboboxContent,
261
+ ComboboxEmpty,
262
+ ComboboxGroup,
263
+ ComboboxInput,
264
+ ComboboxItem,
265
+ ComboboxLabel,
266
+ ComboboxList,
267
+ ComboboxSeparator,
268
+ ComboboxTrigger,
269
+ ComboboxValue,
270
+ useComboboxAnchor,
271
+ }