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.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2409 -0
- package/package.json +37 -0
- package/templates/base-nextjs/.prettierignore +26 -0
- package/templates/base-nextjs/.prettierrc +7 -0
- package/templates/base-nextjs/README.md +256 -0
- package/templates/base-nextjs/_gitignore +2 -0
- package/templates/base-nextjs/components.json +24 -0
- package/templates/base-nextjs/convex/_generated/api.d.ts +125 -0
- package/templates/base-nextjs/convex/_generated/api.js +23 -0
- package/templates/base-nextjs/convex/_generated/dataModel.d.ts +60 -0
- package/templates/base-nextjs/convex/_generated/server.d.ts +143 -0
- package/templates/base-nextjs/convex/_generated/server.js +93 -0
- package/templates/base-nextjs/convex/auth/adapter/index.ts +230 -0
- package/templates/base-nextjs/convex/auth/adapter/utils.ts +547 -0
- package/templates/base-nextjs/convex/auth/api.ts +8 -0
- package/templates/base-nextjs/convex/auth/config.ts +10 -0
- package/templates/base-nextjs/convex/auth/db.ts +303 -0
- package/templates/base-nextjs/convex/auth/index.ts +14 -0
- package/templates/base-nextjs/convex/auth/options.ts +63 -0
- package/templates/base-nextjs/convex/auth/plugins/index.ts +20 -0
- package/templates/base-nextjs/convex/auth/sessions.ts +60 -0
- package/templates/base-nextjs/convex/auth.config.ts +7 -0
- package/templates/base-nextjs/convex/convex.config.ts +5 -0
- package/templates/base-nextjs/convex/http.ts +28 -0
- package/templates/base-nextjs/convex/schema.ts +3 -0
- package/templates/base-nextjs/convex/vex/auth.ts +38 -0
- package/templates/base-nextjs/convex/vex/collections.ts +321 -0
- package/templates/base-nextjs/convex/vex/firstUser.ts +134 -0
- package/templates/base-nextjs/convex/vex/helpers.ts +33 -0
- package/templates/base-nextjs/convex/vex/impersonation.ts +51 -0
- package/templates/base-nextjs/convex/vex/media.ts +246 -0
- package/templates/base-nextjs/convex/vex/migrate.ts +74 -0
- package/templates/base-nextjs/convex/vex/model/collections.ts +196 -0
- package/templates/base-nextjs/convex/vex/model/media.ts +87 -0
- package/templates/base-nextjs/convex/vex/model/versions.ts +254 -0
- package/templates/base-nextjs/convex/vex/previewSnapshot.ts +53 -0
- package/templates/base-nextjs/convex/vex/versions.ts +588 -0
- package/templates/base-nextjs/eslint.config.mjs +164 -0
- package/templates/base-nextjs/next.config.ts +10 -0
- package/templates/base-nextjs/package.json +67 -0
- package/templates/base-nextjs/postcss.config.mjs +7 -0
- package/templates/base-nextjs/public/favicons/favicon.ico +0 -0
- package/templates/base-nextjs/public/file.svg +1 -0
- package/templates/base-nextjs/public/globe.svg +1 -0
- package/templates/base-nextjs/public/next.svg +1 -0
- package/templates/base-nextjs/public/vercel.svg +1 -0
- package/templates/base-nextjs/public/window.svg +1 -0
- package/templates/base-nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/page.tsx +9 -0
- package/templates/base-nextjs/src/app/(frontend)/@auth/(...)auth/[pathname]/view.tsx +23 -0
- package/templates/base-nextjs/src/app/(frontend)/@auth/default.tsx +3 -0
- package/templates/base-nextjs/src/app/(frontend)/@auth/page.tsx +3 -0
- package/templates/base-nextjs/src/app/(frontend)/auth/[pathname]/page.tsx +9 -0
- package/templates/base-nextjs/src/app/(frontend)/auth/[pathname]/view.tsx +11 -0
- package/templates/base-nextjs/src/app/(frontend)/layout.tsx +14 -0
- package/templates/base-nextjs/src/app/(frontend)/page.tsx +116 -0
- package/templates/base-nextjs/src/app/admin/AdminLayoutWrapper.tsx +33 -0
- package/templates/base-nextjs/src/app/admin/AdminPageWrapper.tsx +38 -0
- package/templates/base-nextjs/src/app/admin/RichTextFieldWithMedia.tsx +101 -0
- package/templates/base-nextjs/src/app/admin/[[...path]]/page.tsx +15 -0
- package/templates/base-nextjs/src/app/admin/layout.tsx +33 -0
- package/templates/base-nextjs/src/app/api/auth/[...all]/route.ts +6 -0
- package/templates/base-nextjs/src/app/favicon.ico +0 -0
- package/templates/base-nextjs/src/app/globals.css +130 -0
- package/templates/base-nextjs/src/app/layout.tsx +30 -0
- package/templates/base-nextjs/src/auth/client.tsx +41 -0
- package/templates/base-nextjs/src/auth/permissions.ts +102 -0
- package/templates/base-nextjs/src/auth/server.ts +16 -0
- package/templates/base-nextjs/src/auth/serverUtils.ts +64 -0
- package/templates/base-nextjs/src/auth/types.ts +3 -0
- package/templates/base-nextjs/src/components/component-example.tsx +474 -0
- package/templates/base-nextjs/src/components/example.tsx +52 -0
- package/templates/base-nextjs/src/components/providers/client.tsx +9 -0
- package/templates/base-nextjs/src/components/providers/convex.tsx +32 -0
- package/templates/base-nextjs/src/components/providers/server.tsx +15 -0
- package/templates/base-nextjs/src/components/providers/theme.tsx +11 -0
- package/templates/base-nextjs/src/components/ui/alert-dialog.tsx +162 -0
- package/templates/base-nextjs/src/components/ui/badge.tsx +52 -0
- package/templates/base-nextjs/src/components/ui/button.tsx +60 -0
- package/templates/base-nextjs/src/components/ui/card.tsx +92 -0
- package/templates/base-nextjs/src/components/ui/combobox.tsx +271 -0
- package/templates/base-nextjs/src/components/ui/dialog.tsx +135 -0
- package/templates/base-nextjs/src/components/ui/dropdown-menu.tsx +246 -0
- package/templates/base-nextjs/src/components/ui/field.tsx +224 -0
- package/templates/base-nextjs/src/components/ui/input-group.tsx +146 -0
- package/templates/base-nextjs/src/components/ui/input.tsx +20 -0
- package/templates/base-nextjs/src/components/ui/label.tsx +20 -0
- package/templates/base-nextjs/src/components/ui/select.tsx +189 -0
- package/templates/base-nextjs/src/components/ui/separator.tsx +21 -0
- package/templates/base-nextjs/src/components/ui/textarea.tsx +18 -0
- package/templates/base-nextjs/src/components/ui/theme-toggle.tsx +67 -0
- package/templates/base-nextjs/src/db/constants/auth.ts +8 -0
- package/templates/base-nextjs/src/db/constants/index.ts +44 -0
- package/templates/base-nextjs/src/db/types.ts +6 -0
- package/templates/base-nextjs/src/env.mjs +48 -0
- package/templates/base-nextjs/src/lib/utils.ts +6 -0
- package/templates/base-nextjs/src/proxy.ts +23 -0
- package/templates/base-nextjs/src/vexcms/access.ts +28 -0
- package/templates/base-nextjs/src/vexcms/auth.ts +4 -0
- package/templates/base-nextjs/src/vexcms/collections/index.ts +1 -0
- package/templates/base-nextjs/src/vexcms/collections/media.ts +15 -0
- package/templates/base-nextjs/src/vexcms/collections/users.ts +46 -0
- package/templates/base-nextjs/tsconfig.json +60 -0
- package/templates/base-nextjs/vex.config.ts +23 -0
- package/templates/marketing-site/convex/pages.ts +46 -0
- package/templates/marketing-site/src/app/(frontend)/[slug]/page.tsx +58 -0
- package/templates/marketing-site/src/app/(frontend)/preview/[slug]/page.tsx +69 -0
- package/templates/marketing-site/src/app/admin/AdminLayoutWrapper.tsx +71 -0
- package/templates/marketing-site/src/db/constants/index.ts +52 -0
- package/templates/marketing-site/src/vexcms/collections/footers.ts +28 -0
- package/templates/marketing-site/src/vexcms/collections/headers.ts +35 -0
- package/templates/marketing-site/src/vexcms/collections/index.ts +7 -0
- package/templates/marketing-site/src/vexcms/collections/pages.ts +40 -0
- package/templates/marketing-site/src/vexcms/collections/site_settings.ts +31 -0
- package/templates/marketing-site/src/vexcms/collections/themes.ts +37 -0
- 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
|
+
}
|