@modern-admin/ui 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/components/accordion.d.ts +7 -0
- package/dist/components/accordion.d.ts.map +1 -0
- package/dist/components/accordion.jsx +19 -0
- package/dist/components/accordion.jsx.map +1 -0
- package/dist/components/alert-dialog.d.ts +22 -0
- package/dist/components/alert-dialog.d.ts.map +1 -0
- package/dist/components/alert-dialog.jsx +27 -0
- package/dist/components/alert-dialog.jsx.map +1 -0
- package/dist/components/audit-timeline.d.ts +24 -0
- package/dist/components/audit-timeline.d.ts.map +1 -0
- package/dist/components/audit-timeline.jsx +60 -0
- package/dist/components/audit-timeline.jsx.map +1 -0
- package/dist/components/avatar.d.ts +6 -0
- package/dist/components/avatar.d.ts.map +1 -0
- package/dist/components/avatar.jsx +10 -0
- package/dist/components/avatar.jsx.map +1 -0
- package/dist/components/badge.d.ts +10 -0
- package/dist/components/badge.d.ts.map +1 -0
- package/dist/components/badge.jsx +19 -0
- package/dist/components/badge.jsx.map +1 -0
- package/dist/components/breadcrumb.d.ts +17 -0
- package/dist/components/breadcrumb.d.ts.map +1 -0
- package/dist/components/breadcrumb.jsx +27 -0
- package/dist/components/breadcrumb.jsx.map +1 -0
- package/dist/components/button.d.ts +12 -0
- package/dist/components/button.d.ts.map +1 -0
- package/dist/components/button.jsx +37 -0
- package/dist/components/button.jsx.map +1 -0
- package/dist/components/calendar.d.ts +9 -0
- package/dist/components/calendar.d.ts.map +1 -0
- package/dist/components/calendar.jsx +102 -0
- package/dist/components/calendar.jsx.map +1 -0
- package/dist/components/card.d.ts +8 -0
- package/dist/components/card.d.ts.map +1 -0
- package/dist/components/card.jsx +18 -0
- package/dist/components/card.jsx.map +1 -0
- package/dist/components/chart.d.ts +97 -0
- package/dist/components/chart.d.ts.map +1 -0
- package/dist/components/chart.jsx +233 -0
- package/dist/components/chart.jsx.map +1 -0
- package/dist/components/checkbox.d.ts +4 -0
- package/dist/components/checkbox.d.ts.map +1 -0
- package/dist/components/checkbox.jsx +11 -0
- package/dist/components/checkbox.jsx.map +1 -0
- package/dist/components/combobox.d.ts +46 -0
- package/dist/components/combobox.d.ts.map +1 -0
- package/dist/components/combobox.jsx +145 -0
- package/dist/components/combobox.jsx.map +1 -0
- package/dist/components/command.d.ts +80 -0
- package/dist/components/command.d.ts.map +1 -0
- package/dist/components/command.jsx +32 -0
- package/dist/components/command.jsx.map +1 -0
- package/dist/components/date-picker.d.ts +24 -0
- package/dist/components/date-picker.d.ts.map +1 -0
- package/dist/components/date-picker.jsx +149 -0
- package/dist/components/date-picker.jsx.map +1 -0
- package/dist/components/date-range-input.d.ts +22 -0
- package/dist/components/date-range-input.d.ts.map +1 -0
- package/dist/components/date-range-input.jsx +202 -0
- package/dist/components/date-range-input.jsx.map +1 -0
- package/dist/components/dialog.d.ts +19 -0
- package/dist/components/dialog.d.ts.map +1 -0
- package/dist/components/dialog.jsx +30 -0
- package/dist/components/dialog.jsx.map +1 -0
- package/dist/components/diff-view.d.ts +24 -0
- package/dist/components/diff-view.d.ts.map +1 -0
- package/dist/components/diff-view.jsx +69 -0
- package/dist/components/diff-view.jsx.map +1 -0
- package/dist/components/dropdown-menu.d.ts +27 -0
- package/dist/components/dropdown-menu.d.ts.map +1 -0
- package/dist/components/dropdown-menu.jsx +48 -0
- package/dist/components/dropdown-menu.jsx.map +1 -0
- package/dist/components/empty.d.ts +15 -0
- package/dist/components/empty.d.ts.map +1 -0
- package/dist/components/empty.jsx +27 -0
- package/dist/components/empty.jsx.map +1 -0
- package/dist/components/field.d.ts +23 -0
- package/dist/components/field.d.ts.map +1 -0
- package/dist/components/field.jsx +60 -0
- package/dist/components/field.jsx.map +1 -0
- package/dist/components/file-input.d.ts +50 -0
- package/dist/components/file-input.d.ts.map +1 -0
- package/dist/components/file-input.jsx +104 -0
- package/dist/components/file-input.jsx.map +1 -0
- package/dist/components/form.d.ts +20 -0
- package/dist/components/form.d.ts.map +1 -0
- package/dist/components/form.jsx +66 -0
- package/dist/components/form.jsx.map +1 -0
- package/dist/components/info-tooltip.d.ts +11 -0
- package/dist/components/info-tooltip.d.ts.map +1 -0
- package/dist/components/info-tooltip.jsx +17 -0
- package/dist/components/info-tooltip.jsx.map +1 -0
- package/dist/components/input.d.ts +13 -0
- package/dist/components/input.d.ts.map +1 -0
- package/dist/components/input.jsx +19 -0
- package/dist/components/input.jsx.map +1 -0
- package/dist/components/json-editor.d.ts +23 -0
- package/dist/components/json-editor.d.ts.map +1 -0
- package/dist/components/json-editor.jsx +143 -0
- package/dist/components/json-editor.jsx.map +1 -0
- package/dist/components/kbd.d.ts +15 -0
- package/dist/components/kbd.d.ts.map +1 -0
- package/dist/components/kbd.jsx +23 -0
- package/dist/components/kbd.jsx.map +1 -0
- package/dist/components/key-value-editor.d.ts +92 -0
- package/dist/components/key-value-editor.d.ts.map +1 -0
- package/dist/components/key-value-editor.jsx +187 -0
- package/dist/components/key-value-editor.jsx.map +1 -0
- package/dist/components/keyboard-shortcuts-help.d.ts +17 -0
- package/dist/components/keyboard-shortcuts-help.d.ts.map +1 -0
- package/dist/components/keyboard-shortcuts-help.jsx +97 -0
- package/dist/components/keyboard-shortcuts-help.jsx.map +1 -0
- package/dist/components/label.d.ts +5 -0
- package/dist/components/label.d.ts.map +1 -0
- package/dist/components/label.jsx +8 -0
- package/dist/components/label.jsx.map +1 -0
- package/dist/components/media-preview.d.ts +30 -0
- package/dist/components/media-preview.d.ts.map +1 -0
- package/dist/components/media-preview.jsx +189 -0
- package/dist/components/media-preview.jsx.map +1 -0
- package/dist/components/multi-file-input.d.ts +76 -0
- package/dist/components/multi-file-input.d.ts.map +1 -0
- package/dist/components/multi-file-input.jsx +131 -0
- package/dist/components/multi-file-input.jsx.map +1 -0
- package/dist/components/password-input.d.ts +10 -0
- package/dist/components/password-input.d.ts.map +1 -0
- package/dist/components/password-input.jsx +18 -0
- package/dist/components/password-input.jsx.map +1 -0
- package/dist/components/popover.d.ts +7 -0
- package/dist/components/popover.d.ts.map +1 -0
- package/dist/components/popover.jsx +11 -0
- package/dist/components/popover.jsx.map +1 -0
- package/dist/components/revision-timeline.d.ts +30 -0
- package/dist/components/revision-timeline.d.ts.map +1 -0
- package/dist/components/revision-timeline.jsx +42 -0
- package/dist/components/revision-timeline.jsx.map +1 -0
- package/dist/components/richtext-editor.d.ts +43 -0
- package/dist/components/richtext-editor.d.ts.map +1 -0
- package/dist/components/richtext-editor.jsx +319 -0
- package/dist/components/richtext-editor.jsx.map +1 -0
- package/dist/components/richtext-mode.d.ts +23 -0
- package/dist/components/richtext-mode.d.ts.map +1 -0
- package/dist/components/richtext-mode.js +36 -0
- package/dist/components/richtext-mode.js.map +1 -0
- package/dist/components/richtext-render.d.ts +8 -0
- package/dist/components/richtext-render.d.ts.map +1 -0
- package/dist/components/richtext-render.jsx +33 -0
- package/dist/components/richtext-render.jsx.map +1 -0
- package/dist/components/richtext-sync.d.ts +37 -0
- package/dist/components/richtext-sync.d.ts.map +1 -0
- package/dist/components/richtext-sync.js +46 -0
- package/dist/components/richtext-sync.js.map +1 -0
- package/dist/components/scroll-area.d.ts +5 -0
- package/dist/components/scroll-area.d.ts.map +1 -0
- package/dist/components/scroll-area.jsx +16 -0
- package/dist/components/scroll-area.jsx.map +1 -0
- package/dist/components/select.d.ts +36 -0
- package/dist/components/select.d.ts.map +1 -0
- package/dist/components/select.jsx +87 -0
- package/dist/components/select.jsx.map +1 -0
- package/dist/components/separator.d.ts +4 -0
- package/dist/components/separator.d.ts.map +1 -0
- package/dist/components/separator.jsx +6 -0
- package/dist/components/separator.jsx.map +1 -0
- package/dist/components/sheet.d.ts +29 -0
- package/dist/components/sheet.d.ts.map +1 -0
- package/dist/components/sheet.jsx +44 -0
- package/dist/components/sheet.jsx.map +1 -0
- package/dist/components/sidebar.d.ts +70 -0
- package/dist/components/sidebar.d.ts.map +1 -0
- package/dist/components/sidebar.jsx +245 -0
- package/dist/components/sidebar.jsx.map +1 -0
- package/dist/components/skeleton.d.ts +3 -0
- package/dist/components/skeleton.d.ts.map +1 -0
- package/dist/components/skeleton.jsx +6 -0
- package/dist/components/skeleton.jsx.map +1 -0
- package/dist/components/sonner.d.ts +6 -0
- package/dist/components/sonner.d.ts.map +1 -0
- package/dist/components/sonner.jsx +29 -0
- package/dist/components/sonner.jsx.map +1 -0
- package/dist/components/switch.d.ts +4 -0
- package/dist/components/switch.d.ts.map +1 -0
- package/dist/components/switch.jsx +8 -0
- package/dist/components/switch.jsx.map +1 -0
- package/dist/components/table.d.ts +10 -0
- package/dist/components/table.d.ts.map +1 -0
- package/dist/components/table.jsx +21 -0
- package/dist/components/table.jsx.map +1 -0
- package/dist/components/tabs.d.ts +7 -0
- package/dist/components/tabs.d.ts.map +1 -0
- package/dist/components/tabs.jsx +14 -0
- package/dist/components/tabs.jsx.map +1 -0
- package/dist/components/textarea.d.ts +4 -0
- package/dist/components/textarea.d.ts.map +1 -0
- package/dist/components/textarea.jsx +5 -0
- package/dist/components/textarea.jsx.map +1 -0
- package/dist/components/tooltip.d.ts +7 -0
- package/dist/components/tooltip.d.ts.map +1 -0
- package/dist/components/tooltip.jsx +11 -0
- package/dist/components/tooltip.jsx.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/theme.d.ts +11 -0
- package/dist/lib/theme.d.ts.map +1 -0
- package/dist/lib/theme.js +44 -0
- package/dist/lib/theme.js.map +1 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +6 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/styles.css +242 -0
- package/package.json +85 -0
- package/src/components/accordion.tsx +48 -0
- package/src/components/alert-dialog.tsx +113 -0
- package/src/components/audit-timeline.tsx +102 -0
- package/src/components/avatar.tsx +42 -0
- package/src/components/badge.tsx +34 -0
- package/src/components/breadcrumb.tsx +99 -0
- package/src/components/button.tsx +58 -0
- package/src/components/calendar.tsx +176 -0
- package/src/components/card.tsx +60 -0
- package/src/components/chart.tsx +558 -0
- package/src/components/checkbox.tsx +23 -0
- package/src/components/combobox.tsx +264 -0
- package/src/components/command.tsx +120 -0
- package/src/components/date-picker.tsx +221 -0
- package/src/components/date-range-input.tsx +295 -0
- package/src/components/dialog.tsx +94 -0
- package/src/components/diff-view.tsx +182 -0
- package/src/components/dropdown-menu.tsx +165 -0
- package/src/components/empty.tsx +100 -0
- package/src/components/field.tsx +168 -0
- package/src/components/file-input.tsx +233 -0
- package/src/components/form.tsx +152 -0
- package/src/components/info-tooltip.tsx +40 -0
- package/src/components/input.tsx +55 -0
- package/src/components/json-editor.tsx +210 -0
- package/src/components/kbd.tsx +35 -0
- package/src/components/key-value-editor.tsx +423 -0
- package/src/components/keyboard-shortcuts-help.tsx +136 -0
- package/src/components/label.tsx +16 -0
- package/src/components/media-preview.tsx +278 -0
- package/src/components/multi-file-input.tsx +315 -0
- package/src/components/password-input.tsx +50 -0
- package/src/components/popover.tsx +26 -0
- package/src/components/revision-timeline.tsx +93 -0
- package/src/components/richtext-editor.tsx +624 -0
- package/src/components/richtext-mode.ts +39 -0
- package/src/components/richtext-render.tsx +51 -0
- package/src/components/richtext-sync.ts +57 -0
- package/src/components/scroll-area.tsx +41 -0
- package/src/components/select.tsx +200 -0
- package/src/components/separator.tsx +21 -0
- package/src/components/sheet.tsx +109 -0
- package/src/components/sidebar.tsx +660 -0
- package/src/components/skeleton.tsx +9 -0
- package/src/components/sonner.tsx +45 -0
- package/src/components/switch.tsx +24 -0
- package/src/components/table.tsx +93 -0
- package/src/components/tabs.tsx +57 -0
- package/src/components/textarea.tsx +18 -0
- package/src/components/tooltip.tsx +25 -0
- package/src/index.ts +342 -0
- package/src/lib/theme.ts +45 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles.css +242 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { Slot } from '@radix-ui/react-slot'
|
|
3
|
+
import { ChevronRight, MoreHorizontal } from 'lucide-react'
|
|
4
|
+
import { cn } from '../lib/utils.js'
|
|
5
|
+
|
|
6
|
+
export const Breadcrumb = React.forwardRef<
|
|
7
|
+
HTMLElement,
|
|
8
|
+
React.ComponentPropsWithoutRef<'nav'>
|
|
9
|
+
>((props, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />)
|
|
10
|
+
Breadcrumb.displayName = 'Breadcrumb'
|
|
11
|
+
|
|
12
|
+
export const BreadcrumbList = React.forwardRef<
|
|
13
|
+
HTMLOListElement,
|
|
14
|
+
React.ComponentPropsWithoutRef<'ol'>
|
|
15
|
+
>(({ className, ...props }, ref) => (
|
|
16
|
+
<ol
|
|
17
|
+
ref={ref}
|
|
18
|
+
className={cn(
|
|
19
|
+
'flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5',
|
|
20
|
+
className,
|
|
21
|
+
)}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
))
|
|
25
|
+
BreadcrumbList.displayName = 'BreadcrumbList'
|
|
26
|
+
|
|
27
|
+
export const BreadcrumbItem = React.forwardRef<
|
|
28
|
+
HTMLLIElement,
|
|
29
|
+
React.ComponentPropsWithoutRef<'li'>
|
|
30
|
+
>(({ className, ...props }, ref) => (
|
|
31
|
+
<li
|
|
32
|
+
ref={ref}
|
|
33
|
+
className={cn('inline-flex items-center gap-1.5', className)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
))
|
|
37
|
+
BreadcrumbItem.displayName = 'BreadcrumbItem'
|
|
38
|
+
|
|
39
|
+
export const BreadcrumbLink = React.forwardRef<
|
|
40
|
+
HTMLAnchorElement,
|
|
41
|
+
React.ComponentPropsWithoutRef<'a'> & { asChild?: boolean }
|
|
42
|
+
>(({ asChild, className, ...props }, ref) => {
|
|
43
|
+
const Comp = asChild ? Slot : 'a'
|
|
44
|
+
return (
|
|
45
|
+
<Comp
|
|
46
|
+
ref={ref}
|
|
47
|
+
className={cn('transition-colors hover:text-foreground', className)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
)
|
|
51
|
+
})
|
|
52
|
+
BreadcrumbLink.displayName = 'BreadcrumbLink'
|
|
53
|
+
|
|
54
|
+
export const BreadcrumbPage = React.forwardRef<
|
|
55
|
+
HTMLSpanElement,
|
|
56
|
+
React.ComponentPropsWithoutRef<'span'>
|
|
57
|
+
>(({ className, ...props }, ref) => (
|
|
58
|
+
<span
|
|
59
|
+
ref={ref}
|
|
60
|
+
role="link"
|
|
61
|
+
aria-disabled="true"
|
|
62
|
+
aria-current="page"
|
|
63
|
+
className={cn('font-normal text-foreground', className)}
|
|
64
|
+
{...props}
|
|
65
|
+
/>
|
|
66
|
+
))
|
|
67
|
+
BreadcrumbPage.displayName = 'BreadcrumbPage'
|
|
68
|
+
|
|
69
|
+
export const BreadcrumbSeparator = ({
|
|
70
|
+
children,
|
|
71
|
+
className,
|
|
72
|
+
...props
|
|
73
|
+
}: React.ComponentPropsWithoutRef<'li'>): React.ReactElement => (
|
|
74
|
+
<li
|
|
75
|
+
role="presentation"
|
|
76
|
+
aria-hidden="true"
|
|
77
|
+
className={cn('[&>svg]:size-3.5', className)}
|
|
78
|
+
{...props}
|
|
79
|
+
>
|
|
80
|
+
{children ?? <ChevronRight />}
|
|
81
|
+
</li>
|
|
82
|
+
)
|
|
83
|
+
BreadcrumbSeparator.displayName = 'BreadcrumbSeparator'
|
|
84
|
+
|
|
85
|
+
export const BreadcrumbEllipsis = ({
|
|
86
|
+
className,
|
|
87
|
+
...props
|
|
88
|
+
}: React.ComponentPropsWithoutRef<'span'>): React.ReactElement => (
|
|
89
|
+
<span
|
|
90
|
+
role="presentation"
|
|
91
|
+
aria-hidden="true"
|
|
92
|
+
className={cn('flex size-9 items-center justify-center', className)}
|
|
93
|
+
{...props}
|
|
94
|
+
>
|
|
95
|
+
<MoreHorizontal className="size-4" />
|
|
96
|
+
<span className="sr-only">More</span>
|
|
97
|
+
</span>
|
|
98
|
+
)
|
|
99
|
+
BreadcrumbEllipsis.displayName = 'BreadcrumbEllipsis'
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
3
|
+
import { Slot } from '@radix-ui/react-slot'
|
|
4
|
+
import { cn } from '../lib/utils.js'
|
|
5
|
+
|
|
6
|
+
const buttonVariants = cva(
|
|
7
|
+
'inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
12
|
+
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
13
|
+
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
|
14
|
+
// Colored outline variants — same chrome as `outline` (border +
|
|
15
|
+
// transparent background, height comes from the size token) but
|
|
16
|
+
// with a semantic tint applied to border, text and hover. Use
|
|
17
|
+
// when grouping a row of action buttons so they share a single
|
|
18
|
+
// visual silhouette while still encoding intent through color.
|
|
19
|
+
'outline-primary':
|
|
20
|
+
'border border-primary/40 bg-background text-primary hover:bg-primary/10 hover:border-primary/60',
|
|
21
|
+
'outline-destructive':
|
|
22
|
+
'border border-destructive/40 bg-background text-destructive hover:bg-destructive/10 hover:border-destructive/60',
|
|
23
|
+
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
24
|
+
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
25
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
26
|
+
},
|
|
27
|
+
size: {
|
|
28
|
+
default: 'h-9 px-4 py-2',
|
|
29
|
+
sm: 'h-8 rounded-md px-3 text-xs',
|
|
30
|
+
lg: 'h-10 rounded-md px-6',
|
|
31
|
+
icon: 'h-9 w-9',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
defaultVariants: { variant: 'default', size: 'default' },
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
export interface ButtonProps
|
|
39
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
40
|
+
VariantProps<typeof buttonVariants> {
|
|
41
|
+
asChild?: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
45
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
46
|
+
const Comp = asChild ? Slot : 'button'
|
|
47
|
+
return (
|
|
48
|
+
<Comp
|
|
49
|
+
ref={ref}
|
|
50
|
+
className={cn(buttonVariants({ variant, size }), className)}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
)
|
|
54
|
+
},
|
|
55
|
+
)
|
|
56
|
+
Button.displayName = 'Button'
|
|
57
|
+
|
|
58
|
+
export { buttonVariants }
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// shadcn-style calendar built on react-day-picker 9. Tokens come from the
|
|
2
|
+
// project's semantic palette so light/dark themes work without overrides.
|
|
3
|
+
//
|
|
4
|
+
// Use directly for inline calendars, or wrap with `<DatePicker>` for the
|
|
5
|
+
// popover-based input pattern.
|
|
6
|
+
|
|
7
|
+
import * as React from 'react'
|
|
8
|
+
import { ChevronLeft, ChevronRight } from 'lucide-react'
|
|
9
|
+
import {
|
|
10
|
+
DayPicker,
|
|
11
|
+
type DayPickerProps,
|
|
12
|
+
type DropdownOption,
|
|
13
|
+
} from 'react-day-picker'
|
|
14
|
+
import 'react-day-picker/style.css'
|
|
15
|
+
import { cn } from '../lib/utils.js'
|
|
16
|
+
import { buttonVariants } from './button.js'
|
|
17
|
+
import {
|
|
18
|
+
Select,
|
|
19
|
+
SelectContent,
|
|
20
|
+
SelectItem,
|
|
21
|
+
SelectTrigger,
|
|
22
|
+
SelectValue,
|
|
23
|
+
} from './select.js'
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Replacement for react-day-picker's native <select> dropdown. Uses the
|
|
27
|
+
* shadcn Select (Radix) so month/year pickers match the rest of the UI and
|
|
28
|
+
* pop above the popover instead of being clipped by it.
|
|
29
|
+
*/
|
|
30
|
+
function CalendarDropdown(
|
|
31
|
+
props: {
|
|
32
|
+
options?: DropdownOption[]
|
|
33
|
+
value?: string | number | readonly string[]
|
|
34
|
+
onChange?: React.ChangeEventHandler<HTMLSelectElement>
|
|
35
|
+
disabled?: boolean
|
|
36
|
+
'aria-label'?: string
|
|
37
|
+
className?: string
|
|
38
|
+
},
|
|
39
|
+
): React.ReactElement {
|
|
40
|
+
const { options = [], value, onChange, disabled, className } = props
|
|
41
|
+
const ariaLabel = props['aria-label']
|
|
42
|
+
const handleChange = (next: string): void => {
|
|
43
|
+
// react-day-picker's handlers read `e.target.value`, so synthesize the
|
|
44
|
+
// minimal shape they require.
|
|
45
|
+
onChange?.({
|
|
46
|
+
target: { value: next },
|
|
47
|
+
} as unknown as React.ChangeEvent<HTMLSelectElement>)
|
|
48
|
+
}
|
|
49
|
+
return (
|
|
50
|
+
<Select
|
|
51
|
+
value={value != null ? String(value) : undefined}
|
|
52
|
+
onValueChange={handleChange}
|
|
53
|
+
disabled={disabled}
|
|
54
|
+
>
|
|
55
|
+
<SelectTrigger
|
|
56
|
+
aria-label={ariaLabel}
|
|
57
|
+
className={cn(
|
|
58
|
+
// Borderless ghost-style trigger: compact pill that highlights on
|
|
59
|
+
// hover, with a subtle chevron — matches the "Month ⌄ Year ⌄"
|
|
60
|
+
// reference design.
|
|
61
|
+
'h-7 w-auto gap-1 rounded-md border-transparent bg-transparent px-2 text-sm font-medium shadow-none hover:bg-accent hover:text-accent-foreground focus:ring-1 [&>svg]:size-3.5 [&>svg]:opacity-60',
|
|
62
|
+
className,
|
|
63
|
+
)}
|
|
64
|
+
>
|
|
65
|
+
<SelectValue />
|
|
66
|
+
</SelectTrigger>
|
|
67
|
+
<SelectContent className="max-h-[var(--radix-select-content-available-height)]">
|
|
68
|
+
{options.map((opt) => (
|
|
69
|
+
<SelectItem
|
|
70
|
+
key={opt.value}
|
|
71
|
+
value={String(opt.value)}
|
|
72
|
+
disabled={opt.disabled}
|
|
73
|
+
>
|
|
74
|
+
{opt.label}
|
|
75
|
+
</SelectItem>
|
|
76
|
+
))}
|
|
77
|
+
</SelectContent>
|
|
78
|
+
</Select>
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type CalendarProps = DayPickerProps
|
|
83
|
+
|
|
84
|
+
const CURRENT_YEAR = new Date().getFullYear()
|
|
85
|
+
const DEFAULT_START_MONTH = new Date(CURRENT_YEAR - 100, 0, 1)
|
|
86
|
+
const DEFAULT_END_MONTH = new Date(CURRENT_YEAR + 10, 11, 31)
|
|
87
|
+
|
|
88
|
+
export function Calendar({
|
|
89
|
+
className,
|
|
90
|
+
classNames,
|
|
91
|
+
showOutsideDays = true,
|
|
92
|
+
captionLayout = 'dropdown',
|
|
93
|
+
startMonth = DEFAULT_START_MONTH,
|
|
94
|
+
endMonth = DEFAULT_END_MONTH,
|
|
95
|
+
...props
|
|
96
|
+
}: CalendarProps): React.ReactElement {
|
|
97
|
+
return (
|
|
98
|
+
<DayPicker
|
|
99
|
+
showOutsideDays={showOutsideDays}
|
|
100
|
+
// Lock the grid to 6 weeks so navigating months never shifts the popover
|
|
101
|
+
// height (and thus never visually "jumps").
|
|
102
|
+
fixedWeeks
|
|
103
|
+
captionLayout={captionLayout}
|
|
104
|
+
startMonth={startMonth}
|
|
105
|
+
endMonth={endMonth}
|
|
106
|
+
className={cn('p-3', className)}
|
|
107
|
+
classNames={{
|
|
108
|
+
months: 'flex flex-col gap-4 sm:flex-row sm:gap-6',
|
|
109
|
+
month: 'flex flex-col gap-3',
|
|
110
|
+
// Caption hosts the dropdowns centered; horizontal padding reserves
|
|
111
|
+
// room for the absolutely-positioned nav buttons so wide dropdowns
|
|
112
|
+
// never overlap (and steal clicks from) the next-month chevron.
|
|
113
|
+
month_caption: 'flex justify-center pt-1 relative items-center h-9 px-9',
|
|
114
|
+
caption_label:
|
|
115
|
+
'inline-flex items-center gap-1 text-sm font-medium [&>svg]:size-3.5 [&>svg]:opacity-60',
|
|
116
|
+
// Custom CalendarDropdown component renders shadcn Selects directly,
|
|
117
|
+
// so the rdp wrapper just needs a flex layout for the two pickers.
|
|
118
|
+
dropdowns: 'flex items-center gap-1',
|
|
119
|
+
dropdown_root: 'relative inline-flex',
|
|
120
|
+
dropdown: '',
|
|
121
|
+
months_dropdown: '',
|
|
122
|
+
years_dropdown: '',
|
|
123
|
+
// Nav row floats over the caption. The wrapper itself ignores
|
|
124
|
+
// pointer events so the gap between the buttons doesn't block clicks
|
|
125
|
+
// on the dropdowns underneath; only the buttons themselves are
|
|
126
|
+
// interactive (`pointer-events-auto`).
|
|
127
|
+
nav: 'absolute inset-x-1 top-1 flex items-center justify-between pointer-events-none z-10',
|
|
128
|
+
button_previous: cn(
|
|
129
|
+
buttonVariants({ variant: 'ghost', size: 'icon' }),
|
|
130
|
+
'pointer-events-auto size-7 p-0 opacity-70 hover:opacity-100',
|
|
131
|
+
),
|
|
132
|
+
button_next: cn(
|
|
133
|
+
buttonVariants({ variant: 'ghost', size: 'icon' }),
|
|
134
|
+
'pointer-events-auto size-7 p-0 opacity-70 hover:opacity-100',
|
|
135
|
+
),
|
|
136
|
+
month_grid: 'w-full border-collapse space-y-1',
|
|
137
|
+
weekdays: 'flex',
|
|
138
|
+
weekday:
|
|
139
|
+
'text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]',
|
|
140
|
+
week: 'flex w-full mt-2',
|
|
141
|
+
// Single rounded button per cell — no cell-level background hacks so
|
|
142
|
+
// hover/focus stays inside the rounded shape (no black corners).
|
|
143
|
+
day: 'relative p-0 text-center text-sm focus-within:relative focus-within:z-20',
|
|
144
|
+
day_button: cn(
|
|
145
|
+
buttonVariants({ variant: 'ghost' }),
|
|
146
|
+
'size-9 rounded-md p-0 font-normal aria-selected:opacity-100',
|
|
147
|
+
),
|
|
148
|
+
range_start: 'day-range-start',
|
|
149
|
+
range_end: 'day-range-end',
|
|
150
|
+
// Apply selected state to the inner button so its rounded shape wins
|
|
151
|
+
// over hover/focus rectangles.
|
|
152
|
+
selected:
|
|
153
|
+
'[&>button]:bg-primary [&>button]:text-primary-foreground [&>button]:hover:bg-primary [&>button]:hover:text-primary-foreground [&>button]:focus:bg-primary [&>button]:focus:text-primary-foreground',
|
|
154
|
+
today: '[&>button]:bg-accent [&>button]:text-accent-foreground',
|
|
155
|
+
outside:
|
|
156
|
+
'day-outside text-muted-foreground aria-selected:text-muted-foreground',
|
|
157
|
+
disabled: 'text-muted-foreground opacity-50',
|
|
158
|
+
range_middle:
|
|
159
|
+
'[&>button]:bg-accent [&>button]:text-accent-foreground',
|
|
160
|
+
hidden: 'invisible',
|
|
161
|
+
...classNames,
|
|
162
|
+
}}
|
|
163
|
+
components={{
|
|
164
|
+
Chevron: ({ orientation, className: cls }) => {
|
|
165
|
+
const Icon = orientation === 'left' ? ChevronLeft : ChevronRight
|
|
166
|
+
// pointer-events-none so clicks on the SVG bubble to the parent
|
|
167
|
+
// button (otherwise they're swallowed and nav doesn't fire).
|
|
168
|
+
return <Icon className={cn('size-4 pointer-events-none', cls)} />
|
|
169
|
+
},
|
|
170
|
+
Dropdown: CalendarDropdown,
|
|
171
|
+
}}
|
|
172
|
+
{...props}
|
|
173
|
+
/>
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
Calendar.displayName = 'Calendar'
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { cn } from '../lib/utils.js'
|
|
3
|
+
|
|
4
|
+
export const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
5
|
+
({ className, ...props }, ref) => (
|
|
6
|
+
<div
|
|
7
|
+
ref={ref}
|
|
8
|
+
className={cn('rounded-lg border border-border bg-card text-card-foreground shadow-sm', className)}
|
|
9
|
+
{...props}
|
|
10
|
+
/>
|
|
11
|
+
),
|
|
12
|
+
)
|
|
13
|
+
Card.displayName = 'Card'
|
|
14
|
+
|
|
15
|
+
export const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
16
|
+
({ className, ...props }, ref) => (
|
|
17
|
+
<div ref={ref} className={cn('flex flex-col gap-1.5 p-3 sm:p-6', className)} {...props} />
|
|
18
|
+
),
|
|
19
|
+
)
|
|
20
|
+
CardHeader.displayName = 'CardHeader'
|
|
21
|
+
|
|
22
|
+
export const CardTitle = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
|
23
|
+
({ className, ...props }, ref) => (
|
|
24
|
+
<h3
|
|
25
|
+
ref={ref as unknown as React.Ref<HTMLHeadingElement>}
|
|
26
|
+
className={cn('text-lg font-semibold leading-none tracking-tight', className)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
),
|
|
30
|
+
)
|
|
31
|
+
CardTitle.displayName = 'CardTitle'
|
|
32
|
+
|
|
33
|
+
export const CardDescription = React.forwardRef<
|
|
34
|
+
HTMLDivElement,
|
|
35
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
36
|
+
>(({ className, ...props }, ref) => (
|
|
37
|
+
<p
|
|
38
|
+
ref={ref as unknown as React.Ref<HTMLParagraphElement>}
|
|
39
|
+
className={cn('text-sm text-muted-foreground', className)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
))
|
|
43
|
+
CardDescription.displayName = 'CardDescription'
|
|
44
|
+
|
|
45
|
+
// `[&:not(:first-child)]:pt-0` collapses the top padding only when this content
|
|
46
|
+
// follows a sibling (typically a `CardHeader`), so a standalone `<Card><CardContent/>`
|
|
47
|
+
// keeps full top padding instead of inheriting the header-aware `pt-0`.
|
|
48
|
+
export const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
49
|
+
({ className, ...props }, ref) => (
|
|
50
|
+
<div ref={ref} className={cn('p-3 sm:p-6 [&:not(:first-child)]:pt-0', className)} {...props} />
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
CardContent.displayName = 'CardContent'
|
|
54
|
+
|
|
55
|
+
export const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
56
|
+
({ className, ...props }, ref) => (
|
|
57
|
+
<div ref={ref} className={cn('flex items-center gap-2 p-3 pt-0 sm:p-6 sm:pt-0', className)} {...props} />
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
CardFooter.displayName = 'CardFooter'
|