@leitware/dockets 0.1.0
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/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +18 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/add.d.ts +3 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +86 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +36 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/registry.d.ts +18 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +712 -0
- package/dist/registry.js.map +1 -0
- package/package.json +40 -0
- package/templates/accordion.tsx +77 -0
- package/templates/alert-dialog.tsx +66 -0
- package/templates/alert.tsx +41 -0
- package/templates/aspect-ratio.tsx +15 -0
- package/templates/avatar.tsx +27 -0
- package/templates/badge.tsx +1 -0
- package/templates/block-loader.tsx +1 -0
- package/templates/breadcrumb.tsx +31 -0
- package/templates/button.tsx +1 -0
- package/templates/calendar.tsx +45 -0
- package/templates/card.tsx +35 -0
- package/templates/carousel.tsx +39 -0
- package/templates/checkbox.tsx +50 -0
- package/templates/code-block.tsx +1 -0
- package/templates/collapsible.tsx +35 -0
- package/templates/combobox.tsx +154 -0
- package/templates/command.tsx +50 -0
- package/templates/contact-footer.tsx +193 -0
- package/templates/context-menu.tsx +16 -0
- package/templates/dialog.tsx +67 -0
- package/templates/drawer.tsx +12 -0
- package/templates/dropdown-menu.tsx +95 -0
- package/templates/form-input.tsx +64 -0
- package/templates/form.tsx +10 -0
- package/templates/hover-card.tsx +5 -0
- package/templates/input-otp.tsx +6 -0
- package/templates/label.tsx +1 -0
- package/templates/layout-primitives.tsx +11 -0
- package/templates/layouts.tsx +346 -0
- package/templates/lib/utils.ts +49 -0
- package/templates/list-item.tsx +1 -0
- package/templates/list-items.tsx +41 -0
- package/templates/list.tsx +89 -0
- package/templates/logo.tsx +12 -0
- package/templates/marketing-footer.tsx +33 -0
- package/templates/marketing-header.tsx +46 -0
- package/templates/menubar.tsx +16 -0
- package/templates/navigation-menu.tsx +11 -0
- package/templates/pagination.tsx +86 -0
- package/templates/popover.tsx +8 -0
- package/templates/pricing-receipt.tsx +71 -0
- package/templates/pricing-tabs.tsx +60 -0
- package/templates/progress.tsx +29 -0
- package/templates/radio-group.tsx +58 -0
- package/templates/receipt-card.tsx +1 -0
- package/templates/receipt.tsx +269 -0
- package/templates/resizable.tsx +1 -0
- package/templates/scroll-area.tsx +1 -0
- package/templates/select.tsx +110 -0
- package/templates/separator.tsx +1 -0
- package/templates/sheet.tsx +12 -0
- package/templates/sidebar.tsx +15 -0
- package/templates/simple-footer.tsx +43 -0
- package/templates/simple-header.tsx +77 -0
- package/templates/skeleton.tsx +33 -0
- package/templates/slider.tsx +55 -0
- package/templates/styles/dockets.css +104 -0
- package/templates/switch.tsx +49 -0
- package/templates/table.tsx +73 -0
- package/templates/tabs.tsx +61 -0
- package/templates/theme-toggle.tsx +46 -0
- package/templates/toast.tsx +1 -0
- package/templates/toggle-group.tsx +1 -0
- package/templates/toggle.tsx +1 -0
- package/templates/tooltip.tsx +31 -0
- package/templates/tree-view.tsx +1 -0
- package/templates/ui/accordion.tsx +73 -0
- package/templates/ui/alert-dialog.tsx +128 -0
- package/templates/ui/alert.tsx +56 -0
- package/templates/ui/aspect-ratio.tsx +19 -0
- package/templates/ui/avatar.tsx +74 -0
- package/templates/ui/badge.tsx +48 -0
- package/templates/ui/block-loader.tsx +40 -0
- package/templates/ui/button.tsx +77 -0
- package/templates/ui/calendar.tsx +160 -0
- package/templates/ui/card.tsx +73 -0
- package/templates/ui/carousel.tsx +149 -0
- package/templates/ui/checkbox.tsx +33 -0
- package/templates/ui/code-block.tsx +36 -0
- package/templates/ui/collapsible.tsx +48 -0
- package/templates/ui/combobox.tsx +295 -0
- package/templates/ui/command.tsx +148 -0
- package/templates/ui/context-menu.tsx +212 -0
- package/templates/ui/dialog.tsx +138 -0
- package/templates/ui/drawer.tsx +134 -0
- package/templates/ui/dropdown-menu.tsx +254 -0
- package/templates/ui/form.tsx +122 -0
- package/templates/ui/hover-card.tsx +44 -0
- package/templates/ui/input-group.tsx +148 -0
- package/templates/ui/input-otp.tsx +153 -0
- package/templates/ui/input.tsx +20 -0
- package/templates/ui/label.tsx +17 -0
- package/templates/ui/layout.tsx +252 -0
- package/templates/ui/list-item.tsx +50 -0
- package/templates/ui/menubar.tsx +225 -0
- package/templates/ui/navigation-menu.tsx +117 -0
- package/templates/ui/pagination.tsx +110 -0
- package/templates/ui/popover.tsx +77 -0
- package/templates/ui/progress.tsx +37 -0
- package/templates/ui/radio-group.tsx +41 -0
- package/templates/ui/receipt-card.tsx +70 -0
- package/templates/ui/resizable.tsx +140 -0
- package/templates/ui/scroll-area.tsx +64 -0
- package/templates/ui/select.tsx +186 -0
- package/templates/ui/separator.tsx +21 -0
- package/templates/ui/sheet.tsx +134 -0
- package/templates/ui/sidebar.tsx +222 -0
- package/templates/ui/skeleton.tsx +35 -0
- package/templates/ui/slider.tsx +60 -0
- package/templates/ui/switch.tsx +33 -0
- package/templates/ui/table.tsx +114 -0
- package/templates/ui/tabs.tsx +79 -0
- package/templates/ui/textarea.tsx +18 -0
- package/templates/ui/toast.tsx +139 -0
- package/templates/ui/toggle-group.tsx +68 -0
- package/templates/ui/toggle.tsx +47 -0
- package/templates/ui/tooltip.tsx +53 -0
- package/templates/ui/tree-view.tsx +76 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Accordion as AccordionPrimitive } from '@base-ui/react/accordion'
|
|
2
|
+
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react'
|
|
3
|
+
import { cn } from '@/lib/utils'
|
|
4
|
+
|
|
5
|
+
function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
|
|
6
|
+
return (
|
|
7
|
+
<AccordionPrimitive.Root
|
|
8
|
+
data-slot="accordion"
|
|
9
|
+
className={cn(
|
|
10
|
+
'flex w-full flex-col border-[length:var(--border-width)] border-foreground',
|
|
11
|
+
className,
|
|
12
|
+
)}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
|
|
19
|
+
return (
|
|
20
|
+
<AccordionPrimitive.Item
|
|
21
|
+
data-slot="accordion-item"
|
|
22
|
+
// Border-never-stack: items use border-b only; outer container holds border-t + sides
|
|
23
|
+
className={cn('border-b-[length:var(--border-width)] border-foreground last:border-b-0', className)}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function AccordionTrigger({ className, children, ...props }: AccordionPrimitive.Trigger.Props) {
|
|
30
|
+
return (
|
|
31
|
+
<AccordionPrimitive.Header className="flex">
|
|
32
|
+
<AccordionPrimitive.Trigger
|
|
33
|
+
data-slot="accordion-trigger"
|
|
34
|
+
className={cn(
|
|
35
|
+
'group/accordion-trigger relative flex flex-1 items-start justify-between rounded-[var(--radius)] border border-transparent py-2.5 text-left text-xs font-medium hover:underline aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground',
|
|
36
|
+
className,
|
|
37
|
+
)}
|
|
38
|
+
{...props}
|
|
39
|
+
>
|
|
40
|
+
{children}
|
|
41
|
+
<ChevronDownIcon
|
|
42
|
+
data-slot="accordion-trigger-icon"
|
|
43
|
+
className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden"
|
|
44
|
+
/>
|
|
45
|
+
<ChevronUpIcon
|
|
46
|
+
data-slot="accordion-trigger-icon"
|
|
47
|
+
className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline"
|
|
48
|
+
/>
|
|
49
|
+
</AccordionPrimitive.Trigger>
|
|
50
|
+
</AccordionPrimitive.Header>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function AccordionContent({ className, children, ...props }: AccordionPrimitive.Panel.Props) {
|
|
55
|
+
return (
|
|
56
|
+
<AccordionPrimitive.Panel
|
|
57
|
+
data-slot="accordion-content"
|
|
58
|
+
className="overflow-hidden text-xs data-open:animate-accordion-down data-closed:animate-accordion-up"
|
|
59
|
+
{...props}
|
|
60
|
+
>
|
|
61
|
+
<div
|
|
62
|
+
className={cn(
|
|
63
|
+
'h-(--accordion-panel-height) pt-0 pb-2.5 data-ending-style:h-0 data-starting-style:h-0 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4',
|
|
64
|
+
className,
|
|
65
|
+
)}
|
|
66
|
+
>
|
|
67
|
+
{children}
|
|
68
|
+
</div>
|
|
69
|
+
</AccordionPrimitive.Panel>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { AlertDialog as AlertDialogPrimitive } from '@base-ui/react/alert-dialog'
|
|
4
|
+
import type * as React from 'react'
|
|
5
|
+
import { cn } from '@/lib/utils'
|
|
6
|
+
|
|
7
|
+
function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
|
|
8
|
+
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
|
|
12
|
+
return <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
|
|
16
|
+
return <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function AlertDialogOverlay({ className, ...props }: AlertDialogPrimitive.Backdrop.Props) {
|
|
20
|
+
return (
|
|
21
|
+
<AlertDialogPrimitive.Backdrop
|
|
22
|
+
data-slot="alert-dialog-overlay"
|
|
23
|
+
className={cn(
|
|
24
|
+
'fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0',
|
|
25
|
+
className,
|
|
26
|
+
)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function AlertDialogContent({ className, children, ...props }: AlertDialogPrimitive.Popup.Props) {
|
|
33
|
+
return (
|
|
34
|
+
<AlertDialogPortal>
|
|
35
|
+
<AlertDialogOverlay />
|
|
36
|
+
<AlertDialogPrimitive.Popup
|
|
37
|
+
data-slot="alert-dialog-content"
|
|
38
|
+
className={cn(
|
|
39
|
+
'fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-[var(--radius)] border-[length:var(--border-width)] border-foreground bg-card p-4 text-xs/relaxed text-card-foreground duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95',
|
|
40
|
+
className,
|
|
41
|
+
)}
|
|
42
|
+
{...props}
|
|
43
|
+
>
|
|
44
|
+
{children}
|
|
45
|
+
</AlertDialogPrimitive.Popup>
|
|
46
|
+
</AlertDialogPortal>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
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-1 text-left', className)}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
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
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function AlertDialogTitle({ className, ...props }: AlertDialogPrimitive.Title.Props) {
|
|
71
|
+
return (
|
|
72
|
+
<AlertDialogPrimitive.Title
|
|
73
|
+
data-slot="alert-dialog-title"
|
|
74
|
+
className={cn('text-sm font-medium uppercase tracking-wider', className)}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function AlertDialogDescription({ className, ...props }: AlertDialogPrimitive.Description.Props) {
|
|
81
|
+
return (
|
|
82
|
+
<AlertDialogPrimitive.Description
|
|
83
|
+
data-slot="alert-dialog-description"
|
|
84
|
+
className={cn('text-xs/relaxed text-muted-foreground', className)}
|
|
85
|
+
{...props}
|
|
86
|
+
/>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function AlertDialogAction({ className, ...props }: AlertDialogPrimitive.Close.Props) {
|
|
91
|
+
return (
|
|
92
|
+
<AlertDialogPrimitive.Close
|
|
93
|
+
data-slot="alert-dialog-action"
|
|
94
|
+
className={cn(
|
|
95
|
+
'inline-flex h-10 items-center justify-center rounded-[var(--radius)] bg-foreground px-6 py-2 text-xs font-medium uppercase tracking-wider text-background hover:bg-foreground/90 disabled:pointer-events-none disabled:opacity-50',
|
|
96
|
+
className,
|
|
97
|
+
)}
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function AlertDialogCancel({ className, ...props }: AlertDialogPrimitive.Close.Props) {
|
|
104
|
+
return (
|
|
105
|
+
<AlertDialogPrimitive.Close
|
|
106
|
+
data-slot="alert-dialog-cancel"
|
|
107
|
+
className={cn(
|
|
108
|
+
'inline-flex h-10 items-center justify-center rounded-[var(--radius)] border-[length:var(--border-width)] border-dashed border-foreground bg-transparent px-6 py-2 text-xs font-medium uppercase tracking-wider hover:bg-foreground/5 disabled:pointer-events-none disabled:opacity-50',
|
|
109
|
+
className,
|
|
110
|
+
)}
|
|
111
|
+
{...props}
|
|
112
|
+
/>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export {
|
|
117
|
+
AlertDialog,
|
|
118
|
+
AlertDialogTrigger,
|
|
119
|
+
AlertDialogPortal,
|
|
120
|
+
AlertDialogOverlay,
|
|
121
|
+
AlertDialogContent,
|
|
122
|
+
AlertDialogHeader,
|
|
123
|
+
AlertDialogFooter,
|
|
124
|
+
AlertDialogTitle,
|
|
125
|
+
AlertDialogDescription,
|
|
126
|
+
AlertDialogAction,
|
|
127
|
+
AlertDialogCancel,
|
|
128
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import { cn } from '@/lib/utils'
|
|
4
|
+
|
|
5
|
+
const alertVariants = cva(
|
|
6
|
+
'relative w-full rounded-[var(--radius)] border-[length:var(--border-width)] border-dashed p-4 text-xs/relaxed [&>svg]:absolute [&>svg]:top-4 [&>svg]:left-4 [&>svg]:size-4 [&>svg~*]:pl-6',
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: 'border-foreground bg-card text-card-foreground',
|
|
11
|
+
destructive:
|
|
12
|
+
'border-destructive/50 bg-destructive/5 text-destructive dark:border-destructive [&>svg]:text-destructive',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultVariants: {
|
|
16
|
+
variant: 'default',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
function Alert({
|
|
22
|
+
className,
|
|
23
|
+
variant,
|
|
24
|
+
...props
|
|
25
|
+
}: React.ComponentProps<'div'> & VariantProps<typeof alertVariants>) {
|
|
26
|
+
return (
|
|
27
|
+
<div
|
|
28
|
+
data-slot="alert"
|
|
29
|
+
role="alert"
|
|
30
|
+
className={cn(alertVariants({ variant }), className)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
data-slot="alert-title"
|
|
40
|
+
className={cn('mb-1 font-medium uppercase tracking-wider leading-none', className)}
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function AlertDescription({ className, ...props }: React.ComponentProps<'div'>) {
|
|
47
|
+
return (
|
|
48
|
+
<div
|
|
49
|
+
data-slot="alert-description"
|
|
50
|
+
className={cn('text-xs/relaxed [&_p]:leading-relaxed', className)}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { Alert, AlertTitle, AlertDescription, alertVariants }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { cn } from '@/lib/utils'
|
|
3
|
+
|
|
4
|
+
interface AspectRatioProps extends React.ComponentProps<'div'> {
|
|
5
|
+
ratio?: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function AspectRatio({ ratio = 1, className, style, ...props }: AspectRatioProps) {
|
|
9
|
+
return (
|
|
10
|
+
<div
|
|
11
|
+
data-slot="aspect-ratio"
|
|
12
|
+
style={{ aspectRatio: ratio, ...style }}
|
|
13
|
+
className={cn('relative w-full overflow-hidden', className)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { AspectRatio }
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { cn } from '@/lib/utils'
|
|
3
|
+
|
|
4
|
+
interface AvatarProps extends React.ComponentProps<'span'> {
|
|
5
|
+
src?: string
|
|
6
|
+
alt?: string
|
|
7
|
+
fallback?: React.ReactNode
|
|
8
|
+
size?: 'sm' | 'default' | 'lg'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const sizeClasses = {
|
|
12
|
+
sm: 'size-7 text-[10px]',
|
|
13
|
+
default: 'size-9 text-xs',
|
|
14
|
+
lg: 'size-12 text-sm',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function Avatar({ className, src, alt, fallback, size = 'default', ...props }: AvatarProps) {
|
|
18
|
+
const [imgError, setImgError] = React.useState(false)
|
|
19
|
+
const showFallback = !src || imgError
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<span
|
|
23
|
+
data-slot="avatar"
|
|
24
|
+
className={cn(
|
|
25
|
+
'relative inline-flex shrink-0 overflow-hidden rounded-[var(--radius)] border-[length:var(--border-width)] border-dashed border-foreground',
|
|
26
|
+
sizeClasses[size],
|
|
27
|
+
className,
|
|
28
|
+
)}
|
|
29
|
+
{...props}
|
|
30
|
+
>
|
|
31
|
+
{!showFallback && (
|
|
32
|
+
<img
|
|
33
|
+
src={src}
|
|
34
|
+
alt={alt ?? ''}
|
|
35
|
+
className="h-full w-full object-cover"
|
|
36
|
+
onError={() => setImgError(true)}
|
|
37
|
+
/>
|
|
38
|
+
)}
|
|
39
|
+
{showFallback && (
|
|
40
|
+
<span
|
|
41
|
+
data-slot="avatar-fallback"
|
|
42
|
+
className="flex h-full w-full items-center justify-center bg-muted font-medium uppercase tracking-wider text-muted-foreground"
|
|
43
|
+
>
|
|
44
|
+
{fallback}
|
|
45
|
+
</span>
|
|
46
|
+
)}
|
|
47
|
+
</span>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function AvatarImage({ className, ...props }: React.ComponentProps<'img'>) {
|
|
52
|
+
return (
|
|
53
|
+
<img
|
|
54
|
+
data-slot="avatar-image"
|
|
55
|
+
className={cn('h-full w-full object-cover', className)}
|
|
56
|
+
{...props}
|
|
57
|
+
/>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function AvatarFallback({ className, ...props }: React.ComponentProps<'span'>) {
|
|
62
|
+
return (
|
|
63
|
+
<span
|
|
64
|
+
data-slot="avatar-fallback"
|
|
65
|
+
className={cn(
|
|
66
|
+
'flex h-full w-full items-center justify-center bg-muted font-medium uppercase tracking-wider text-muted-foreground',
|
|
67
|
+
className,
|
|
68
|
+
)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { Avatar, AvatarImage, AvatarFallback }
|
|
@@ -0,0 +1,48 @@
|
|
|
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-[var(--radius)] border border-transparent px-2 py-0.5 text-xs font-medium uppercase tracking-wider whitespace-nowrap has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 [&>svg]:pointer-events-none [&>svg]:size-3!',
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: 'bg-foreground text-background',
|
|
13
|
+
secondary: 'bg-card text-card-foreground',
|
|
14
|
+
destructive: 'bg-destructive text-destructive-foreground',
|
|
15
|
+
outline: 'border-foreground border-dashed text-foreground',
|
|
16
|
+
ghost: 'text-foreground/60',
|
|
17
|
+
link: 'text-foreground underline-offset-4 underline',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: {
|
|
21
|
+
variant: 'default',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
function Badge({
|
|
27
|
+
className,
|
|
28
|
+
variant = 'default',
|
|
29
|
+
render,
|
|
30
|
+
...props
|
|
31
|
+
}: useRender.ComponentProps<'span'> & VariantProps<typeof badgeVariants>) {
|
|
32
|
+
return useRender({
|
|
33
|
+
defaultTagName: 'span',
|
|
34
|
+
props: mergeProps<'span'>(
|
|
35
|
+
{
|
|
36
|
+
className: cn(badgeVariants({ variant }), className),
|
|
37
|
+
},
|
|
38
|
+
props,
|
|
39
|
+
),
|
|
40
|
+
render,
|
|
41
|
+
state: {
|
|
42
|
+
slot: 'badge',
|
|
43
|
+
variant,
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { Badge, badgeVariants }
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { cn } from '@/lib/utils'
|
|
3
|
+
|
|
4
|
+
const SEQUENCES = [
|
|
5
|
+
['⠁', '⠂', '⠄', '⡀', '⢀', '⠠', '⠐', '⠈'],
|
|
6
|
+
['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'],
|
|
7
|
+
['▖', '▘', '▝', '▗'],
|
|
8
|
+
['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█', '▇', '▆', '▅', '▄', '▃', '▁'],
|
|
9
|
+
['▉', '▊', '▋', '▌', '▍', '▎', '▏', '▎', '▍', '▌', '▋', '▊', '▉'],
|
|
10
|
+
['←', '↖', '↑', '↗', '→', '↘', '↓', '↙'],
|
|
11
|
+
['┤', '┘', '┴', '└', '├', '┌', '┬', '┐'],
|
|
12
|
+
['◢', '◣', '◤', '◥'],
|
|
13
|
+
['◰', '◳', '◲', '◱'],
|
|
14
|
+
['◴', '◷', '◶', '◵'],
|
|
15
|
+
['◐', '◓', '◑', '◒'],
|
|
16
|
+
] as const
|
|
17
|
+
|
|
18
|
+
interface BlockLoaderProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'children'> {
|
|
19
|
+
mode?: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function BlockLoader({ mode = 1, className, ...props }: BlockLoaderProps) {
|
|
23
|
+
const sequence = SEQUENCES[mode] ?? SEQUENCES[0]
|
|
24
|
+
const [index, setIndex] = React.useState(0)
|
|
25
|
+
|
|
26
|
+
React.useEffect(() => {
|
|
27
|
+
const id = window.setInterval(() => {
|
|
28
|
+
setIndex((prev) => (prev + 1) % sequence.length)
|
|
29
|
+
}, 100)
|
|
30
|
+
return () => clearInterval(id)
|
|
31
|
+
}, [sequence.length])
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<span data-slot="block-loader" className={cn('inline-block w-[1em] text-center', className)} aria-label="Loading" {...props}>
|
|
35
|
+
{sequence[index]}
|
|
36
|
+
</span>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export { BlockLoader, SEQUENCES }
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import { cn } from '@/lib/utils'
|
|
4
|
+
|
|
5
|
+
const buttonVariants = cva(
|
|
6
|
+
'inline-flex items-center justify-center gap-2 whitespace-nowrap text-xs font-medium uppercase tracking-wider disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: 'bg-foreground text-background hover:bg-foreground/90',
|
|
11
|
+
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
12
|
+
outline:
|
|
13
|
+
'border-[length:var(--border-width)] border-dashed border-foreground bg-transparent hover:bg-foreground/5',
|
|
14
|
+
secondary: 'bg-card text-card-foreground',
|
|
15
|
+
ghost: 'hover:text-foreground',
|
|
16
|
+
link: 'text-foreground underline-offset-4 hover:underline',
|
|
17
|
+
},
|
|
18
|
+
size: {
|
|
19
|
+
default: 'h-10 px-6 py-2',
|
|
20
|
+
sm: 'h-9 px-4',
|
|
21
|
+
lg: 'h-11 px-6',
|
|
22
|
+
xl: 'h-12 px-8',
|
|
23
|
+
icon: 'h-10 w-10',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
variant: 'default',
|
|
28
|
+
size: 'default',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
export interface ButtonProps
|
|
34
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
35
|
+
VariantProps<typeof buttonVariants> {
|
|
36
|
+
asChild?: boolean
|
|
37
|
+
render?: React.ReactElement<Record<string, unknown>>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
41
|
+
({ className, variant, size, asChild = false, render, children, ...props }, ref) => {
|
|
42
|
+
const classes = cn(buttonVariants({ variant, size, className }))
|
|
43
|
+
|
|
44
|
+
// Base UI render prop pattern (preferred)
|
|
45
|
+
if (render) {
|
|
46
|
+
const renderProps = render.props as Record<string, unknown>
|
|
47
|
+
return React.cloneElement(render, {
|
|
48
|
+
...props,
|
|
49
|
+
ref,
|
|
50
|
+
className: cn(classes, renderProps.className as string | undefined),
|
|
51
|
+
children: (renderProps.children as React.ReactNode) ?? children,
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Legacy asChild pattern (backwards-compatible without Radix Slot)
|
|
56
|
+
if (asChild && React.isValidElement(children)) {
|
|
57
|
+
const childProps = (children as React.ReactElement<Record<string, unknown>>).props as Record<
|
|
58
|
+
string,
|
|
59
|
+
unknown
|
|
60
|
+
>
|
|
61
|
+
return React.cloneElement(children as React.ReactElement<Record<string, unknown>>, {
|
|
62
|
+
...props,
|
|
63
|
+
ref,
|
|
64
|
+
className: cn(classes, childProps.className as string | undefined),
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<button data-slot="button" className={classes} ref={ref} {...props}>
|
|
70
|
+
{children}
|
|
71
|
+
</button>
|
|
72
|
+
)
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
Button.displayName = 'Button'
|
|
76
|
+
|
|
77
|
+
export { Button, buttonVariants }
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react'
|
|
5
|
+
import { cn } from '@/lib/utils'
|
|
6
|
+
|
|
7
|
+
const DAYS = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT']
|
|
8
|
+
const MONTHS = [
|
|
9
|
+
'JANUARY', 'FEBRUARY', 'MARCH', 'APRIL', 'MAY', 'JUNE',
|
|
10
|
+
'JULY', 'AUGUST', 'SEPTEMBER', 'OCTOBER', 'NOVEMBER', 'DECEMBER',
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
interface CalendarProps {
|
|
14
|
+
className?: string
|
|
15
|
+
selected?: Date
|
|
16
|
+
onSelect?: (date: Date) => void
|
|
17
|
+
defaultMonth?: Date
|
|
18
|
+
disabled?: (date: Date) => boolean
|
|
19
|
+
mode?: 'single'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function Calendar({
|
|
23
|
+
className,
|
|
24
|
+
selected,
|
|
25
|
+
onSelect,
|
|
26
|
+
defaultMonth,
|
|
27
|
+
disabled,
|
|
28
|
+
}: CalendarProps) {
|
|
29
|
+
const today = new Date()
|
|
30
|
+
const [viewDate, setViewDate] = React.useState(
|
|
31
|
+
defaultMonth ?? selected ?? today,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
const year = viewDate.getFullYear()
|
|
35
|
+
const month = viewDate.getMonth()
|
|
36
|
+
|
|
37
|
+
const firstDay = new Date(year, month, 1).getDay()
|
|
38
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate()
|
|
39
|
+
|
|
40
|
+
const cells: (number | null)[] = [
|
|
41
|
+
...Array(firstDay).fill(null),
|
|
42
|
+
...Array.from({ length: daysInMonth }, (_, i) => i + 1),
|
|
43
|
+
]
|
|
44
|
+
// Pad to complete last row
|
|
45
|
+
while (cells.length % 7 !== 0) cells.push(null)
|
|
46
|
+
|
|
47
|
+
const prevMonth = () => setViewDate(new Date(year, month - 1, 1))
|
|
48
|
+
const nextMonth = () => setViewDate(new Date(year, month + 1, 1))
|
|
49
|
+
|
|
50
|
+
const isSelected = (day: number) => {
|
|
51
|
+
if (!selected) return false
|
|
52
|
+
return (
|
|
53
|
+
selected.getFullYear() === year &&
|
|
54
|
+
selected.getMonth() === month &&
|
|
55
|
+
selected.getDate() === day
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const isToday = (day: number) =>
|
|
60
|
+
today.getFullYear() === year &&
|
|
61
|
+
today.getMonth() === month &&
|
|
62
|
+
today.getDate() === day
|
|
63
|
+
|
|
64
|
+
const isDisabled = (day: number) => {
|
|
65
|
+
if (!disabled) return false
|
|
66
|
+
return disabled(new Date(year, month, day))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div
|
|
71
|
+
data-slot="calendar"
|
|
72
|
+
className={cn(
|
|
73
|
+
'w-full max-w-xs rounded-[var(--radius)] border-[length:var(--border-width)] border-foreground bg-card text-card-foreground',
|
|
74
|
+
className,
|
|
75
|
+
)}
|
|
76
|
+
>
|
|
77
|
+
{/* Header — no stacking: outer border owns top+sides */}
|
|
78
|
+
<div className="flex items-center justify-between border-b-[length:var(--border-width)] border-foreground px-3 py-2">
|
|
79
|
+
<button
|
|
80
|
+
type="button"
|
|
81
|
+
onClick={prevMonth}
|
|
82
|
+
className="flex size-6 items-center justify-center text-muted-foreground hover:text-foreground"
|
|
83
|
+
aria-label="Previous month"
|
|
84
|
+
>
|
|
85
|
+
<ChevronLeftIcon className="size-3.5" />
|
|
86
|
+
</button>
|
|
87
|
+
<span className="text-xs font-medium uppercase tracking-wider">
|
|
88
|
+
{MONTHS[month]} {year}
|
|
89
|
+
</span>
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
onClick={nextMonth}
|
|
93
|
+
className="flex size-6 items-center justify-center text-muted-foreground hover:text-foreground"
|
|
94
|
+
aria-label="Next month"
|
|
95
|
+
>
|
|
96
|
+
<ChevronRightIcon className="size-3.5" />
|
|
97
|
+
</button>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
{/* Day-of-week header */}
|
|
101
|
+
{/* Newspaper grid: container holds border-t + border-l; cells add border-r + border-b */}
|
|
102
|
+
<div className="grid grid-cols-7 border-b-[length:var(--border-width)] border-foreground">
|
|
103
|
+
{DAYS.map((d) => (
|
|
104
|
+
<div
|
|
105
|
+
key={d}
|
|
106
|
+
className="flex h-7 items-center justify-center text-[10px] font-medium uppercase tracking-wider text-muted-foreground"
|
|
107
|
+
>
|
|
108
|
+
{d.slice(0, 2)}
|
|
109
|
+
</div>
|
|
110
|
+
))}
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
{/* Day cells */}
|
|
114
|
+
<div className="grid grid-cols-7">
|
|
115
|
+
{cells.map((day, idx) => {
|
|
116
|
+
if (day === null) {
|
|
117
|
+
return (
|
|
118
|
+
<div
|
|
119
|
+
key={`empty-${idx}`}
|
|
120
|
+
className={cn(
|
|
121
|
+
'h-8',
|
|
122
|
+
// Newspaper border rule: right+bottom only
|
|
123
|
+
(idx + 1) % 7 !== 0 && 'border-r-[length:var(--border-width)] border-foreground/20',
|
|
124
|
+
idx < cells.length - 7 && 'border-b-[length:var(--border-width)] border-foreground/20',
|
|
125
|
+
)}
|
|
126
|
+
/>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
const sel = isSelected(day)
|
|
130
|
+
const tod = isToday(day)
|
|
131
|
+
const dis = isDisabled(day)
|
|
132
|
+
return (
|
|
133
|
+
<button
|
|
134
|
+
key={day}
|
|
135
|
+
type="button"
|
|
136
|
+
disabled={dis}
|
|
137
|
+
onClick={() => !dis && onSelect?.(new Date(year, month, day))}
|
|
138
|
+
className={cn(
|
|
139
|
+
'flex h-8 items-center justify-center text-xs',
|
|
140
|
+
// Newspaper border rule
|
|
141
|
+
(idx + 1) % 7 !== 0 && 'border-r-[length:var(--border-width)] border-foreground/20',
|
|
142
|
+
idx < cells.length - 7 && 'border-b-[length:var(--border-width)] border-foreground/20',
|
|
143
|
+
sel
|
|
144
|
+
? 'bg-foreground text-background'
|
|
145
|
+
: tod
|
|
146
|
+
? 'border-[length:var(--border-width)] border-dashed border-foreground font-medium'
|
|
147
|
+
: 'hover:bg-accent hover:text-accent-foreground',
|
|
148
|
+
dis && 'pointer-events-none opacity-30',
|
|
149
|
+
)}
|
|
150
|
+
>
|
|
151
|
+
{day}
|
|
152
|
+
</button>
|
|
153
|
+
)
|
|
154
|
+
})}
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export { Calendar }
|