@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.
Files changed (76) hide show
  1. package/README.md +25 -4
  2. package/package.json +84 -9
  3. package/src/App.tsx +28 -0
  4. package/src/components/turnstile-captcha.tsx +47 -0
  5. package/src/components/ui/alert-dialog.tsx +196 -0
  6. package/src/components/ui/alert.tsx +76 -0
  7. package/src/components/ui/avatar.tsx +110 -0
  8. package/src/components/ui/badge.tsx +49 -0
  9. package/src/components/ui/breadcrumb.tsx +122 -0
  10. package/src/components/ui/button-group.tsx +82 -0
  11. package/src/components/ui/button.tsx +77 -0
  12. package/src/components/ui/calendar.tsx +235 -0
  13. package/src/components/ui/card.tsx +100 -0
  14. package/src/components/ui/chart.tsx +364 -0
  15. package/src/components/ui/checkbox.tsx +30 -0
  16. package/src/components/ui/dialog.tsx +162 -0
  17. package/src/components/ui/drawer.tsx +126 -0
  18. package/src/components/ui/dropdown-menu.tsx +267 -0
  19. package/src/components/ui/empty-minimal.tsx +20 -0
  20. package/src/components/ui/empty.tsx +101 -0
  21. package/src/components/ui/field.tsx +235 -0
  22. package/src/components/ui/input-group.tsx +170 -0
  23. package/src/components/ui/input-otp.tsx +84 -0
  24. package/src/components/ui/input.tsx +37 -0
  25. package/src/components/ui/item.tsx +196 -0
  26. package/src/components/ui/label.tsx +19 -0
  27. package/src/components/ui/popover.tsx +87 -0
  28. package/src/components/ui/radio-group.tsx +47 -0
  29. package/src/components/ui/select.tsx +205 -0
  30. package/src/components/ui/separator.tsx +26 -0
  31. package/src/components/ui/sheet.tsx +141 -0
  32. package/src/components/ui/sidebar.tsx +699 -0
  33. package/src/components/ui/skeleton.tsx +13 -0
  34. package/src/components/ui/sonner.tsx +74 -0
  35. package/src/components/ui/spinner.tsx +18 -0
  36. package/src/components/ui/table.tsx +114 -0
  37. package/src/components/ui/tabs.tsx +88 -0
  38. package/src/components/ui/textarea.tsx +35 -0
  39. package/src/components/ui/toggle-group.tsx +91 -0
  40. package/src/components/ui/toggle.tsx +44 -0
  41. package/src/components/ui/tooltip.tsx +59 -0
  42. package/src/constants/common.test.ts +1 -1
  43. package/src/constants/legal.test.ts +1 -1
  44. package/src/constants/payment.test.ts +9 -9
  45. package/src/constants/platforms.test.ts +1 -1
  46. package/src/constants/services.test.ts +1 -1
  47. package/src/hooks/use-mobile.ts +19 -0
  48. package/src/index.css +135 -0
  49. package/src/lib/utils.ts +6 -0
  50. package/src/main.tsx +16 -0
  51. package/src/moderation/datasets/bn.ts +708 -708
  52. package/src/moderation/normalizer.test.ts +1 -1
  53. package/src/moderation/normalizer.ts +16 -16
  54. package/src/moderation/profanity-service.test.ts +3 -3
  55. package/src/providers/theme-provider.tsx +73 -0
  56. package/src/types/supabase.ts +649 -647
  57. package/src/utils/check-moderation.test.ts +12 -12
  58. package/src/utils/format-number.test.ts +1 -1
  59. package/src/utils/format-plain-text.test.ts +1 -1
  60. package/src/utils/get-social-handle.test.ts +3 -3
  61. package/src/utils/get-social-link.test.ts +9 -9
  62. package/src/utils/get-social-link.ts +5 -3
  63. package/src/utils/get-user-name-initials.test.ts +1 -1
  64. package/src/utils/get-user-name-initials.ts +4 -4
  65. package/src/utils/get-user-page-link.ts +1 -1
  66. package/src/utils/index.ts +5 -5
  67. package/src/utils/open-to-new-window.ts +3 -1
  68. package/src/utils/post-to-facebook.test.ts +1 -1
  69. package/src/utils/post-to-facebook.ts +9 -3
  70. package/src/utils/post-to-instagram.test.ts +1 -1
  71. package/src/utils/post-to-linkedin.test.ts +1 -1
  72. package/src/utils/post-to-linkedin.ts +9 -3
  73. package/src/utils/post-to-x.test.ts +1 -1
  74. package/src/utils/post-to-x.ts +12 -4
  75. package/src/utils/to-human-readable.ts +6 -2
  76. 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
+ };