@hobenakicoffee/libraries 1.11.0 → 1.12.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/README.md +25 -4
- package/package.json +84 -9
- package/src/App.tsx +28 -0
- package/src/components/turnstile-captcha.tsx +47 -0
- package/src/components/ui/alert-dialog.tsx +196 -0
- package/src/components/ui/alert.tsx +76 -0
- package/src/components/ui/avatar.tsx +110 -0
- package/src/components/ui/badge.tsx +49 -0
- package/src/components/ui/breadcrumb.tsx +122 -0
- package/src/components/ui/button-group.tsx +82 -0
- package/src/components/ui/button.tsx +77 -0
- package/src/components/ui/calendar.tsx +235 -0
- package/src/components/ui/card.tsx +100 -0
- package/src/components/ui/chart.tsx +364 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/dialog.tsx +162 -0
- package/src/components/ui/drawer.tsx +126 -0
- package/src/components/ui/dropdown-menu.tsx +267 -0
- package/src/components/ui/empty-minimal.tsx +20 -0
- package/src/components/ui/empty.tsx +101 -0
- package/src/components/ui/field.tsx +235 -0
- package/src/components/ui/input-group.tsx +170 -0
- package/src/components/ui/input-otp.tsx +84 -0
- package/src/components/ui/input.tsx +37 -0
- package/src/components/ui/item.tsx +196 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/popover.tsx +87 -0
- package/src/components/ui/radio-group.tsx +47 -0
- package/src/components/ui/select.tsx +205 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/sheet.tsx +141 -0
- package/src/components/ui/sidebar.tsx +699 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/sonner.tsx +74 -0
- package/src/components/ui/spinner.tsx +18 -0
- package/src/components/ui/table.tsx +114 -0
- package/src/components/ui/tabs.tsx +88 -0
- package/src/components/ui/textarea.tsx +35 -0
- package/src/components/ui/toggle-group.tsx +91 -0
- package/src/components/ui/toggle.tsx +44 -0
- package/src/components/ui/tooltip.tsx +59 -0
- package/src/constants/common.test.ts +1 -1
- package/src/constants/legal.test.ts +1 -1
- package/src/constants/payment.test.ts +9 -9
- package/src/constants/platforms.test.ts +1 -1
- package/src/constants/services.test.ts +1 -1
- package/src/hooks/use-mobile.ts +19 -0
- package/src/index.css +135 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +16 -0
- package/src/moderation/datasets/bn.ts +708 -708
- package/src/moderation/normalizer.test.ts +1 -1
- package/src/moderation/normalizer.ts +16 -16
- package/src/moderation/profanity-service.test.ts +3 -3
- package/src/providers/theme-provider.tsx +73 -0
- package/src/types/supabase.ts +649 -647
- package/src/utils/check-moderation.test.ts +12 -12
- package/src/utils/format-number.test.ts +1 -1
- package/src/utils/format-plain-text.test.ts +1 -1
- package/src/utils/get-social-handle.test.ts +3 -3
- package/src/utils/get-social-link.test.ts +9 -9
- package/src/utils/get-social-link.ts +5 -3
- package/src/utils/get-user-name-initials.test.ts +1 -1
- package/src/utils/get-user-name-initials.ts +4 -4
- package/src/utils/get-user-page-link.ts +1 -1
- package/src/utils/index.ts +5 -5
- package/src/utils/open-to-new-window.ts +3 -1
- package/src/utils/post-to-facebook.test.ts +1 -1
- package/src/utils/post-to-facebook.ts +9 -3
- package/src/utils/post-to-instagram.test.ts +1 -1
- package/src/utils/post-to-linkedin.test.ts +1 -1
- package/src/utils/post-to-linkedin.ts +9 -3
- package/src/utils/post-to-x.test.ts +1 -1
- package/src/utils/post-to-x.ts +12 -4
- package/src/utils/to-human-readable.ts +6 -2
- package/src/utils/validate-phone-number.test.ts +1 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
import { Slot } from "radix-ui";
|
|
3
|
+
import type * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const badgeVariants = cva(
|
|
8
|
+
"group/badge inline-flex h-6 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden whitespace-nowrap rounded-4xl px-2 py-1 font-normal text-xs uppercase ring-1 transition-all transition-colors focus-visible:border-ring focus-visible:ring-ring focus-visible:ring-ring has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
13
|
+
secondary:
|
|
14
|
+
"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
15
|
+
destructive:
|
|
16
|
+
"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20",
|
|
17
|
+
outline:
|
|
18
|
+
"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
19
|
+
ghost:
|
|
20
|
+
"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
21
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
defaultVariants: {
|
|
25
|
+
variant: "default",
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
function Badge({
|
|
31
|
+
className,
|
|
32
|
+
variant = "default",
|
|
33
|
+
asChild = false,
|
|
34
|
+
...props
|
|
35
|
+
}: React.ComponentProps<"span"> &
|
|
36
|
+
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
|
37
|
+
const Comp = asChild ? Slot.Root : "span";
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<Comp
|
|
41
|
+
className={cn(badgeVariants({ variant }), className)}
|
|
42
|
+
data-slot="badge"
|
|
43
|
+
data-variant={variant}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { Badge, badgeVariants };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArrowRight01Icon,
|
|
3
|
+
MoreHorizontalCircle01Icon,
|
|
4
|
+
} from "@hugeicons/core-free-icons";
|
|
5
|
+
import { HugeiconsIcon } from "@hugeicons/react";
|
|
6
|
+
import { Slot } from "radix-ui";
|
|
7
|
+
import type * as React from "react";
|
|
8
|
+
import { cn } from "@/lib/utils";
|
|
9
|
+
|
|
10
|
+
function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
|
|
11
|
+
return (
|
|
12
|
+
<nav
|
|
13
|
+
aria-label="breadcrumb"
|
|
14
|
+
className={cn(className)}
|
|
15
|
+
data-slot="breadcrumb"
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
|
22
|
+
return (
|
|
23
|
+
<ol
|
|
24
|
+
className={cn(
|
|
25
|
+
"wrap-break-word flex flex-wrap items-center gap-1.5 text-muted-foreground text-sm sm:gap-2.5",
|
|
26
|
+
className
|
|
27
|
+
)}
|
|
28
|
+
data-slot="breadcrumb-list"
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
35
|
+
return (
|
|
36
|
+
<li
|
|
37
|
+
className={cn("inline-flex items-center gap-1.5", className)}
|
|
38
|
+
data-slot="breadcrumb-item"
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function BreadcrumbLink({
|
|
45
|
+
asChild,
|
|
46
|
+
className,
|
|
47
|
+
...props
|
|
48
|
+
}: React.ComponentProps<"a"> & {
|
|
49
|
+
asChild?: boolean;
|
|
50
|
+
}) {
|
|
51
|
+
const Comp = asChild ? Slot.Root : "a";
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Comp
|
|
55
|
+
className={cn("transition-colors hover:text-foreground", className)}
|
|
56
|
+
data-slot="breadcrumb-link"
|
|
57
|
+
{...props}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
|
63
|
+
return (
|
|
64
|
+
<span
|
|
65
|
+
aria-current="page"
|
|
66
|
+
aria-disabled="true"
|
|
67
|
+
className={cn("font-normal text-foreground", className)}
|
|
68
|
+
data-slot="breadcrumb-page"
|
|
69
|
+
role="link"
|
|
70
|
+
{...props}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function BreadcrumbSeparator({
|
|
76
|
+
children,
|
|
77
|
+
className,
|
|
78
|
+
...props
|
|
79
|
+
}: React.ComponentProps<"li">) {
|
|
80
|
+
return (
|
|
81
|
+
<li
|
|
82
|
+
aria-hidden="true"
|
|
83
|
+
className={cn("[&>svg]:size-3.5", className)}
|
|
84
|
+
data-slot="breadcrumb-separator"
|
|
85
|
+
role="presentation"
|
|
86
|
+
{...props}
|
|
87
|
+
>
|
|
88
|
+
{children ?? <HugeiconsIcon icon={ArrowRight01Icon} strokeWidth={2} />}
|
|
89
|
+
</li>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function BreadcrumbEllipsis({
|
|
94
|
+
className,
|
|
95
|
+
...props
|
|
96
|
+
}: React.ComponentProps<"span">) {
|
|
97
|
+
return (
|
|
98
|
+
<span
|
|
99
|
+
aria-hidden="true"
|
|
100
|
+
className={cn(
|
|
101
|
+
"flex size-5 items-center justify-center [&>svg]:size-4",
|
|
102
|
+
className
|
|
103
|
+
)}
|
|
104
|
+
data-slot="breadcrumb-ellipsis"
|
|
105
|
+
role="presentation"
|
|
106
|
+
{...props}
|
|
107
|
+
>
|
|
108
|
+
<HugeiconsIcon icon={MoreHorizontalCircle01Icon} strokeWidth={2} />
|
|
109
|
+
<span className="sr-only">More</span>
|
|
110
|
+
</span>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export {
|
|
115
|
+
Breadcrumb,
|
|
116
|
+
BreadcrumbList,
|
|
117
|
+
BreadcrumbItem,
|
|
118
|
+
BreadcrumbLink,
|
|
119
|
+
BreadcrumbPage,
|
|
120
|
+
BreadcrumbSeparator,
|
|
121
|
+
BreadcrumbEllipsis,
|
|
122
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
import { Slot } from "radix-ui";
|
|
3
|
+
import { Separator } from "@/components/ui/separator";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
const buttonGroupVariants = cva(
|
|
7
|
+
"flex w-fit items-stretch has-[>[data-slot=button-group]]:gap-2 [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-xl [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
orientation: {
|
|
11
|
+
horizontal:
|
|
12
|
+
"[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-xl!",
|
|
13
|
+
vertical:
|
|
14
|
+
"flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-xl!",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: {
|
|
18
|
+
orientation: "horizontal",
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
function ButtonGroup({
|
|
24
|
+
className,
|
|
25
|
+
orientation,
|
|
26
|
+
...props
|
|
27
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {
|
|
28
|
+
return (
|
|
29
|
+
<div
|
|
30
|
+
className={cn(buttonGroupVariants({ orientation }), className)}
|
|
31
|
+
data-orientation={orientation}
|
|
32
|
+
data-slot="button-group"
|
|
33
|
+
role="group"
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function ButtonGroupText({
|
|
40
|
+
className,
|
|
41
|
+
asChild = false,
|
|
42
|
+
...props
|
|
43
|
+
}: React.ComponentProps<"div"> & {
|
|
44
|
+
asChild?: boolean;
|
|
45
|
+
}) {
|
|
46
|
+
const Comp = asChild ? Slot.Root : "div";
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Comp
|
|
50
|
+
className={cn(
|
|
51
|
+
"flex items-center gap-2 rounded-xl border bg-muted px-2.5 font-medium text-sm shadow-xs [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
|
|
52
|
+
className
|
|
53
|
+
)}
|
|
54
|
+
{...props}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function ButtonGroupSeparator({
|
|
60
|
+
className,
|
|
61
|
+
orientation = "vertical",
|
|
62
|
+
...props
|
|
63
|
+
}: React.ComponentProps<typeof Separator>) {
|
|
64
|
+
return (
|
|
65
|
+
<Separator
|
|
66
|
+
className={cn(
|
|
67
|
+
"relative self-stretch bg-input data-[orientation=horizontal]:mx-px data-[orientation=vertical]:my-px data-[orientation=vertical]:h-auto data-[orientation=horizontal]:w-auto",
|
|
68
|
+
className
|
|
69
|
+
)}
|
|
70
|
+
data-slot="button-group-separator"
|
|
71
|
+
orientation={orientation}
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
ButtonGroup,
|
|
79
|
+
ButtonGroupSeparator,
|
|
80
|
+
ButtonGroupText,
|
|
81
|
+
buttonGroupVariants,
|
|
82
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2
|
+
import { Slot } from "radix-ui";
|
|
3
|
+
import type * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
import { Spinner } from "./spinner";
|
|
7
|
+
|
|
8
|
+
const buttonVariants = cva(
|
|
9
|
+
"group/button inline-flex shrink-0 select-none items-center justify-center whitespace-nowrap rounded-xl border border-transparent bg-clip-padding font-medium text-sm outline-none transition-all focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background active:scale-[0.98] disabled:pointer-events-none disabled:opacity-70 aria-invalid:border-destructive aria-invalid:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/80",
|
|
14
|
+
outline:
|
|
15
|
+
"border-border bg-input/30 hover:border-primary/50 hover:bg-input/20 hover:bg-primary/10 hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground",
|
|
16
|
+
secondary:
|
|
17
|
+
"bg-secondary text-secondary-foreground hover:bg-primary/5 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
18
|
+
ghost:
|
|
19
|
+
"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
|
20
|
+
destructive:
|
|
21
|
+
"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 dark:hover:bg-destructive/30",
|
|
22
|
+
link: "underline-offset-4 hover:underline",
|
|
23
|
+
inverted:
|
|
24
|
+
"border-border bg-background text-foreground hover:border-primary/50 hover:bg-primary/5",
|
|
25
|
+
dottedUnderline:
|
|
26
|
+
"rounded-none border-0 border-muted-foreground/50 border-b border-dotted bg-transparent text-muted-foreground hover:bg-transparent hover:text-foreground",
|
|
27
|
+
},
|
|
28
|
+
size: {
|
|
29
|
+
default:
|
|
30
|
+
"h-12 gap-1.5 px-4 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
|
|
31
|
+
xs: "h-6 gap-1 px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
|
|
32
|
+
sm: "h-8 gap-1 px-3 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
33
|
+
lg: "h-16 gap-1.5 px-5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3",
|
|
34
|
+
icon: "size-9",
|
|
35
|
+
"icon-xs": "size-6 [&_svg:not([class*='size-'])]:size-3",
|
|
36
|
+
"icon-sm": "size-8",
|
|
37
|
+
"icon-lg": "size-12",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
defaultVariants: {
|
|
41
|
+
variant: "default",
|
|
42
|
+
size: "default",
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
function Button({
|
|
48
|
+
className,
|
|
49
|
+
variant = "default",
|
|
50
|
+
size = "default",
|
|
51
|
+
asChild = false,
|
|
52
|
+
loading,
|
|
53
|
+
disabled,
|
|
54
|
+
children,
|
|
55
|
+
...props
|
|
56
|
+
}: React.ComponentProps<"button"> &
|
|
57
|
+
VariantProps<typeof buttonVariants> & {
|
|
58
|
+
asChild?: boolean;
|
|
59
|
+
loading?: boolean;
|
|
60
|
+
}) {
|
|
61
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<Comp
|
|
65
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
66
|
+
data-size={size}
|
|
67
|
+
data-slot="button"
|
|
68
|
+
data-variant={variant}
|
|
69
|
+
disabled={loading || disabled}
|
|
70
|
+
{...props}
|
|
71
|
+
>
|
|
72
|
+
{loading ? <Spinner /> : children}
|
|
73
|
+
</Comp>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArrowDownIcon,
|
|
3
|
+
ArrowLeftIcon,
|
|
4
|
+
ArrowRightIcon,
|
|
5
|
+
} from "@hugeicons/core-free-icons";
|
|
6
|
+
import { HugeiconsIcon } from "@hugeicons/react";
|
|
7
|
+
import { type ComponentProps, useEffect, useRef } from "react";
|
|
8
|
+
import {
|
|
9
|
+
type DayButton,
|
|
10
|
+
DayPicker,
|
|
11
|
+
getDefaultClassNames,
|
|
12
|
+
} from "react-day-picker";
|
|
13
|
+
import { Button, buttonVariants } from "@/components/ui/button";
|
|
14
|
+
import { cn } from "@/lib/utils";
|
|
15
|
+
|
|
16
|
+
function Calendar({
|
|
17
|
+
className,
|
|
18
|
+
classNames,
|
|
19
|
+
showOutsideDays = true,
|
|
20
|
+
captionLayout = "label",
|
|
21
|
+
buttonVariant = "ghost",
|
|
22
|
+
formatters,
|
|
23
|
+
components,
|
|
24
|
+
...props
|
|
25
|
+
}: ComponentProps<typeof DayPicker> & {
|
|
26
|
+
buttonVariant?: ComponentProps<typeof Button>["variant"];
|
|
27
|
+
}) {
|
|
28
|
+
const defaultClassNames = getDefaultClassNames();
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<DayPicker
|
|
32
|
+
captionLayout={captionLayout}
|
|
33
|
+
className={cn(
|
|
34
|
+
"group/calendar bg-background in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent p-3 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(8)]",
|
|
35
|
+
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
|
36
|
+
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
|
37
|
+
className
|
|
38
|
+
)}
|
|
39
|
+
classNames={{
|
|
40
|
+
root: cn("w-fit", defaultClassNames.root),
|
|
41
|
+
months: cn(
|
|
42
|
+
"relative flex flex-col gap-4 md:flex-row",
|
|
43
|
+
defaultClassNames.months
|
|
44
|
+
),
|
|
45
|
+
month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
|
|
46
|
+
nav: cn(
|
|
47
|
+
"absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
|
|
48
|
+
defaultClassNames.nav
|
|
49
|
+
),
|
|
50
|
+
button_previous: cn(
|
|
51
|
+
buttonVariants({ variant: buttonVariant }),
|
|
52
|
+
"size-(--cell-size) select-none p-0 aria-disabled:opacity-50",
|
|
53
|
+
defaultClassNames.button_previous
|
|
54
|
+
),
|
|
55
|
+
button_next: cn(
|
|
56
|
+
buttonVariants({ variant: buttonVariant }),
|
|
57
|
+
"size-(--cell-size) select-none p-0 aria-disabled:opacity-50",
|
|
58
|
+
defaultClassNames.button_next
|
|
59
|
+
),
|
|
60
|
+
month_caption: cn(
|
|
61
|
+
"flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
|
|
62
|
+
defaultClassNames.month_caption
|
|
63
|
+
),
|
|
64
|
+
dropdowns: cn(
|
|
65
|
+
"flex h-(--cell-size) w-full items-center justify-center gap-1.5 font-medium text-sm",
|
|
66
|
+
defaultClassNames.dropdowns
|
|
67
|
+
),
|
|
68
|
+
dropdown_root: cn(
|
|
69
|
+
"cn-calendar-dropdown-root relative rounded-(--cell-radius)",
|
|
70
|
+
defaultClassNames.dropdown_root
|
|
71
|
+
),
|
|
72
|
+
dropdown: cn(
|
|
73
|
+
"absolute inset-0 bg-popover opacity-0",
|
|
74
|
+
defaultClassNames.dropdown
|
|
75
|
+
),
|
|
76
|
+
caption_label: cn(
|
|
77
|
+
"select-none font-medium",
|
|
78
|
+
captionLayout === "label"
|
|
79
|
+
? "text-sm"
|
|
80
|
+
: "cn-calendar-caption-label flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
|
|
81
|
+
defaultClassNames.caption_label
|
|
82
|
+
),
|
|
83
|
+
table: "w-full border-collapse",
|
|
84
|
+
weekdays: cn("flex", defaultClassNames.weekdays),
|
|
85
|
+
weekday: cn(
|
|
86
|
+
"flex-1 select-none rounded-(--cell-radius) font-normal text-[0.8rem] text-muted-foreground",
|
|
87
|
+
defaultClassNames.weekday
|
|
88
|
+
),
|
|
89
|
+
week: cn("mt-2 flex w-full", defaultClassNames.week),
|
|
90
|
+
week_number_header: cn(
|
|
91
|
+
"w-(--cell-size) select-none",
|
|
92
|
+
defaultClassNames.week_number_header
|
|
93
|
+
),
|
|
94
|
+
week_number: cn(
|
|
95
|
+
"select-none text-[0.8rem] text-muted-foreground",
|
|
96
|
+
defaultClassNames.week_number
|
|
97
|
+
),
|
|
98
|
+
day: cn(
|
|
99
|
+
"group/day relative aspect-square h-full w-full select-none rounded-(--cell-radius) p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)",
|
|
100
|
+
props.showWeekNumber
|
|
101
|
+
? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)"
|
|
102
|
+
: "[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)",
|
|
103
|
+
defaultClassNames.day
|
|
104
|
+
),
|
|
105
|
+
range_start: cn(
|
|
106
|
+
"relative isolate -z-0 rounded-l-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted",
|
|
107
|
+
defaultClassNames.range_start
|
|
108
|
+
),
|
|
109
|
+
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
|
110
|
+
range_end: cn(
|
|
111
|
+
"relative isolate -z-0 rounded-r-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted-200",
|
|
112
|
+
defaultClassNames.range_end
|
|
113
|
+
),
|
|
114
|
+
today: cn(
|
|
115
|
+
"rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none",
|
|
116
|
+
defaultClassNames.today
|
|
117
|
+
),
|
|
118
|
+
outside: cn(
|
|
119
|
+
"text-muted-foreground aria-selected:text-muted-foreground",
|
|
120
|
+
defaultClassNames.outside
|
|
121
|
+
),
|
|
122
|
+
disabled: cn(
|
|
123
|
+
"text-muted-foreground opacity-50",
|
|
124
|
+
defaultClassNames.disabled
|
|
125
|
+
),
|
|
126
|
+
hidden: cn("invisible", defaultClassNames.hidden),
|
|
127
|
+
...classNames,
|
|
128
|
+
}}
|
|
129
|
+
components={{
|
|
130
|
+
Root: ({ className, rootRef, ...props }) => {
|
|
131
|
+
return (
|
|
132
|
+
<div
|
|
133
|
+
className={cn(className)}
|
|
134
|
+
data-slot="calendar"
|
|
135
|
+
ref={rootRef}
|
|
136
|
+
{...props}
|
|
137
|
+
/>
|
|
138
|
+
);
|
|
139
|
+
},
|
|
140
|
+
Chevron: ({ className, orientation, ...props }) => {
|
|
141
|
+
if (orientation === "left") {
|
|
142
|
+
return (
|
|
143
|
+
<HugeiconsIcon
|
|
144
|
+
className={cn("size-4", className)}
|
|
145
|
+
icon={ArrowLeftIcon}
|
|
146
|
+
strokeWidth={2}
|
|
147
|
+
{...props}
|
|
148
|
+
/>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (orientation === "right") {
|
|
153
|
+
return (
|
|
154
|
+
<HugeiconsIcon
|
|
155
|
+
className={cn("size-4", className)}
|
|
156
|
+
icon={ArrowRightIcon}
|
|
157
|
+
strokeWidth={2}
|
|
158
|
+
{...props}
|
|
159
|
+
/>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<HugeiconsIcon
|
|
165
|
+
className={cn("size-4", className)}
|
|
166
|
+
icon={ArrowDownIcon}
|
|
167
|
+
strokeWidth={2}
|
|
168
|
+
{...props}
|
|
169
|
+
/>
|
|
170
|
+
);
|
|
171
|
+
},
|
|
172
|
+
DayButton: CalendarDayButton,
|
|
173
|
+
WeekNumber: ({ children, ...props }) => {
|
|
174
|
+
return (
|
|
175
|
+
<td {...props}>
|
|
176
|
+
<div className="flex size-(--cell-size) items-center justify-center text-center">
|
|
177
|
+
{children}
|
|
178
|
+
</div>
|
|
179
|
+
</td>
|
|
180
|
+
);
|
|
181
|
+
},
|
|
182
|
+
...components,
|
|
183
|
+
}}
|
|
184
|
+
formatters={{
|
|
185
|
+
formatMonthDropdown: (date) =>
|
|
186
|
+
date.toLocaleString("default", { month: "short" }),
|
|
187
|
+
...formatters,
|
|
188
|
+
}}
|
|
189
|
+
showOutsideDays={showOutsideDays}
|
|
190
|
+
{...props}
|
|
191
|
+
/>
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function CalendarDayButton({
|
|
196
|
+
className,
|
|
197
|
+
day,
|
|
198
|
+
modifiers,
|
|
199
|
+
...props
|
|
200
|
+
}: ComponentProps<typeof DayButton>) {
|
|
201
|
+
const defaultClassNames = getDefaultClassNames();
|
|
202
|
+
|
|
203
|
+
const ref = useRef<HTMLButtonElement>(null);
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (modifiers.focused) {
|
|
206
|
+
ref.current?.focus();
|
|
207
|
+
}
|
|
208
|
+
}, [modifiers.focused]);
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<Button
|
|
212
|
+
className={cn(
|
|
213
|
+
"relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 font-normal leading-none data-[range-end=true]:rounded-(--cell-radius) data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-r-(--cell-radius) data-[range-start=true]:rounded-l-(--cell-radius) data-[range-end=true]:bg-primary data-[range-middle=true]:bg-muted data-[range-start=true]:bg-primary data-[selected-single=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:text-foreground data-[range-start=true]:text-primary-foreground data-[selected-single=true]:text-primary-foreground group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 dark:hover:text-foreground [&>span]:text-xs [&>span]:opacity-70",
|
|
214
|
+
defaultClassNames.day,
|
|
215
|
+
className
|
|
216
|
+
)}
|
|
217
|
+
data-day={day.date.toLocaleDateString()}
|
|
218
|
+
data-range-end={modifiers.range_end}
|
|
219
|
+
data-range-middle={modifiers.range_middle}
|
|
220
|
+
data-range-start={modifiers.range_start}
|
|
221
|
+
data-selected-single={
|
|
222
|
+
modifiers.selected &&
|
|
223
|
+
!modifiers.range_start &&
|
|
224
|
+
!modifiers.range_end &&
|
|
225
|
+
!modifiers.range_middle
|
|
226
|
+
}
|
|
227
|
+
ref={ref}
|
|
228
|
+
size="icon"
|
|
229
|
+
variant="ghost"
|
|
230
|
+
{...props}
|
|
231
|
+
/>
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export { Calendar, CalendarDayButton };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
function Card({
|
|
6
|
+
className,
|
|
7
|
+
size = "default",
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
|
|
10
|
+
return (
|
|
11
|
+
<div
|
|
12
|
+
className={cn(
|
|
13
|
+
"group/card flex flex-col gap-6 overflow-hidden rounded-2xl bg-card py-6 text-card-foreground text-sm ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
|
|
14
|
+
className
|
|
15
|
+
)}
|
|
16
|
+
data-size={size}
|
|
17
|
+
data-slot="card"
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
24
|
+
return (
|
|
25
|
+
<div
|
|
26
|
+
className={cn(
|
|
27
|
+
"group/card-header @container/card-header grid auto-rows-min items-start gap-2 rounded-t-xl px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] group-data-[size=sm]/card:px-4 sm:px-6 [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4",
|
|
28
|
+
className
|
|
29
|
+
)}
|
|
30
|
+
data-slot="card-header"
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
className={cn("font-medium text-base", className)}
|
|
40
|
+
data-slot="card-title"
|
|
41
|
+
{...props}
|
|
42
|
+
/>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
47
|
+
return (
|
|
48
|
+
<div
|
|
49
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
50
|
+
data-slot="card-description"
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
57
|
+
return (
|
|
58
|
+
<div
|
|
59
|
+
className={cn(
|
|
60
|
+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
61
|
+
className
|
|
62
|
+
)}
|
|
63
|
+
data-slot="card-action"
|
|
64
|
+
{...props}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
70
|
+
return (
|
|
71
|
+
<div
|
|
72
|
+
className={cn("px-4 group-data-[size=sm]/card:px-4 sm:px-6", className)}
|
|
73
|
+
data-slot="card-content"
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
80
|
+
return (
|
|
81
|
+
<div
|
|
82
|
+
className={cn(
|
|
83
|
+
"flex items-center rounded-b-xl px-6 group-data-[size=sm]/card:px-4 [.border-t]:pt-6 group-data-[size=sm]/card:[.border-t]:pt-4",
|
|
84
|
+
className
|
|
85
|
+
)}
|
|
86
|
+
data-slot="card-footer"
|
|
87
|
+
{...props}
|
|
88
|
+
/>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export {
|
|
93
|
+
Card,
|
|
94
|
+
CardHeader,
|
|
95
|
+
CardFooter,
|
|
96
|
+
CardTitle,
|
|
97
|
+
CardAction,
|
|
98
|
+
CardDescription,
|
|
99
|
+
CardContent,
|
|
100
|
+
};
|