create-z3 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +50 -0
- package/package.json +54 -0
- package/templates/tanstack-start/.env.example +45 -0
- package/templates/tanstack-start/.prettierignore +18 -0
- package/templates/tanstack-start/README.md +221 -0
- package/templates/tanstack-start/components.json +24 -0
- package/templates/tanstack-start/convex/_generated/api.d.ts +65 -0
- package/templates/tanstack-start/convex/_generated/api.js +23 -0
- package/templates/tanstack-start/convex/_generated/dataModel.d.ts +60 -0
- package/templates/tanstack-start/convex/_generated/server.d.ts +143 -0
- package/templates/tanstack-start/convex/_generated/server.js +93 -0
- package/templates/tanstack-start/convex/auth/adapter/index.ts +222 -0
- package/templates/tanstack-start/convex/auth/adapter/utils.ts +597 -0
- package/templates/tanstack-start/convex/auth/api.ts +8 -0
- package/templates/tanstack-start/convex/auth/config.ts +8 -0
- package/templates/tanstack-start/convex/auth/db.ts +299 -0
- package/templates/tanstack-start/convex/auth/index.ts +62 -0
- package/templates/tanstack-start/convex/auth/plugins/index.ts +7 -0
- package/templates/tanstack-start/convex/auth/sessions.ts +60 -0
- package/templates/tanstack-start/convex/http.ts +44 -0
- package/templates/tanstack-start/convex/schema.ts +69 -0
- package/templates/tanstack-start/eslint.config.js +77 -0
- package/templates/tanstack-start/package.json +74 -0
- package/templates/tanstack-start/prettier.config.js +35 -0
- package/templates/tanstack-start/public/favicon.ico +0 -0
- package/templates/tanstack-start/public/logo192.png +0 -0
- package/templates/tanstack-start/public/logo512.png +0 -0
- package/templates/tanstack-start/public/manifest.json +25 -0
- package/templates/tanstack-start/public/robots.txt +3 -0
- package/templates/tanstack-start/public/tanstack-circle-logo.png +0 -0
- package/templates/tanstack-start/public/tanstack-word-logo-white.svg +1 -0
- package/templates/tanstack-start/src/components/component-example.tsx +470 -0
- package/templates/tanstack-start/src/components/example.tsx +52 -0
- package/templates/tanstack-start/src/components/ui/alert-dialog.tsx +160 -0
- package/templates/tanstack-start/src/components/ui/badge.tsx +49 -0
- package/templates/tanstack-start/src/components/ui/button.tsx +58 -0
- package/templates/tanstack-start/src/components/ui/card.tsx +92 -0
- package/templates/tanstack-start/src/components/ui/combobox.tsx +271 -0
- package/templates/tanstack-start/src/components/ui/dropdown-menu.tsx +244 -0
- package/templates/tanstack-start/src/components/ui/field.tsx +222 -0
- package/templates/tanstack-start/src/components/ui/input-group.tsx +146 -0
- package/templates/tanstack-start/src/components/ui/input.tsx +20 -0
- package/templates/tanstack-start/src/components/ui/label.tsx +20 -0
- package/templates/tanstack-start/src/components/ui/select.tsx +189 -0
- package/templates/tanstack-start/src/components/ui/separator.tsx +19 -0
- package/templates/tanstack-start/src/components/ui/textarea.tsx +18 -0
- package/templates/tanstack-start/src/db/constants/auth.ts +41 -0
- package/templates/tanstack-start/src/db/constants/index.ts +8 -0
- package/templates/tanstack-start/src/env.ts +94 -0
- package/templates/tanstack-start/src/lib/auth/client.ts +18 -0
- package/templates/tanstack-start/src/lib/auth/server.ts +14 -0
- package/templates/tanstack-start/src/lib/utils.ts +6 -0
- package/templates/tanstack-start/src/logo.svg +12 -0
- package/templates/tanstack-start/src/providers.tsx +38 -0
- package/templates/tanstack-start/src/routeTree.gen.ts +127 -0
- package/templates/tanstack-start/src/router.tsx +41 -0
- package/templates/tanstack-start/src/routes/__root.tsx +92 -0
- package/templates/tanstack-start/src/routes/account/$accountView.tsx +15 -0
- package/templates/tanstack-start/src/routes/api/auth/$.tsx +11 -0
- package/templates/tanstack-start/src/routes/auth/$authView.tsx +15 -0
- package/templates/tanstack-start/src/routes/index.tsx +9 -0
- package/templates/tanstack-start/src/styles.css +156 -0
- package/templates/tanstack-start/tsconfig.json +42 -0
- package/templates/tanstack-start/vite.config.ts +32 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { Menu as MenuPrimitive } from '@base-ui/react/menu'
|
|
3
|
+
import { CheckIcon, ChevronRightIcon } from 'lucide-react'
|
|
4
|
+
|
|
5
|
+
import { cn } from '~/lib/utils'
|
|
6
|
+
|
|
7
|
+
function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
|
|
8
|
+
return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
|
|
12
|
+
return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
|
|
16
|
+
return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function DropdownMenuContent({
|
|
20
|
+
align = 'start',
|
|
21
|
+
alignOffset = 0,
|
|
22
|
+
side = 'bottom',
|
|
23
|
+
sideOffset = 4,
|
|
24
|
+
className,
|
|
25
|
+
...props
|
|
26
|
+
}: MenuPrimitive.Popup.Props &
|
|
27
|
+
Pick<MenuPrimitive.Positioner.Props, 'align' | 'alignOffset' | 'side' | 'sideOffset'>) {
|
|
28
|
+
return (
|
|
29
|
+
<MenuPrimitive.Portal>
|
|
30
|
+
<MenuPrimitive.Positioner
|
|
31
|
+
className="isolate z-50 outline-none"
|
|
32
|
+
align={align}
|
|
33
|
+
alignOffset={alignOffset}
|
|
34
|
+
side={side}
|
|
35
|
+
sideOffset={sideOffset}
|
|
36
|
+
>
|
|
37
|
+
<MenuPrimitive.Popup
|
|
38
|
+
data-slot="dropdown-menu-content"
|
|
39
|
+
className={cn(
|
|
40
|
+
'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 z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md p-1 shadow-md ring-1 duration-100 outline-none data-closed:overflow-hidden',
|
|
41
|
+
className,
|
|
42
|
+
)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
</MenuPrimitive.Positioner>
|
|
46
|
+
</MenuPrimitive.Portal>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
|
|
51
|
+
return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function DropdownMenuLabel({
|
|
55
|
+
className,
|
|
56
|
+
inset,
|
|
57
|
+
...props
|
|
58
|
+
}: MenuPrimitive.GroupLabel.Props & {
|
|
59
|
+
inset?: boolean
|
|
60
|
+
}) {
|
|
61
|
+
return (
|
|
62
|
+
<MenuPrimitive.GroupLabel
|
|
63
|
+
data-slot="dropdown-menu-label"
|
|
64
|
+
data-inset={inset}
|
|
65
|
+
className={cn(
|
|
66
|
+
'text-muted-foreground px-2 py-1.5 text-xs font-medium data-[inset]:pl-8',
|
|
67
|
+
className,
|
|
68
|
+
)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function DropdownMenuItem({
|
|
75
|
+
className,
|
|
76
|
+
inset,
|
|
77
|
+
variant = 'default',
|
|
78
|
+
...props
|
|
79
|
+
}: MenuPrimitive.Item.Props & {
|
|
80
|
+
inset?: boolean
|
|
81
|
+
variant?: 'default' | 'destructive'
|
|
82
|
+
}) {
|
|
83
|
+
return (
|
|
84
|
+
<MenuPrimitive.Item
|
|
85
|
+
data-slot="dropdown-menu-item"
|
|
86
|
+
data-inset={inset}
|
|
87
|
+
data-variant={variant}
|
|
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 group/dropdown-menu-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
90
|
+
className,
|
|
91
|
+
)}
|
|
92
|
+
{...props}
|
|
93
|
+
/>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
|
|
98
|
+
return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function DropdownMenuSubTrigger({
|
|
102
|
+
className,
|
|
103
|
+
inset,
|
|
104
|
+
children,
|
|
105
|
+
...props
|
|
106
|
+
}: MenuPrimitive.SubmenuTrigger.Props & {
|
|
107
|
+
inset?: boolean
|
|
108
|
+
}) {
|
|
109
|
+
return (
|
|
110
|
+
<MenuPrimitive.SubmenuTrigger
|
|
111
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
112
|
+
data-inset={inset}
|
|
113
|
+
className={cn(
|
|
114
|
+
"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
115
|
+
className,
|
|
116
|
+
)}
|
|
117
|
+
{...props}
|
|
118
|
+
>
|
|
119
|
+
{children}
|
|
120
|
+
<ChevronRightIcon className="ml-auto" />
|
|
121
|
+
</MenuPrimitive.SubmenuTrigger>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function DropdownMenuSubContent({
|
|
126
|
+
align = 'start',
|
|
127
|
+
alignOffset = -3,
|
|
128
|
+
side = 'right',
|
|
129
|
+
sideOffset = 0,
|
|
130
|
+
className,
|
|
131
|
+
...props
|
|
132
|
+
}: React.ComponentProps<typeof DropdownMenuContent>) {
|
|
133
|
+
return (
|
|
134
|
+
<DropdownMenuContent
|
|
135
|
+
data-slot="dropdown-menu-sub-content"
|
|
136
|
+
className={cn(
|
|
137
|
+
'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 w-auto min-w-[96px] rounded-md p-1 shadow-lg ring-1 duration-100',
|
|
138
|
+
className,
|
|
139
|
+
)}
|
|
140
|
+
align={align}
|
|
141
|
+
alignOffset={alignOffset}
|
|
142
|
+
side={side}
|
|
143
|
+
sideOffset={sideOffset}
|
|
144
|
+
{...props}
|
|
145
|
+
/>
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function DropdownMenuCheckboxItem({
|
|
150
|
+
className,
|
|
151
|
+
children,
|
|
152
|
+
checked,
|
|
153
|
+
...props
|
|
154
|
+
}: MenuPrimitive.CheckboxItem.Props) {
|
|
155
|
+
return (
|
|
156
|
+
<MenuPrimitive.CheckboxItem
|
|
157
|
+
data-slot="dropdown-menu-checkbox-item"
|
|
158
|
+
className={cn(
|
|
159
|
+
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
160
|
+
className,
|
|
161
|
+
)}
|
|
162
|
+
checked={checked}
|
|
163
|
+
{...props}
|
|
164
|
+
>
|
|
165
|
+
<span
|
|
166
|
+
className="pointer-events-none absolute right-2 flex items-center justify-center"
|
|
167
|
+
data-slot="dropdown-menu-checkbox-item-indicator"
|
|
168
|
+
>
|
|
169
|
+
<MenuPrimitive.CheckboxItemIndicator>
|
|
170
|
+
<CheckIcon />
|
|
171
|
+
</MenuPrimitive.CheckboxItemIndicator>
|
|
172
|
+
</span>
|
|
173
|
+
{children}
|
|
174
|
+
</MenuPrimitive.CheckboxItem>
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
|
|
179
|
+
return <MenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function DropdownMenuRadioItem({ className, children, ...props }: MenuPrimitive.RadioItem.Props) {
|
|
183
|
+
return (
|
|
184
|
+
<MenuPrimitive.RadioItem
|
|
185
|
+
data-slot="dropdown-menu-radio-item"
|
|
186
|
+
className={cn(
|
|
187
|
+
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
188
|
+
className,
|
|
189
|
+
)}
|
|
190
|
+
{...props}
|
|
191
|
+
>
|
|
192
|
+
<span
|
|
193
|
+
className="pointer-events-none absolute right-2 flex items-center justify-center"
|
|
194
|
+
data-slot="dropdown-menu-radio-item-indicator"
|
|
195
|
+
>
|
|
196
|
+
<MenuPrimitive.RadioItemIndicator>
|
|
197
|
+
<CheckIcon />
|
|
198
|
+
</MenuPrimitive.RadioItemIndicator>
|
|
199
|
+
</span>
|
|
200
|
+
{children}
|
|
201
|
+
</MenuPrimitive.RadioItem>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function DropdownMenuSeparator({ className, ...props }: MenuPrimitive.Separator.Props) {
|
|
206
|
+
return (
|
|
207
|
+
<MenuPrimitive.Separator
|
|
208
|
+
data-slot="dropdown-menu-separator"
|
|
209
|
+
className={cn('bg-border -mx-1 my-1 h-px', className)}
|
|
210
|
+
{...props}
|
|
211
|
+
/>
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {
|
|
216
|
+
return (
|
|
217
|
+
<span
|
|
218
|
+
data-slot="dropdown-menu-shortcut"
|
|
219
|
+
className={cn(
|
|
220
|
+
'text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest',
|
|
221
|
+
className,
|
|
222
|
+
)}
|
|
223
|
+
{...props}
|
|
224
|
+
/>
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export {
|
|
229
|
+
DropdownMenu,
|
|
230
|
+
DropdownMenuPortal,
|
|
231
|
+
DropdownMenuTrigger,
|
|
232
|
+
DropdownMenuContent,
|
|
233
|
+
DropdownMenuGroup,
|
|
234
|
+
DropdownMenuLabel,
|
|
235
|
+
DropdownMenuItem,
|
|
236
|
+
DropdownMenuCheckboxItem,
|
|
237
|
+
DropdownMenuRadioGroup,
|
|
238
|
+
DropdownMenuRadioItem,
|
|
239
|
+
DropdownMenuSeparator,
|
|
240
|
+
DropdownMenuShortcut,
|
|
241
|
+
DropdownMenuSub,
|
|
242
|
+
DropdownMenuSubTrigger,
|
|
243
|
+
DropdownMenuSubContent,
|
|
244
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
3
|
+
|
|
4
|
+
import { Label } from '~/components/ui/label'
|
|
5
|
+
import { Separator } from '~/components/ui/separator'
|
|
6
|
+
import { cn } from '~/lib/utils'
|
|
7
|
+
|
|
8
|
+
function FieldSet({ className, ...props }: React.ComponentProps<'fieldset'>) {
|
|
9
|
+
return (
|
|
10
|
+
<fieldset
|
|
11
|
+
data-slot="field-set"
|
|
12
|
+
className={cn(
|
|
13
|
+
'flex flex-col gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3',
|
|
14
|
+
className,
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function FieldLegend({
|
|
22
|
+
className,
|
|
23
|
+
variant = 'legend',
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {
|
|
26
|
+
return (
|
|
27
|
+
<legend
|
|
28
|
+
data-slot="field-legend"
|
|
29
|
+
data-variant={variant}
|
|
30
|
+
className={cn(
|
|
31
|
+
'mb-3 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base',
|
|
32
|
+
className,
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function FieldGroup({ className, ...props }: React.ComponentProps<'div'>) {
|
|
40
|
+
return (
|
|
41
|
+
<div
|
|
42
|
+
data-slot="field-group"
|
|
43
|
+
className={cn(
|
|
44
|
+
'group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4',
|
|
45
|
+
className,
|
|
46
|
+
)}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const fieldVariants = cva('data-[invalid=true]:text-destructive gap-3 group/field flex w-full', {
|
|
53
|
+
variants: {
|
|
54
|
+
orientation: {
|
|
55
|
+
vertical: 'flex-col [&>*]:w-full [&>.sr-only]:w-auto',
|
|
56
|
+
horizontal:
|
|
57
|
+
'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',
|
|
58
|
+
responsive:
|
|
59
|
+
'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',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
defaultVariants: {
|
|
63
|
+
orientation: 'vertical',
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
function Field({
|
|
68
|
+
className,
|
|
69
|
+
orientation = 'vertical',
|
|
70
|
+
...props
|
|
71
|
+
}: React.ComponentProps<'div'> & VariantProps<typeof fieldVariants>) {
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
role="group"
|
|
75
|
+
data-slot="field"
|
|
76
|
+
data-orientation={orientation}
|
|
77
|
+
className={cn(fieldVariants({ orientation }), className)}
|
|
78
|
+
{...props}
|
|
79
|
+
/>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function FieldContent({ className, ...props }: React.ComponentProps<'div'>) {
|
|
84
|
+
return (
|
|
85
|
+
<div
|
|
86
|
+
data-slot="field-content"
|
|
87
|
+
className={cn('group/field-content flex flex-1 flex-col gap-1 leading-snug', className)}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
|
|
94
|
+
return (
|
|
95
|
+
<Label
|
|
96
|
+
data-slot="field-label"
|
|
97
|
+
className={cn(
|
|
98
|
+
'has-data-checked:bg-primary/5 has-data-checked:border-primary dark:has-data-checked:bg-primary/10 group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-3',
|
|
99
|
+
'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col',
|
|
100
|
+
className,
|
|
101
|
+
)}
|
|
102
|
+
{...props}
|
|
103
|
+
/>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function FieldTitle({ className, ...props }: React.ComponentProps<'div'>) {
|
|
108
|
+
return (
|
|
109
|
+
<div
|
|
110
|
+
data-slot="field-label"
|
|
111
|
+
className={cn(
|
|
112
|
+
'flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50',
|
|
113
|
+
className,
|
|
114
|
+
)}
|
|
115
|
+
{...props}
|
|
116
|
+
/>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function FieldDescription({ className, ...props }: React.ComponentProps<'p'>) {
|
|
121
|
+
return (
|
|
122
|
+
<p
|
|
123
|
+
data-slot="field-description"
|
|
124
|
+
className={cn(
|
|
125
|
+
'text-muted-foreground text-left text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance [[data-variant=legend]+&]:-mt-1.5',
|
|
126
|
+
'last:mt-0 nth-last-2:-mt-1',
|
|
127
|
+
'[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4',
|
|
128
|
+
className,
|
|
129
|
+
)}
|
|
130
|
+
{...props}
|
|
131
|
+
/>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function FieldSeparator({
|
|
136
|
+
children,
|
|
137
|
+
className,
|
|
138
|
+
...props
|
|
139
|
+
}: React.ComponentProps<'div'> & {
|
|
140
|
+
children?: React.ReactNode
|
|
141
|
+
}) {
|
|
142
|
+
return (
|
|
143
|
+
<div
|
|
144
|
+
data-slot="field-separator"
|
|
145
|
+
data-content={!!children}
|
|
146
|
+
className={cn(
|
|
147
|
+
'relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2',
|
|
148
|
+
className,
|
|
149
|
+
)}
|
|
150
|
+
{...props}
|
|
151
|
+
>
|
|
152
|
+
<Separator className="absolute inset-0 top-1/2" />
|
|
153
|
+
{children && (
|
|
154
|
+
<span
|
|
155
|
+
className="text-muted-foreground bg-background relative mx-auto block w-fit px-2"
|
|
156
|
+
data-slot="field-separator-content"
|
|
157
|
+
>
|
|
158
|
+
{children}
|
|
159
|
+
</span>
|
|
160
|
+
)}
|
|
161
|
+
</div>
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function FieldError({
|
|
166
|
+
className,
|
|
167
|
+
children,
|
|
168
|
+
errors,
|
|
169
|
+
...props
|
|
170
|
+
}: React.ComponentProps<'div'> & {
|
|
171
|
+
errors?: Array<{ message?: string } | undefined>
|
|
172
|
+
}) {
|
|
173
|
+
const content = useMemo(() => {
|
|
174
|
+
if (children) {
|
|
175
|
+
return children
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!errors?.length) {
|
|
179
|
+
return null
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()]
|
|
183
|
+
|
|
184
|
+
if (uniqueErrors.length == 1) {
|
|
185
|
+
return uniqueErrors[0]?.message
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<ul className="ml-4 flex list-disc flex-col gap-1">
|
|
190
|
+
{uniqueErrors.map((error, index) => error?.message && <li key={index}>{error.message}</li>)}
|
|
191
|
+
</ul>
|
|
192
|
+
)
|
|
193
|
+
}, [children, errors])
|
|
194
|
+
|
|
195
|
+
if (!content) {
|
|
196
|
+
return null
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<div
|
|
201
|
+
role="alert"
|
|
202
|
+
data-slot="field-error"
|
|
203
|
+
className={cn('text-destructive text-sm font-normal', className)}
|
|
204
|
+
{...props}
|
|
205
|
+
>
|
|
206
|
+
{content}
|
|
207
|
+
</div>
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export {
|
|
212
|
+
Field,
|
|
213
|
+
FieldLabel,
|
|
214
|
+
FieldDescription,
|
|
215
|
+
FieldError,
|
|
216
|
+
FieldGroup,
|
|
217
|
+
FieldLegend,
|
|
218
|
+
FieldSeparator,
|
|
219
|
+
FieldSet,
|
|
220
|
+
FieldContent,
|
|
221
|
+
FieldTitle,
|
|
222
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
5
|
+
|
|
6
|
+
import { Button } from '~/components/ui/button'
|
|
7
|
+
import { Input } from '~/components/ui/input'
|
|
8
|
+
import { Textarea } from '~/components/ui/textarea'
|
|
9
|
+
import { cn } from '~/lib/utils'
|
|
10
|
+
|
|
11
|
+
function InputGroup({ className, ...props }: React.ComponentProps<'div'>) {
|
|
12
|
+
return (
|
|
13
|
+
<div
|
|
14
|
+
data-slot="input-group"
|
|
15
|
+
role="group"
|
|
16
|
+
className={cn(
|
|
17
|
+
'border-input dark:bg-input/30 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 group/input-group relative flex h-9 w-full min-w-0 items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] has-[[data-slot][aria-invalid=true]]:ring-[3px] has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5 [[data-slot=combobox-content]_&]:focus-within:border-inherit [[data-slot=combobox-content]_&]:focus-within:ring-0',
|
|
18
|
+
className,
|
|
19
|
+
)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const inputGroupAddonVariants = cva(
|
|
26
|
+
"text-muted-foreground h-auto gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4 flex cursor-text items-center justify-center select-none",
|
|
27
|
+
{
|
|
28
|
+
variants: {
|
|
29
|
+
align: {
|
|
30
|
+
'inline-start': 'pl-2 has-[>button]:ml-[-0.25rem] has-[>kbd]:ml-[-0.15rem] order-first',
|
|
31
|
+
'inline-end': 'pr-2 has-[>button]:mr-[-0.25rem] has-[>kbd]:mr-[-0.15rem] order-last',
|
|
32
|
+
'block-start':
|
|
33
|
+
'px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2 order-first w-full justify-start',
|
|
34
|
+
'block-end':
|
|
35
|
+
'px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2 order-last w-full justify-start',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
defaultVariants: {
|
|
39
|
+
align: 'inline-start',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
function InputGroupAddon({
|
|
45
|
+
className,
|
|
46
|
+
align = 'inline-start',
|
|
47
|
+
...props
|
|
48
|
+
}: React.ComponentProps<'div'> & VariantProps<typeof inputGroupAddonVariants>) {
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
role="group"
|
|
52
|
+
data-slot="input-group-addon"
|
|
53
|
+
data-align={align}
|
|
54
|
+
className={cn(inputGroupAddonVariants({ align }), className)}
|
|
55
|
+
onClick={(e) => {
|
|
56
|
+
if ((e.target as HTMLElement).closest('button')) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
e.currentTarget.parentElement?.querySelector('input')?.focus()
|
|
60
|
+
}}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const inputGroupButtonVariants = cva('gap-2 text-sm shadow-none flex items-center', {
|
|
67
|
+
variants: {
|
|
68
|
+
size: {
|
|
69
|
+
xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
|
|
70
|
+
sm: '',
|
|
71
|
+
'icon-xs': 'size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0',
|
|
72
|
+
'icon-sm': 'size-8 p-0 has-[>svg]:p-0',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
defaultVariants: {
|
|
76
|
+
size: 'xs',
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
function InputGroupButton({
|
|
81
|
+
className,
|
|
82
|
+
type = 'button',
|
|
83
|
+
variant = 'ghost',
|
|
84
|
+
size = 'xs',
|
|
85
|
+
...props
|
|
86
|
+
}: Omit<React.ComponentProps<typeof Button>, 'size' | 'type'> &
|
|
87
|
+
VariantProps<typeof inputGroupButtonVariants> & {
|
|
88
|
+
type?: 'button' | 'submit' | 'reset'
|
|
89
|
+
}) {
|
|
90
|
+
return (
|
|
91
|
+
<Button
|
|
92
|
+
type={type}
|
|
93
|
+
data-size={size}
|
|
94
|
+
variant={variant}
|
|
95
|
+
className={cn(inputGroupButtonVariants({ size }), className)}
|
|
96
|
+
{...props}
|
|
97
|
+
/>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function InputGroupText({ className, ...props }: React.ComponentProps<'span'>) {
|
|
102
|
+
return (
|
|
103
|
+
<span
|
|
104
|
+
className={cn(
|
|
105
|
+
"text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
106
|
+
className,
|
|
107
|
+
)}
|
|
108
|
+
{...props}
|
|
109
|
+
/>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function InputGroupInput({ className, ...props }: React.ComponentProps<'input'>) {
|
|
114
|
+
return (
|
|
115
|
+
<Input
|
|
116
|
+
data-slot="input-group-control"
|
|
117
|
+
className={cn(
|
|
118
|
+
'flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent',
|
|
119
|
+
className,
|
|
120
|
+
)}
|
|
121
|
+
{...props}
|
|
122
|
+
/>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function InputGroupTextarea({ className, ...props }: React.ComponentProps<'textarea'>) {
|
|
127
|
+
return (
|
|
128
|
+
<Textarea
|
|
129
|
+
data-slot="input-group-control"
|
|
130
|
+
className={cn(
|
|
131
|
+
'flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent',
|
|
132
|
+
className,
|
|
133
|
+
)}
|
|
134
|
+
{...props}
|
|
135
|
+
/>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export {
|
|
140
|
+
InputGroup,
|
|
141
|
+
InputGroupAddon,
|
|
142
|
+
InputGroupButton,
|
|
143
|
+
InputGroupText,
|
|
144
|
+
InputGroupInput,
|
|
145
|
+
InputGroupTextarea,
|
|
146
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { Input as InputPrimitive } from '@base-ui/react/input'
|
|
3
|
+
|
|
4
|
+
import { cn } from '~/lib/utils'
|
|
5
|
+
|
|
6
|
+
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
|
|
7
|
+
return (
|
|
8
|
+
<InputPrimitive
|
|
9
|
+
type={type}
|
|
10
|
+
data-slot="input"
|
|
11
|
+
className={cn(
|
|
12
|
+
'dark:bg-input/30 border-input 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 file:text-foreground placeholder:text-muted-foreground h-9 w-full min-w-0 rounded-md border bg-transparent px-2.5 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-[3px] disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-[3px] md:text-sm',
|
|
13
|
+
className,
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { Input }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
|
|
5
|
+
import { cn } from '~/lib/utils'
|
|
6
|
+
|
|
7
|
+
function Label({ className, ...props }: React.ComponentProps<'label'>) {
|
|
8
|
+
return (
|
|
9
|
+
<label
|
|
10
|
+
data-slot="label"
|
|
11
|
+
className={cn(
|
|
12
|
+
'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
|
|
13
|
+
className,
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { Label }
|