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,135 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
|
|
4
|
+
import { XIcon } from "lucide-react"
|
|
5
|
+
import * as React from "react"
|
|
6
|
+
|
|
7
|
+
import { Button } from "~/components/ui/button"
|
|
8
|
+
import { cn } from "~/lib/utils"
|
|
9
|
+
|
|
10
|
+
function Dialog({ ...props }: DialogPrimitive.Root.Props) {
|
|
11
|
+
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
|
|
15
|
+
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function DialogContent({
|
|
19
|
+
children,
|
|
20
|
+
className,
|
|
21
|
+
showCloseButton = true,
|
|
22
|
+
...props
|
|
23
|
+
}: DialogPrimitive.Popup.Props & {
|
|
24
|
+
showCloseButton?: boolean
|
|
25
|
+
}) {
|
|
26
|
+
return (
|
|
27
|
+
<DialogPortal>
|
|
28
|
+
<DialogOverlay />
|
|
29
|
+
<DialogPrimitive.Popup
|
|
30
|
+
className={cn(
|
|
31
|
+
"bg-background 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 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-6 rounded-xl p-6 text-sm ring-1 duration-100 sm:max-w-md fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none",
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
data-slot="dialog-content"
|
|
35
|
+
{...props}
|
|
36
|
+
>
|
|
37
|
+
{children}
|
|
38
|
+
{showCloseButton && (
|
|
39
|
+
<DialogPrimitive.Close
|
|
40
|
+
data-slot="dialog-close"
|
|
41
|
+
render={<Button className="absolute top-4 right-4" size="icon-sm" variant="ghost" />}
|
|
42
|
+
>
|
|
43
|
+
<XIcon />
|
|
44
|
+
<span className="sr-only">Close</span>
|
|
45
|
+
</DialogPrimitive.Close>
|
|
46
|
+
)}
|
|
47
|
+
</DialogPrimitive.Popup>
|
|
48
|
+
</DialogPortal>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function DialogDescription({ className, ...props }: DialogPrimitive.Description.Props) {
|
|
53
|
+
return (
|
|
54
|
+
<DialogPrimitive.Description
|
|
55
|
+
className={cn(
|
|
56
|
+
"text-muted-foreground *:[a]:hover:text-foreground text-sm *:[a]:underline *:[a]:underline-offset-3",
|
|
57
|
+
className
|
|
58
|
+
)}
|
|
59
|
+
data-slot="dialog-description"
|
|
60
|
+
{...props}
|
|
61
|
+
/>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function DialogFooter({
|
|
66
|
+
children,
|
|
67
|
+
className,
|
|
68
|
+
showCloseButton = false,
|
|
69
|
+
...props
|
|
70
|
+
}: React.ComponentProps<"div"> & {
|
|
71
|
+
showCloseButton?: boolean
|
|
72
|
+
}) {
|
|
73
|
+
return (
|
|
74
|
+
<div
|
|
75
|
+
className={cn("gap-2 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className)}
|
|
76
|
+
data-slot="dialog-footer"
|
|
77
|
+
{...props}
|
|
78
|
+
>
|
|
79
|
+
{children}
|
|
80
|
+
{showCloseButton && (
|
|
81
|
+
<DialogPrimitive.Close render={<Button variant="outline" />}>Close</DialogPrimitive.Close>
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
88
|
+
return (
|
|
89
|
+
<div className={cn("gap-2 flex flex-col", className)} data-slot="dialog-header" {...props} />
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function DialogOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props) {
|
|
94
|
+
return (
|
|
95
|
+
<DialogPrimitive.Backdrop
|
|
96
|
+
className={cn(
|
|
97
|
+
"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",
|
|
98
|
+
className
|
|
99
|
+
)}
|
|
100
|
+
data-slot="dialog-overlay"
|
|
101
|
+
{...props}
|
|
102
|
+
/>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
|
|
107
|
+
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
|
|
111
|
+
return (
|
|
112
|
+
<DialogPrimitive.Title
|
|
113
|
+
className={cn("leading-none font-medium", className)}
|
|
114
|
+
data-slot="dialog-title"
|
|
115
|
+
{...props}
|
|
116
|
+
/>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
|
|
121
|
+
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export {
|
|
125
|
+
Dialog,
|
|
126
|
+
DialogClose,
|
|
127
|
+
DialogContent,
|
|
128
|
+
DialogDescription,
|
|
129
|
+
DialogFooter,
|
|
130
|
+
DialogHeader,
|
|
131
|
+
DialogOverlay,
|
|
132
|
+
DialogPortal,
|
|
133
|
+
DialogTitle,
|
|
134
|
+
DialogTrigger,
|
|
135
|
+
}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Menu as MenuPrimitive } from "@base-ui/react/menu"
|
|
4
|
+
import { CheckIcon, ChevronRightIcon } from "lucide-react"
|
|
5
|
+
import * as React from "react"
|
|
6
|
+
|
|
7
|
+
import { cn } from "~/lib/utils"
|
|
8
|
+
|
|
9
|
+
function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
|
|
10
|
+
return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function DropdownMenuCheckboxItem({
|
|
14
|
+
checked,
|
|
15
|
+
children,
|
|
16
|
+
className,
|
|
17
|
+
...props
|
|
18
|
+
}: MenuPrimitive.CheckboxItem.Props) {
|
|
19
|
+
return (
|
|
20
|
+
<MenuPrimitive.CheckboxItem
|
|
21
|
+
checked={checked}
|
|
22
|
+
className={cn(
|
|
23
|
+
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
27
|
+
{...props}
|
|
28
|
+
>
|
|
29
|
+
<span
|
|
30
|
+
className="pointer-events-none absolute right-2 flex items-center justify-center pointer-events-none"
|
|
31
|
+
data-slot="dropdown-menu-checkbox-item-indicator"
|
|
32
|
+
>
|
|
33
|
+
<MenuPrimitive.CheckboxItemIndicator>
|
|
34
|
+
<CheckIcon />
|
|
35
|
+
</MenuPrimitive.CheckboxItemIndicator>
|
|
36
|
+
</span>
|
|
37
|
+
{children}
|
|
38
|
+
</MenuPrimitive.CheckboxItem>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function DropdownMenuContent({
|
|
43
|
+
align = "start",
|
|
44
|
+
alignOffset = 0,
|
|
45
|
+
className,
|
|
46
|
+
side = "bottom",
|
|
47
|
+
sideOffset = 4,
|
|
48
|
+
...props
|
|
49
|
+
}: MenuPrimitive.Popup.Props &
|
|
50
|
+
Pick<MenuPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset">) {
|
|
51
|
+
return (
|
|
52
|
+
<MenuPrimitive.Portal>
|
|
53
|
+
<MenuPrimitive.Positioner
|
|
54
|
+
align={align}
|
|
55
|
+
alignOffset={alignOffset}
|
|
56
|
+
className="isolate z-50 outline-none"
|
|
57
|
+
side={side}
|
|
58
|
+
sideOffset={sideOffset}
|
|
59
|
+
>
|
|
60
|
+
<MenuPrimitive.Popup
|
|
61
|
+
className={cn(
|
|
62
|
+
"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 bg-popover text-popover-foreground min-w-32 rounded-md p-1 shadow-md ring-1 duration-100 z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto outline-none data-closed:overflow-hidden",
|
|
63
|
+
className
|
|
64
|
+
)}
|
|
65
|
+
data-slot="dropdown-menu-content"
|
|
66
|
+
{...props}
|
|
67
|
+
/>
|
|
68
|
+
</MenuPrimitive.Positioner>
|
|
69
|
+
</MenuPrimitive.Portal>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
|
|
74
|
+
return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function DropdownMenuItem({
|
|
78
|
+
className,
|
|
79
|
+
inset,
|
|
80
|
+
variant = "default",
|
|
81
|
+
...props
|
|
82
|
+
}: MenuPrimitive.Item.Props & {
|
|
83
|
+
inset?: boolean
|
|
84
|
+
variant?: "default" | "destructive"
|
|
85
|
+
}) {
|
|
86
|
+
return (
|
|
87
|
+
<MenuPrimitive.Item
|
|
88
|
+
className={cn(
|
|
89
|
+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-sm px-2 py-1.5 text-sm [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-default items-center outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
90
|
+
className
|
|
91
|
+
)}
|
|
92
|
+
data-inset={inset}
|
|
93
|
+
data-slot="dropdown-menu-item"
|
|
94
|
+
data-variant={variant}
|
|
95
|
+
{...props}
|
|
96
|
+
/>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function DropdownMenuLabel({
|
|
101
|
+
className,
|
|
102
|
+
inset,
|
|
103
|
+
...props
|
|
104
|
+
}: MenuPrimitive.GroupLabel.Props & {
|
|
105
|
+
inset?: boolean
|
|
106
|
+
}) {
|
|
107
|
+
return (
|
|
108
|
+
<MenuPrimitive.GroupLabel
|
|
109
|
+
className={cn(
|
|
110
|
+
"text-muted-foreground px-2 py-1.5 text-xs font-medium data-[inset]:pl-8",
|
|
111
|
+
className
|
|
112
|
+
)}
|
|
113
|
+
data-inset={inset}
|
|
114
|
+
data-slot="dropdown-menu-label"
|
|
115
|
+
{...props}
|
|
116
|
+
/>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
|
|
121
|
+
return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
|
|
125
|
+
return <MenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function DropdownMenuRadioItem({ children, className, ...props }: MenuPrimitive.RadioItem.Props) {
|
|
129
|
+
return (
|
|
130
|
+
<MenuPrimitive.RadioItem
|
|
131
|
+
className={cn(
|
|
132
|
+
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-default items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
133
|
+
className
|
|
134
|
+
)}
|
|
135
|
+
data-slot="dropdown-menu-radio-item"
|
|
136
|
+
{...props}
|
|
137
|
+
>
|
|
138
|
+
<span
|
|
139
|
+
className="pointer-events-none absolute right-2 flex items-center justify-center pointer-events-none"
|
|
140
|
+
data-slot="dropdown-menu-radio-item-indicator"
|
|
141
|
+
>
|
|
142
|
+
<MenuPrimitive.RadioItemIndicator>
|
|
143
|
+
<CheckIcon />
|
|
144
|
+
</MenuPrimitive.RadioItemIndicator>
|
|
145
|
+
</span>
|
|
146
|
+
{children}
|
|
147
|
+
</MenuPrimitive.RadioItem>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function DropdownMenuSeparator({ className, ...props }: MenuPrimitive.Separator.Props) {
|
|
152
|
+
return (
|
|
153
|
+
<MenuPrimitive.Separator
|
|
154
|
+
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
|
155
|
+
data-slot="dropdown-menu-separator"
|
|
156
|
+
{...props}
|
|
157
|
+
/>
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
|
|
162
|
+
return (
|
|
163
|
+
<span
|
|
164
|
+
className={cn(
|
|
165
|
+
"text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest",
|
|
166
|
+
className
|
|
167
|
+
)}
|
|
168
|
+
data-slot="dropdown-menu-shortcut"
|
|
169
|
+
{...props}
|
|
170
|
+
/>
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
|
|
175
|
+
return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function DropdownMenuSubContent({
|
|
179
|
+
align = "start",
|
|
180
|
+
alignOffset = -3,
|
|
181
|
+
className,
|
|
182
|
+
side = "right",
|
|
183
|
+
sideOffset = 0,
|
|
184
|
+
...props
|
|
185
|
+
}: React.ComponentProps<typeof DropdownMenuContent>) {
|
|
186
|
+
return (
|
|
187
|
+
<DropdownMenuContent
|
|
188
|
+
align={align}
|
|
189
|
+
alignOffset={alignOffset}
|
|
190
|
+
className={cn(
|
|
191
|
+
"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 bg-popover text-popover-foreground min-w-[96px] rounded-md p-1 shadow-lg ring-1 duration-100 w-auto",
|
|
192
|
+
className
|
|
193
|
+
)}
|
|
194
|
+
data-slot="dropdown-menu-sub-content"
|
|
195
|
+
side={side}
|
|
196
|
+
sideOffset={sideOffset}
|
|
197
|
+
{...props}
|
|
198
|
+
/>
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function DropdownMenuSubTrigger({
|
|
203
|
+
children,
|
|
204
|
+
className,
|
|
205
|
+
inset,
|
|
206
|
+
...props
|
|
207
|
+
}: MenuPrimitive.SubmenuTrigger.Props & {
|
|
208
|
+
inset?: boolean
|
|
209
|
+
}) {
|
|
210
|
+
return (
|
|
211
|
+
<MenuPrimitive.SubmenuTrigger
|
|
212
|
+
className={cn(
|
|
213
|
+
"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-sm px-2 py-1.5 text-sm [&_svg:not([class*='size-'])]:size-4 flex cursor-default items-center outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
214
|
+
className
|
|
215
|
+
)}
|
|
216
|
+
data-inset={inset}
|
|
217
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
218
|
+
{...props}
|
|
219
|
+
>
|
|
220
|
+
{children}
|
|
221
|
+
<ChevronRightIcon className="ml-auto" />
|
|
222
|
+
</MenuPrimitive.SubmenuTrigger>
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
|
|
227
|
+
return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export {
|
|
231
|
+
DropdownMenu,
|
|
232
|
+
DropdownMenuCheckboxItem,
|
|
233
|
+
DropdownMenuContent,
|
|
234
|
+
DropdownMenuGroup,
|
|
235
|
+
DropdownMenuItem,
|
|
236
|
+
DropdownMenuLabel,
|
|
237
|
+
DropdownMenuPortal,
|
|
238
|
+
DropdownMenuRadioGroup,
|
|
239
|
+
DropdownMenuRadioItem,
|
|
240
|
+
DropdownMenuSeparator,
|
|
241
|
+
DropdownMenuShortcut,
|
|
242
|
+
DropdownMenuSub,
|
|
243
|
+
DropdownMenuSubContent,
|
|
244
|
+
DropdownMenuSubTrigger,
|
|
245
|
+
DropdownMenuTrigger,
|
|
246
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
import { useMemo } from "react"
|
|
5
|
+
|
|
6
|
+
import { Label } from "~/components/ui/label"
|
|
7
|
+
import { Separator } from "~/components/ui/separator"
|
|
8
|
+
import { cn } from "~/lib/utils"
|
|
9
|
+
|
|
10
|
+
function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
11
|
+
return (
|
|
12
|
+
<div
|
|
13
|
+
className={cn(
|
|
14
|
+
"gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4 group/field-group @container/field-group flex w-full flex-col",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
data-slot="field-group"
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function FieldLegend({
|
|
24
|
+
className,
|
|
25
|
+
variant = "legend",
|
|
26
|
+
...props
|
|
27
|
+
}: React.ComponentProps<"legend"> & { variant?: "label" | "legend" }) {
|
|
28
|
+
return (
|
|
29
|
+
<legend
|
|
30
|
+
className={cn(
|
|
31
|
+
"mb-3 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
data-slot="field-legend"
|
|
35
|
+
data-variant={variant}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
|
|
42
|
+
return (
|
|
43
|
+
<fieldset
|
|
44
|
+
className={cn(
|
|
45
|
+
"gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 flex flex-col",
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
data-slot="field-set"
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const fieldVariants = cva("data-[invalid=true]:text-destructive gap-3 group/field flex w-full", {
|
|
55
|
+
defaultVariants: {
|
|
56
|
+
orientation: "vertical",
|
|
57
|
+
},
|
|
58
|
+
variants: {
|
|
59
|
+
orientation: {
|
|
60
|
+
horizontal:
|
|
61
|
+
"flex-row items-center [&>[data-slot=field-label]]:flex-auto has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
|
62
|
+
responsive:
|
|
63
|
+
"flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto @md/field-group:[&>[data-slot=field-label]]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
|
|
64
|
+
vertical: "flex-col [&>*]:w-full [&>.sr-only]:w-auto",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
function Field({
|
|
70
|
+
className,
|
|
71
|
+
orientation = "vertical",
|
|
72
|
+
...props
|
|
73
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
|
|
74
|
+
return (
|
|
75
|
+
<div
|
|
76
|
+
className={cn(fieldVariants({ orientation }), className)}
|
|
77
|
+
data-orientation={orientation}
|
|
78
|
+
data-slot="field"
|
|
79
|
+
role="group"
|
|
80
|
+
{...props}
|
|
81
|
+
/>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
86
|
+
return (
|
|
87
|
+
<div
|
|
88
|
+
className={cn("gap-1 group/field-content flex flex-1 flex-col leading-snug", className)}
|
|
89
|
+
data-slot="field-content"
|
|
90
|
+
{...props}
|
|
91
|
+
/>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
|
|
96
|
+
return (
|
|
97
|
+
<p
|
|
98
|
+
className={cn(
|
|
99
|
+
"text-muted-foreground text-left text-sm [[data-variant=legend]+&]:-mt-1.5 leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance",
|
|
100
|
+
"last:mt-0 nth-last-2:-mt-1",
|
|
101
|
+
"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
|
|
102
|
+
className
|
|
103
|
+
)}
|
|
104
|
+
data-slot="field-description"
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function FieldError({
|
|
111
|
+
children,
|
|
112
|
+
className,
|
|
113
|
+
errors,
|
|
114
|
+
...props
|
|
115
|
+
}: React.ComponentProps<"div"> & {
|
|
116
|
+
errors?: Array<undefined | { message?: string }>
|
|
117
|
+
}) {
|
|
118
|
+
const content = useMemo(() => {
|
|
119
|
+
if (children) {
|
|
120
|
+
return children
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!errors?.length) {
|
|
124
|
+
return null
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()]
|
|
128
|
+
|
|
129
|
+
if (uniqueErrors?.length == 1) {
|
|
130
|
+
return uniqueErrors[0]?.message
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<ul className="ml-4 flex list-disc flex-col gap-1">
|
|
135
|
+
{uniqueErrors.map((error, index) => error?.message && <li key={index}>{error.message}</li>)}
|
|
136
|
+
</ul>
|
|
137
|
+
)
|
|
138
|
+
}, [children, errors])
|
|
139
|
+
|
|
140
|
+
if (!content) {
|
|
141
|
+
return null
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<div
|
|
146
|
+
className={cn("text-destructive text-sm font-normal", className)}
|
|
147
|
+
data-slot="field-error"
|
|
148
|
+
role="alert"
|
|
149
|
+
{...props}
|
|
150
|
+
>
|
|
151
|
+
{content}
|
|
152
|
+
</div>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
|
|
157
|
+
return (
|
|
158
|
+
<Label
|
|
159
|
+
className={cn(
|
|
160
|
+
"has-data-checked:bg-primary/5 has-data-checked:border-primary dark:has-data-checked:bg-primary/10 gap-2 group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-3 group/field-label peer/field-label flex w-fit leading-snug",
|
|
161
|
+
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
|
|
162
|
+
className
|
|
163
|
+
)}
|
|
164
|
+
data-slot="field-label"
|
|
165
|
+
{...props}
|
|
166
|
+
/>
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function FieldSeparator({
|
|
171
|
+
children,
|
|
172
|
+
className,
|
|
173
|
+
...props
|
|
174
|
+
}: React.ComponentProps<"div"> & {
|
|
175
|
+
children?: React.ReactNode
|
|
176
|
+
}) {
|
|
177
|
+
return (
|
|
178
|
+
<div
|
|
179
|
+
className={cn(
|
|
180
|
+
"-my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2 relative",
|
|
181
|
+
className
|
|
182
|
+
)}
|
|
183
|
+
data-content={!!children}
|
|
184
|
+
data-slot="field-separator"
|
|
185
|
+
{...props}
|
|
186
|
+
>
|
|
187
|
+
<Separator className="absolute inset-0 top-1/2" />
|
|
188
|
+
{children && (
|
|
189
|
+
<span
|
|
190
|
+
className="text-muted-foreground px-2 bg-background relative mx-auto block w-fit"
|
|
191
|
+
data-slot="field-separator-content"
|
|
192
|
+
>
|
|
193
|
+
{children}
|
|
194
|
+
</span>
|
|
195
|
+
)}
|
|
196
|
+
</div>
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
201
|
+
return (
|
|
202
|
+
<div
|
|
203
|
+
className={cn(
|
|
204
|
+
"gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50 flex w-fit items-center leading-snug",
|
|
205
|
+
className
|
|
206
|
+
)}
|
|
207
|
+
data-slot="field-label"
|
|
208
|
+
{...props}
|
|
209
|
+
/>
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export {
|
|
214
|
+
Field,
|
|
215
|
+
FieldContent,
|
|
216
|
+
FieldDescription,
|
|
217
|
+
FieldError,
|
|
218
|
+
FieldGroup,
|
|
219
|
+
FieldLabel,
|
|
220
|
+
FieldLegend,
|
|
221
|
+
FieldSeparator,
|
|
222
|
+
FieldSet,
|
|
223
|
+
FieldTitle,
|
|
224
|
+
}
|