create-nextjs-cms 0.9.15 → 0.9.17
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/package.json +3 -3
- package/templates/default/components/ItemEditPage.tsx +25 -25
- package/templates/default/components/LocaleSwitcher.tsx +32 -23
- package/templates/default/components/SectionPage.tsx +1 -0
- package/templates/default/components/form/helpers/_section-hot-reload.js +1 -1
- package/templates/default/components/ui/alert-dialog.tsx +84 -128
- package/templates/default/components/ui/alert.tsx +1 -1
- package/templates/default/components/ui/button.tsx +64 -62
- package/templates/default/components/ui/calendar.tsx +220 -166
- package/templates/default/components/ui/checkbox.tsx +29 -29
- package/templates/default/components/ui/command.tsx +1 -1
- package/templates/default/components/ui/custom-alert-dialog.tsx +2 -2
- package/templates/default/components/ui/custom-dialog.tsx +3 -3
- package/templates/default/components/ui/dialog.tsx +3 -3
- package/templates/default/components/ui/dropdown-menu.tsx +9 -9
- package/templates/default/components/ui/input-group.tsx +1 -1
- package/templates/default/components/ui/scroll-area.tsx +1 -1
- package/templates/default/components/ui/select.tsx +3 -3
- package/templates/default/components/ui/sheet.tsx +3 -3
- package/templates/default/components/ui/switch.tsx +1 -1
- package/templates/default/components/ui/table.tsx +2 -2
- package/templates/default/components/ui/toast.tsx +3 -3
- package/templates/default/components/ui/use-toast.ts +0 -1
- package/templates/default/components.json +25 -21
- package/templates/default/dynamic-schemas/schema.ts +1 -1
- package/templates/default/package.json +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-nextjs-cms",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.17",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"tsx": "^4.20.6",
|
|
30
30
|
"typescript": "^5.9.2",
|
|
31
31
|
"@lzcms/eslint-config": "0.3.0",
|
|
32
|
-
"@lzcms/
|
|
33
|
-
"@lzcms/
|
|
32
|
+
"@lzcms/prettier-config": "0.1.0",
|
|
33
|
+
"@lzcms/tsconfig": "0.1.0"
|
|
34
34
|
},
|
|
35
35
|
"prettier": "@lzcms/prettier-config",
|
|
36
36
|
"scripts": {
|
|
@@ -51,7 +51,7 @@ export default function ItemEditPage({
|
|
|
51
51
|
const handleDirtyChange = useCallback((isDirty: boolean) => setFormDirty(isDirty), [])
|
|
52
52
|
const dropzoneRef: RefObject<DropzoneHandles | null> = useRef(null)
|
|
53
53
|
const variantRef: RefObject<VariantHandles[]> = useRef([])
|
|
54
|
-
const [data, {refetch}] = trpc.hasItemsSections.editItem.useSuspenseQuery({
|
|
54
|
+
const [data, { refetch }] = trpc.hasItemsSections.editItem.useSuspenseQuery({
|
|
55
55
|
sectionName: section,
|
|
56
56
|
sectionItemId: itemId,
|
|
57
57
|
locale,
|
|
@@ -222,21 +222,21 @@ export default function ItemEditPage({
|
|
|
222
222
|
}, [])
|
|
223
223
|
|
|
224
224
|
if (data.error) {
|
|
225
|
-
|
|
225
|
+
return <ErrorComponent message={data.error.message} />
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
return (
|
|
229
229
|
<div className='flex w-full flex-col overflow-hidden'>
|
|
230
230
|
<div className='flex w-full flex-col'>
|
|
231
|
-
<div className='relative z-1 border-b-2 p-8 pt-12 font-extrabold
|
|
232
|
-
<div className='absolute
|
|
231
|
+
<div className='text-foreground relative z-1 border-b-2 p-8 pt-12 font-extrabold'>
|
|
232
|
+
<div className='absolute top-0 left-0 z-2 h-4 w-full bg-linear-to-r from-emerald-800 via-emerald-400 to-sky-600'></div>
|
|
233
233
|
<h1 className='pb-4 text-4xl'>{data.section?.title.section}</h1>
|
|
234
234
|
<span>
|
|
235
235
|
/{t('edit')} {data.section?.title.singular}
|
|
236
236
|
</span>
|
|
237
237
|
</div>
|
|
238
238
|
{data.localization?.localeSwitcherEnabled ? (
|
|
239
|
-
<div className=
|
|
239
|
+
<div className='px-4 pt-4'>
|
|
240
240
|
<LocaleSwitcher
|
|
241
241
|
section={section}
|
|
242
242
|
itemId={itemId}
|
|
@@ -245,29 +245,29 @@ export default function ItemEditPage({
|
|
|
245
245
|
existingTranslations={data.localization.existingTranslations}
|
|
246
246
|
locales={data.localization.locales}
|
|
247
247
|
hasUnsavedChanges={formDirty}
|
|
248
|
+
developerOnly={data.localization.developerNoteEnabled}
|
|
248
249
|
/>
|
|
249
250
|
{locale && (
|
|
250
251
|
<Alert variant='info' className='mt-2 w-full'>
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
</
|
|
270
|
-
</Alert>
|
|
252
|
+
<AlertDescription className='text-md font-bold'>
|
|
253
|
+
<div className='flex items-center justify-between'>
|
|
254
|
+
<span>{t('editingContentTranslation', { locale })}</span>
|
|
255
|
+
{data.localization.existingTranslations.includes(locale) && (
|
|
256
|
+
<button
|
|
257
|
+
type='button'
|
|
258
|
+
onClick={handleDeleteLocale}
|
|
259
|
+
disabled={deleteLocaleMutation.isPending}
|
|
260
|
+
className='inline-flex items-center gap-1.5 rounded-md bg-red-600 px-3 py-1.5 text-xs font-medium text-white transition-colors hover:bg-red-700 disabled:opacity-50'
|
|
261
|
+
>
|
|
262
|
+
<Trash2 className='h-3.5 w-3.5' />
|
|
263
|
+
{deleteLocaleMutation.isPending
|
|
264
|
+
? t('loading')
|
|
265
|
+
: t('deleteLocaleTranslation')}
|
|
266
|
+
</button>
|
|
267
|
+
)}
|
|
268
|
+
</div>
|
|
269
|
+
</AlertDescription>
|
|
270
|
+
</Alert>
|
|
271
271
|
)}
|
|
272
272
|
</div>
|
|
273
273
|
) : null}
|
|
@@ -6,6 +6,7 @@ import { Check } from 'lucide-react'
|
|
|
6
6
|
import { useI18n } from 'nextjs-cms/translations/client'
|
|
7
7
|
import { useSession } from 'nextjs-cms/auth/react'
|
|
8
8
|
import ContainerBox from './ContainerBox'
|
|
9
|
+
import { Alert, AlertDescription } from './ui/alert'
|
|
9
10
|
|
|
10
11
|
type LocaleConfig = {
|
|
11
12
|
code: string
|
|
@@ -21,6 +22,7 @@ type Props = {
|
|
|
21
22
|
existingTranslations: string[]
|
|
22
23
|
locales: LocaleConfig[]
|
|
23
24
|
hasUnsavedChanges?: boolean
|
|
25
|
+
developerOnly?: boolean
|
|
24
26
|
/** Base path for locale links. Defaults to `/edit/${section}/${itemId}` for hasItems sections. */
|
|
25
27
|
basePath?: string
|
|
26
28
|
}
|
|
@@ -33,6 +35,7 @@ export default function LocaleSwitcher({
|
|
|
33
35
|
existingTranslations,
|
|
34
36
|
locales,
|
|
35
37
|
hasUnsavedChanges,
|
|
38
|
+
developerOnly,
|
|
36
39
|
basePath,
|
|
37
40
|
}: Props) {
|
|
38
41
|
const t = useI18n()
|
|
@@ -59,31 +62,37 @@ export default function LocaleSwitcher({
|
|
|
59
62
|
|
|
60
63
|
return (
|
|
61
64
|
<ContainerBox title={t('localesHeading')}>
|
|
62
|
-
<div className=
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
<div className='flex flex-wrap items-center gap-2'>
|
|
66
|
+
{locales.map((locale) => {
|
|
67
|
+
const isActive = locale.code === currentLocale.code
|
|
68
|
+
const hasTranslation =
|
|
69
|
+
locale.code === defaultLocale.code || existingTranslations.includes(locale.code)
|
|
70
|
+
const isDefault = locale.code === defaultLocale.code
|
|
67
71
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
72
|
+
return (
|
|
73
|
+
<Link
|
|
74
|
+
key={locale.code}
|
|
75
|
+
href={getHref(locale.code)}
|
|
76
|
+
onClick={(e) => handleClick(e, locale.code)}
|
|
77
|
+
className={cn(
|
|
78
|
+
'border-primary/20 inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm font-medium transition-colors',
|
|
79
|
+
isActive
|
|
80
|
+
? 'bg-success text-success-foreground'
|
|
81
|
+
: 'bg-muted text-muted-foreground hover:bg-accent hover:text-accent-foreground',
|
|
82
|
+
)}
|
|
83
|
+
>
|
|
84
|
+
{locale.label}
|
|
85
|
+
{isDefault && <span className='text-[10px] opacity-70'>{t('baseLocaleBadge')}</span>}
|
|
86
|
+
{hasTranslation && !isDefault && <Check className='h-3 w-3' />}
|
|
87
|
+
</Link>
|
|
88
|
+
)
|
|
89
|
+
})}
|
|
86
90
|
</div>
|
|
91
|
+
{developerOnly && (
|
|
92
|
+
<Alert variant='info' className='mt-2 w-full'>
|
|
93
|
+
<AlertDescription className='text-md font-bold'>{t('localeSwitcherDevOnly')}</AlertDescription>
|
|
94
|
+
</Alert>
|
|
95
|
+
)}
|
|
87
96
|
</ContainerBox>
|
|
88
97
|
)
|
|
89
98
|
}
|
|
@@ -155,6 +155,7 @@ export default function SectionPage({ section }: { section: string }) {
|
|
|
155
155
|
locales={data.localization.locales}
|
|
156
156
|
hasUnsavedChanges={formDirty}
|
|
157
157
|
basePath={`/section/${section}`}
|
|
158
|
+
developerOnly={data.localization.developerNoteEnabled}
|
|
158
159
|
/>
|
|
159
160
|
{locale && (
|
|
160
161
|
<Alert variant='info' className='mt-2 w-full'>
|
|
@@ -1,157 +1,113 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
|
-
import * as React from
|
|
4
|
-
import * as AlertDialogPrimitive from
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'
|
|
5
5
|
|
|
6
|
-
import { cn } from
|
|
7
|
-
import { buttonVariants } from
|
|
6
|
+
import { cn } from '@/lib/utils'
|
|
7
|
+
import { buttonVariants } from '@/components/ui/button'
|
|
8
8
|
|
|
9
|
-
function AlertDialog({
|
|
10
|
-
|
|
11
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
|
|
12
|
-
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
|
|
9
|
+
function AlertDialog({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
|
|
10
|
+
return <AlertDialogPrimitive.Root data-slot='alert-dialog' {...props} />
|
|
13
11
|
}
|
|
14
12
|
|
|
15
|
-
function AlertDialogTrigger({
|
|
16
|
-
|
|
17
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
|
|
18
|
-
return (
|
|
19
|
-
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
|
|
20
|
-
)
|
|
13
|
+
function AlertDialogTrigger({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
|
|
14
|
+
return <AlertDialogPrimitive.Trigger data-slot='alert-dialog-trigger' {...props} />
|
|
21
15
|
}
|
|
22
16
|
|
|
23
|
-
function AlertDialogPortal({
|
|
24
|
-
|
|
25
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
|
|
26
|
-
return (
|
|
27
|
-
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
|
|
28
|
-
)
|
|
17
|
+
function AlertDialogPortal({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
|
|
18
|
+
return <AlertDialogPrimitive.Portal data-slot='alert-dialog-portal' {...props} />
|
|
29
19
|
}
|
|
30
20
|
|
|
31
|
-
function AlertDialogOverlay({
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
{...props}
|
|
43
|
-
/>
|
|
44
|
-
)
|
|
21
|
+
function AlertDialogOverlay({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
|
|
22
|
+
return (
|
|
23
|
+
<AlertDialogPrimitive.Overlay
|
|
24
|
+
data-slot='alert-dialog-overlay'
|
|
25
|
+
className={cn(
|
|
26
|
+
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
|
|
27
|
+
className,
|
|
28
|
+
)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
)
|
|
45
32
|
}
|
|
46
33
|
|
|
47
|
-
function AlertDialogContent({
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
/>
|
|
62
|
-
</AlertDialogPortal>
|
|
63
|
-
)
|
|
34
|
+
function AlertDialogContent({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
|
|
35
|
+
return (
|
|
36
|
+
<AlertDialogPortal>
|
|
37
|
+
<AlertDialogOverlay />
|
|
38
|
+
<AlertDialogPrimitive.Content
|
|
39
|
+
data-slot='alert-dialog-content'
|
|
40
|
+
className={cn(
|
|
41
|
+
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed start-[50%] top-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg rtl:-translate-x-[-50%]',
|
|
42
|
+
className,
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
</AlertDialogPortal>
|
|
47
|
+
)
|
|
64
48
|
}
|
|
65
49
|
|
|
66
|
-
function AlertDialogHeader({
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
{...props}
|
|
75
|
-
/>
|
|
76
|
-
)
|
|
50
|
+
function AlertDialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
data-slot='alert-dialog-header'
|
|
54
|
+
className={cn('flex flex-col gap-2 text-center sm:text-start', className)}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
77
58
|
}
|
|
78
59
|
|
|
79
|
-
function AlertDialogFooter({
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
88
|
-
className
|
|
89
|
-
)}
|
|
90
|
-
{...props}
|
|
91
|
-
/>
|
|
92
|
-
)
|
|
60
|
+
function AlertDialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
|
61
|
+
return (
|
|
62
|
+
<div
|
|
63
|
+
data-slot='alert-dialog-footer'
|
|
64
|
+
className={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
)
|
|
93
68
|
}
|
|
94
69
|
|
|
95
|
-
function AlertDialogTitle({
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
{...props}
|
|
104
|
-
/>
|
|
105
|
-
)
|
|
70
|
+
function AlertDialogTitle({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
|
|
71
|
+
return (
|
|
72
|
+
<AlertDialogPrimitive.Title
|
|
73
|
+
data-slot='alert-dialog-title'
|
|
74
|
+
className={cn('text-lg font-semibold', className)}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
)
|
|
106
78
|
}
|
|
107
79
|
|
|
108
80
|
function AlertDialogDescription({
|
|
109
|
-
|
|
110
|
-
|
|
81
|
+
className,
|
|
82
|
+
...props
|
|
111
83
|
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
84
|
+
return (
|
|
85
|
+
<AlertDialogPrimitive.Description
|
|
86
|
+
data-slot='alert-dialog-description'
|
|
87
|
+
className={cn('text-muted-foreground text-sm', className)}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
)
|
|
119
91
|
}
|
|
120
92
|
|
|
121
|
-
function AlertDialogAction({
|
|
122
|
-
|
|
123
|
-
...props
|
|
124
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
|
|
125
|
-
return (
|
|
126
|
-
<AlertDialogPrimitive.Action
|
|
127
|
-
className={cn(buttonVariants(), className)}
|
|
128
|
-
{...props}
|
|
129
|
-
/>
|
|
130
|
-
)
|
|
93
|
+
function AlertDialogAction({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
|
|
94
|
+
return <AlertDialogPrimitive.Action className={cn(buttonVariants(), className)} {...props} />
|
|
131
95
|
}
|
|
132
96
|
|
|
133
|
-
function AlertDialogCancel({
|
|
134
|
-
|
|
135
|
-
...props
|
|
136
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
|
|
137
|
-
return (
|
|
138
|
-
<AlertDialogPrimitive.Cancel
|
|
139
|
-
className={cn(buttonVariants({ variant: "outline" }), className)}
|
|
140
|
-
{...props}
|
|
141
|
-
/>
|
|
142
|
-
)
|
|
97
|
+
function AlertDialogCancel({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
|
|
98
|
+
return <AlertDialogPrimitive.Cancel className={cn(buttonVariants({ variant: 'outline' }), className)} {...props} />
|
|
143
99
|
}
|
|
144
100
|
|
|
145
101
|
export {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
102
|
+
AlertDialog,
|
|
103
|
+
AlertDialogPortal,
|
|
104
|
+
AlertDialogOverlay,
|
|
105
|
+
AlertDialogTrigger,
|
|
106
|
+
AlertDialogContent,
|
|
107
|
+
AlertDialogHeader,
|
|
108
|
+
AlertDialogFooter,
|
|
109
|
+
AlertDialogTitle,
|
|
110
|
+
AlertDialogDescription,
|
|
111
|
+
AlertDialogAction,
|
|
112
|
+
AlertDialogCancel,
|
|
157
113
|
}
|
|
@@ -4,7 +4,7 @@ import { cva, type VariantProps } from 'class-variance-authority'
|
|
|
4
4
|
import { cn } from '@/lib/utils'
|
|
5
5
|
|
|
6
6
|
const alertVariants = cva(
|
|
7
|
-
'relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:
|
|
7
|
+
'relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:start-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:ps-7',
|
|
8
8
|
{
|
|
9
9
|
variants: {
|
|
10
10
|
variant: {
|
|
@@ -1,62 +1,64 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import { cn } from "@/lib/utils"
|
|
6
|
-
|
|
7
|
-
const buttonVariants = cva(
|
|
8
|
-
"inline-flex items-center justify-center gap-2
|
|
9
|
-
{
|
|
10
|
-
variants: {
|
|
11
|
-
variant: {
|
|
12
|
-
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
13
|
-
destructive:
|
|
14
|
-
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40
|
|
15
|
-
outline:
|
|
16
|
-
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:
|
|
17
|
-
secondary:
|
|
18
|
-
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
19
|
-
ghost:
|
|
20
|
-
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
21
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
-
},
|
|
23
|
-
size: {
|
|
24
|
-
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"icon-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
data-
|
|
56
|
-
|
|
57
|
-
{
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { Slot } from "radix-ui"
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils"
|
|
6
|
+
|
|
7
|
+
const buttonVariants = cva(
|
|
8
|
+
"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
13
|
+
destructive:
|
|
14
|
+
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
15
|
+
outline:
|
|
16
|
+
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
17
|
+
secondary:
|
|
18
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
19
|
+
ghost:
|
|
20
|
+
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
21
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
25
|
+
xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
26
|
+
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
|
27
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
28
|
+
icon: "size-9",
|
|
29
|
+
"icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
30
|
+
"icon-sm": "size-8",
|
|
31
|
+
"icon-lg": "size-10",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
defaultVariants: {
|
|
35
|
+
variant: "default",
|
|
36
|
+
size: "default",
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
function Button({
|
|
42
|
+
className,
|
|
43
|
+
variant = "default",
|
|
44
|
+
size = "default",
|
|
45
|
+
asChild = false,
|
|
46
|
+
...props
|
|
47
|
+
}: React.ComponentProps<"button"> &
|
|
48
|
+
VariantProps<typeof buttonVariants> & {
|
|
49
|
+
asChild?: boolean
|
|
50
|
+
}) {
|
|
51
|
+
const Comp = asChild ? Slot.Root : "button"
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Comp
|
|
55
|
+
data-slot="button"
|
|
56
|
+
data-variant={variant}
|
|
57
|
+
data-size={size}
|
|
58
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { Button, buttonVariants }
|