@hexclave/ui 1.0.2 → 1.0.5

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 (86) hide show
  1. package/dist/components/copy-button.d.ts +1 -1
  2. package/dist/components/simple-tooltip.js +2 -2
  3. package/dist/components/simple-tooltip.js.map +1 -1
  4. package/dist/components/ui/badge.d.ts +2 -2
  5. package/dist/components/ui/button.d.ts +2 -2
  6. package/dist/components/ui/form.d.ts +1 -1
  7. package/dist/components/ui/form.d.ts.map +1 -1
  8. package/dist/components/ui/resizable.d.ts +1 -1
  9. package/dist/components/ui/resizable.d.ts.map +1 -1
  10. package/dist/components/ui/typography.d.ts +1 -1
  11. package/dist/esm/components/copy-button.d.ts +1 -1
  12. package/dist/esm/components/simple-tooltip.js +3 -3
  13. package/dist/esm/components/simple-tooltip.js.map +1 -1
  14. package/dist/esm/components/ui/badge.d.ts +2 -2
  15. package/dist/esm/components/ui/button.d.ts +2 -2
  16. package/dist/esm/components/ui/form.d.ts +1 -1
  17. package/dist/esm/components/ui/form.d.ts.map +1 -1
  18. package/dist/esm/components/ui/resizable.d.ts +1 -1
  19. package/dist/esm/components/ui/resizable.d.ts.map +1 -1
  20. package/dist/esm/components/ui/typography.d.ts +1 -1
  21. package/package.json +3 -2
  22. package/src/components/action-dialog.tsx +135 -0
  23. package/src/components/brand-icons.tsx +276 -0
  24. package/src/components/browser-frame/LICENSE +3 -0
  25. package/src/components/browser-frame/index.tsx +51 -0
  26. package/src/components/copy-button.tsx +36 -0
  27. package/src/components/copy-field.tsx +52 -0
  28. package/src/components/data-table/cells.tsx +133 -0
  29. package/src/components/data-table/column-header.tsx +52 -0
  30. package/src/components/data-table/data-table.tsx +318 -0
  31. package/src/components/data-table/faceted-filter.tsx +138 -0
  32. package/src/components/data-table/index.tsx +9 -0
  33. package/src/components/data-table/pagination.tsx +76 -0
  34. package/src/components/data-table/toolbar-items.tsx +13 -0
  35. package/src/components/data-table/toolbar.tsx +100 -0
  36. package/src/components/data-table/utils.tsx +7 -0
  37. package/src/components/data-table/view-options.tsx +64 -0
  38. package/src/components/simple-tooltip.tsx +48 -0
  39. package/src/components/ui/accordion.tsx +58 -0
  40. package/src/components/ui/alert.tsx +60 -0
  41. package/src/components/ui/aspect-ratio.tsx +7 -0
  42. package/src/components/ui/avatar.tsx +51 -0
  43. package/src/components/ui/badge.tsx +36 -0
  44. package/src/components/ui/breadcrumb.tsx +116 -0
  45. package/src/components/ui/button.tsx +95 -0
  46. package/src/components/ui/calendar.tsx +77 -0
  47. package/src/components/ui/card.tsx +88 -0
  48. package/src/components/ui/checkbox.tsx +31 -0
  49. package/src/components/ui/collapsible.tsx +11 -0
  50. package/src/components/ui/command.tsx +159 -0
  51. package/src/components/ui/context-menu.tsx +205 -0
  52. package/src/components/ui/dialog.tsx +135 -0
  53. package/src/components/ui/dropdown-menu.tsx +245 -0
  54. package/src/components/ui/form.tsx +173 -0
  55. package/src/components/ui/hover-card.tsx +30 -0
  56. package/src/components/ui/inline-code.tsx +40 -0
  57. package/src/components/ui/input-otp.tsx +73 -0
  58. package/src/components/ui/input.tsx +68 -0
  59. package/src/components/ui/label.tsx +40 -0
  60. package/src/components/ui/menubar.tsx +241 -0
  61. package/src/components/ui/navigation-menu.tsx +131 -0
  62. package/src/components/ui/password-input.tsx +50 -0
  63. package/src/components/ui/popover.tsx +34 -0
  64. package/src/components/ui/progress.tsx +29 -0
  65. package/src/components/ui/radio-group.tsx +45 -0
  66. package/src/components/ui/resizable.tsx +45 -0
  67. package/src/components/ui/scroll-area.tsx +49 -0
  68. package/src/components/ui/select.tsx +162 -0
  69. package/src/components/ui/separator.tsx +32 -0
  70. package/src/components/ui/sheet.tsx +139 -0
  71. package/src/components/ui/skeleton.tsx +23 -0
  72. package/src/components/ui/slider.tsx +29 -0
  73. package/src/components/ui/spinner.tsx +18 -0
  74. package/src/components/ui/switch.tsx +75 -0
  75. package/src/components/ui/table.tsx +117 -0
  76. package/src/components/ui/tabs.tsx +56 -0
  77. package/src/components/ui/textarea.tsx +24 -0
  78. package/src/components/ui/toast.tsx +135 -0
  79. package/src/components/ui/toaster.tsx +35 -0
  80. package/src/components/ui/toggle-group.tsx +62 -0
  81. package/src/components/ui/toggle.tsx +46 -0
  82. package/src/components/ui/tooltip.tsx +40 -0
  83. package/src/components/ui/typography.tsx +47 -0
  84. package/src/components/ui/use-toast.tsx +195 -0
  85. package/src/index.ts +54 -0
  86. package/src/lib/utils.tsx +6 -0
@@ -0,0 +1,64 @@
1
+ "use client";
2
+
3
+ import { MixerHorizontalIcon } from "@radix-ui/react-icons";
4
+ import { Button } from "../ui/button";
5
+ import {
6
+ DropdownMenu,
7
+ DropdownMenuCheckboxItem,
8
+ DropdownMenuContent,
9
+ DropdownMenuLabel,
10
+ DropdownMenuSeparator,
11
+ DropdownMenuTrigger,
12
+ } from "../ui/dropdown-menu";
13
+ import { Table } from "@tanstack/react-table";
14
+
15
+ type DataTableViewOptionsProps<TData> = {
16
+ table: Table<TData>,
17
+ variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link",
18
+ className?: string,
19
+ iconClassName?: string,
20
+ }
21
+
22
+ export function DataTableViewOptions<TData>({
23
+ table,
24
+ variant = "secondary",
25
+ className = "ml-auto hidden h-8 px-3 text-xs gap-1.5 lg:flex",
26
+ iconClassName = "h-3.5 w-3.5",
27
+ }: DataTableViewOptionsProps<TData>) {
28
+ return (
29
+ <DropdownMenu>
30
+ <DropdownMenuTrigger asChild>
31
+ <Button
32
+ variant={variant}
33
+ size="sm"
34
+ className={className}
35
+ >
36
+ <MixerHorizontalIcon className={iconClassName} />
37
+ View
38
+ </Button>
39
+ </DropdownMenuTrigger>
40
+ <DropdownMenuContent align="end">
41
+ <DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
42
+ <DropdownMenuSeparator />
43
+ {table
44
+ .getAllColumns()
45
+ .filter(
46
+ (column) =>
47
+ (typeof column.accessorFn !== "undefined" || "accessorKey" in column.columnDef) && column.getCanHide()
48
+ )
49
+ .map((column) => {
50
+ return (
51
+ <DropdownMenuCheckboxItem
52
+ key={column.id}
53
+ className="capitalize"
54
+ checked={column.getIsVisible()}
55
+ onCheckedChange={(value) => column.toggleVisibility(!!value)}
56
+ >
57
+ {column.id}
58
+ </DropdownMenuCheckboxItem>
59
+ );
60
+ })}
61
+ </DropdownMenuContent>
62
+ </DropdownMenu>
63
+ );
64
+ }
@@ -0,0 +1,48 @@
1
+ import { TooltipPortal } from "@radix-ui/react-tooltip";
2
+ import { CircleAlert, Info } from "lucide-react";
3
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, cn } from "..";
4
+
5
+ export function SimpleTooltip(props: {
6
+ tooltip: React.ReactNode,
7
+ children?: React.ReactNode,
8
+ type?: 'info' | 'warning',
9
+ inline?: boolean,
10
+ className?: string,
11
+ disabled?: boolean,
12
+ }) {
13
+ const iconClassName = cn("w-4 h-4 text-muted-foreground", props.inline && "inline");
14
+ const icon = props.type === 'warning' ?
15
+ <CircleAlert className={iconClassName} /> :
16
+ props.type === 'info' ?
17
+ <Info className={iconClassName} /> :
18
+ null;
19
+
20
+ const trigger = (
21
+ <>{icon}{props.children}</>
22
+ );
23
+
24
+ return (
25
+ <TooltipProvider>
26
+ <Tooltip open={props.disabled ? false : undefined}>
27
+ <TooltipTrigger asChild>
28
+ {props.inline ? (
29
+ <span className={cn(props.className)}>
30
+ {trigger}
31
+ </span>
32
+ ) : (
33
+ <div className={cn("flex items-center gap-1", props.className)}>
34
+ {trigger}
35
+ </div>
36
+ )}
37
+ </TooltipTrigger>
38
+ {props.tooltip && <TooltipPortal>
39
+ <TooltipContent>
40
+ <div className="max-w-60 text-center text-wrap whitespace-pre-wrap">
41
+ {props.tooltip}
42
+ </div>
43
+ </TooltipContent>
44
+ </TooltipPortal>}
45
+ </Tooltip>
46
+ </TooltipProvider>
47
+ );
48
+ }
@@ -0,0 +1,58 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
5
+ import * as AccordionPrimitive from "@radix-ui/react-accordion";
6
+ import { ChevronDownIcon } from "@radix-ui/react-icons";
7
+
8
+ import { cn } from "../../lib/utils";
9
+
10
+ const Accordion = AccordionPrimitive.Root;
11
+
12
+ const AccordionItem = forwardRefIfNeeded<
13
+ React.ElementRef<typeof AccordionPrimitive.Item>,
14
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
15
+ >(({ className, ...props }, ref) => (
16
+ <AccordionPrimitive.Item
17
+ ref={ref}
18
+ className={cn("border-b", className)}
19
+ {...props}
20
+ />
21
+ ));
22
+ AccordionItem.displayName = "AccordionItem";
23
+
24
+ const AccordionTrigger = forwardRefIfNeeded<
25
+ React.ElementRef<typeof AccordionPrimitive.Trigger>,
26
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
27
+ >(({ className, children, disabled, ...props }, ref) => (
28
+ <AccordionPrimitive.Header className="stack-scope flex">
29
+ <AccordionPrimitive.Trigger
30
+ ref={ref}
31
+ className={cn(
32
+ "flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
33
+ className
34
+ )}
35
+ {...props}
36
+ >
37
+ {children}
38
+ {!disabled && <ChevronDownIcon className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />}
39
+ </AccordionPrimitive.Trigger>
40
+ </AccordionPrimitive.Header>
41
+ ));
42
+ AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
43
+
44
+ const AccordionContent = forwardRefIfNeeded<
45
+ React.ElementRef<typeof AccordionPrimitive.Content>,
46
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
47
+ >(({ className, children, ...props }, ref) => (
48
+ <AccordionPrimitive.Content
49
+ ref={ref}
50
+ className="stack-scope text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
51
+ {...props}
52
+ >
53
+ <div className={cn("pb-4 pt-0", className)}>{children}</div>
54
+ </AccordionPrimitive.Content>
55
+ ));
56
+ AccordionContent.displayName = AccordionPrimitive.Content.displayName;
57
+
58
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
@@ -0,0 +1,60 @@
1
+ import React from "react";
2
+ import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+
5
+ import { cn } from "../../lib/utils";
6
+
7
+ const alertVariants = cva(
8
+ "stack-scope relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-background text-foreground",
13
+ destructive: "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive bg-destructive/5",
14
+ success: "border-success/50 text-success dark:border-success [&>svg]:text-success bg-success/5",
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ variant: "default",
19
+ },
20
+ }
21
+ );
22
+
23
+ const Alert = forwardRefIfNeeded<
24
+ HTMLDivElement,
25
+ React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
26
+ >(({ className, variant, ...props }, ref) => (
27
+ <div
28
+ ref={ref}
29
+ role="alert"
30
+ className={cn(alertVariants({ variant }), className)}
31
+ {...props}
32
+ />
33
+ ));
34
+ Alert.displayName = "Alert";
35
+
36
+ const AlertTitle = forwardRefIfNeeded<
37
+ HTMLParagraphElement,
38
+ React.HTMLAttributes<HTMLHeadingElement>
39
+ >(({ className, ...props }, ref) => (
40
+ <h5
41
+ ref={ref}
42
+ className={cn("mb-1 font-medium leading-none tracking-tight", className)}
43
+ {...props}
44
+ />
45
+ ));
46
+ AlertTitle.displayName = "AlertTitle";
47
+
48
+ const AlertDescription = forwardRefIfNeeded<
49
+ HTMLParagraphElement,
50
+ React.HTMLAttributes<HTMLParagraphElement>
51
+ >(({ className, ...props }, ref) => (
52
+ <div
53
+ ref={ref}
54
+ className={cn("text-sm [&_p]:leading-relaxed", className)}
55
+ {...props}
56
+ />
57
+ ));
58
+ AlertDescription.displayName = "AlertDescription";
59
+
60
+ export { Alert, AlertTitle, AlertDescription };
@@ -0,0 +1,7 @@
1
+ "use client";
2
+
3
+ import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio";
4
+
5
+ const AspectRatio = AspectRatioPrimitive.Root;
6
+
7
+ export { AspectRatio };
@@ -0,0 +1,51 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
5
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
6
+
7
+ import { cn } from "../../lib/utils";
8
+
9
+ const Avatar = forwardRefIfNeeded<
10
+ React.ElementRef<typeof AvatarPrimitive.Root>,
11
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
12
+ >(({ className, ...props }, ref) => (
13
+ <AvatarPrimitive.Root
14
+ ref={ref}
15
+ className={cn(
16
+ "stack-scope relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
17
+ className
18
+ )}
19
+ {...props}
20
+ />
21
+ ));
22
+ Avatar.displayName = AvatarPrimitive.Root.displayName;
23
+
24
+ const AvatarImage = forwardRefIfNeeded<
25
+ React.ElementRef<typeof AvatarPrimitive.Image>,
26
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
27
+ >(({ className, ...props }, ref) => (
28
+ <AvatarPrimitive.Image
29
+ ref={ref}
30
+ className={cn("aspect-square h-full w-full", className)}
31
+ {...props}
32
+ />
33
+ ));
34
+ AvatarImage.displayName = AvatarPrimitive.Image.displayName;
35
+
36
+ const AvatarFallback = forwardRefIfNeeded<
37
+ React.ElementRef<typeof AvatarPrimitive.Fallback>,
38
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
39
+ >(({ className, ...props }, ref) => (
40
+ <AvatarPrimitive.Fallback
41
+ ref={ref}
42
+ className={cn(
43
+ "flex h-full w-full items-center justify-center rounded-full bg-muted",
44
+ className
45
+ )}
46
+ {...props}
47
+ />
48
+ ));
49
+ AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
50
+
51
+ export { Avatar, AvatarImage, AvatarFallback };
@@ -0,0 +1,36 @@
1
+ import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import React from "react";
4
+
5
+ import { cn } from "../../lib/utils";
6
+
7
+ const badgeVariants = cva(
8
+ "stack-scope inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default:
13
+ "border-transparent bg-primary text-primary-foreground shadow",
14
+ secondary:
15
+ "border-transparent bg-secondary text-secondary-foreground",
16
+ destructive:
17
+ "border-transparent bg-destructive text-destructive-foreground shadow",
18
+ outline: "text-foreground",
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ variant: "default",
23
+ },
24
+ }
25
+ );
26
+
27
+ export type BadgeProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof badgeVariants>;
28
+
29
+ const Badge = forwardRefIfNeeded<HTMLDivElement, BadgeProps>(({ className, variant, ...props }, ref) => {
30
+ return (
31
+ <div ref={ref} className={cn(badgeVariants({ variant }), className)} {...props} />
32
+ );
33
+ });
34
+ Badge.displayName = "Badge";
35
+
36
+ export { Badge, badgeVariants };
@@ -0,0 +1,116 @@
1
+ import React from "react";
2
+ import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
3
+ import { ChevronRightIcon, DotsHorizontalIcon } from "@radix-ui/react-icons";
4
+ import { Slot } from "@radix-ui/react-slot";
5
+
6
+ import { cn } from "../../lib/utils";
7
+
8
+ const Breadcrumb = forwardRefIfNeeded<
9
+ HTMLElement,
10
+ React.ComponentPropsWithoutRef<"nav"> & {
11
+ separator?: React.ReactNode,
12
+ }
13
+ >(({ ...props }, ref) => <nav ref={ref} aria-label="breadcrumb" {...props} />);
14
+ Breadcrumb.displayName = "Breadcrumb";
15
+
16
+ const BreadcrumbList = forwardRefIfNeeded<
17
+ HTMLOListElement,
18
+ React.ComponentPropsWithoutRef<"ol">
19
+ >(({ className, ...props }, ref) => (
20
+ <ol
21
+ ref={ref}
22
+ className={cn(
23
+ "stack-scope flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
24
+ className
25
+ )}
26
+ {...props}
27
+ />
28
+ ));
29
+ BreadcrumbList.displayName = "BreadcrumbList";
30
+
31
+ const BreadcrumbItem = forwardRefIfNeeded<
32
+ HTMLLIElement,
33
+ React.ComponentPropsWithoutRef<"li">
34
+ >(({ className, ...props }, ref) => (
35
+ <li
36
+ ref={ref}
37
+ className={cn("inline-flex items-center gap-1.5", className)}
38
+ {...props}
39
+ />
40
+ ));
41
+ BreadcrumbItem.displayName = "BreadcrumbItem";
42
+
43
+ const BreadcrumbLink = forwardRefIfNeeded<
44
+ HTMLAnchorElement,
45
+ React.ComponentPropsWithoutRef<"a"> & {
46
+ asChild?: boolean,
47
+ }
48
+ >(({ asChild, className, ...props }, ref) => {
49
+ const Comp = asChild ? Slot : "a";
50
+
51
+ return (
52
+ <Comp
53
+ ref={ref}
54
+ className={cn("hover:text-foreground", className)}
55
+ {...props}
56
+ />
57
+ );
58
+ });
59
+ BreadcrumbLink.displayName = "BreadcrumbLink";
60
+
61
+ const BreadcrumbPage = forwardRefIfNeeded<
62
+ HTMLSpanElement,
63
+ React.ComponentPropsWithoutRef<"span">
64
+ >(({ className, ...props }, ref) => (
65
+ <span
66
+ ref={ref}
67
+ role="link"
68
+ aria-disabled="true"
69
+ aria-current="page"
70
+ className={cn("font-normal text-foreground", className)}
71
+ {...props}
72
+ />
73
+ ));
74
+ BreadcrumbPage.displayName = "BreadcrumbPage";
75
+
76
+ const BreadcrumbSeparator = ({
77
+ children,
78
+ className,
79
+ ...props
80
+ }: React.ComponentProps<"li">) => (
81
+ <li
82
+ role="presentation"
83
+ aria-hidden="true"
84
+ className={cn("[&>svg]:size-3.5", className)}
85
+ {...props}
86
+ >
87
+ {children ?? <ChevronRightIcon />}
88
+ </li>
89
+ );
90
+ BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
91
+
92
+ const BreadcrumbEllipsis = ({
93
+ className,
94
+ ...props
95
+ }: React.ComponentProps<"span">) => (
96
+ <span
97
+ role="presentation"
98
+ aria-hidden="true"
99
+ className={cn("flex h-9 w-9 items-center justify-center", className)}
100
+ {...props}
101
+ >
102
+ <DotsHorizontalIcon className="h-4 w-4" />
103
+ <span className="sr-only">More</span>
104
+ </span>
105
+ );
106
+ BreadcrumbEllipsis.displayName = "BreadcrumbElipssis";
107
+
108
+ export {
109
+ Breadcrumb,
110
+ BreadcrumbList,
111
+ BreadcrumbItem,
112
+ BreadcrumbLink,
113
+ BreadcrumbPage,
114
+ BreadcrumbSeparator,
115
+ BreadcrumbEllipsis,
116
+ };
@@ -0,0 +1,95 @@
1
+ import { Slot, Slottable } from "@radix-ui/react-slot";
2
+ import { forwardRefIfNeeded } from "@hexclave/shared/dist/utils/react";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import React from "react";
5
+
6
+ import { useAsyncCallback } from "@hexclave/shared/dist/hooks/use-async-callback";
7
+ import { runAsynchronouslyWithAlert } from "@hexclave/shared/dist/utils/promises";
8
+ import { cn } from "../../lib/utils";
9
+ import { Spinner } from "./spinner";
10
+
11
+ const buttonVariants = cva(
12
+ "stack-scope inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
13
+ {
14
+ variants: {
15
+ variant: {
16
+ default:
17
+ "bg-primary text-primary-foreground hover:bg-primary/90",
18
+ destructive:
19
+ "bg-destructive text-destructive-foreground hover:bg-destructive/90",
20
+ outline:
21
+ "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
22
+ secondary:
23
+ "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
+ plain: "",
27
+ },
28
+ size: {
29
+ default: "h-9 px-4 py-2",
30
+ sm: "h-8 rounded-md px-3 text-xs",
31
+ lg: "h-10 rounded-md px-8",
32
+ icon: "h-9 w-9",
33
+ plain: "",
34
+ },
35
+ },
36
+ defaultVariants: {
37
+ variant: "default",
38
+ size: "default",
39
+ },
40
+ }
41
+ );
42
+
43
+ export type OriginalButtonProps = {
44
+ asChild?: boolean,
45
+ } & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<typeof buttonVariants>
46
+
47
+ const OriginalButton = forwardRefIfNeeded<HTMLButtonElement, OriginalButtonProps>(
48
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
49
+ const Comp = asChild ? Slot : "button";
50
+ return (
51
+ <Comp
52
+ className={cn(buttonVariants({ variant, size, className }))}
53
+ ref={ref}
54
+ {...props}
55
+ />
56
+ );
57
+ }
58
+ );
59
+ OriginalButton.displayName = "Button";
60
+
61
+ type ButtonProps = {
62
+ onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void | Promise<void>,
63
+ loading?: boolean,
64
+ loadingStyle?: "spinner" | "disabled",
65
+ } & OriginalButtonProps
66
+
67
+ const Button = forwardRefIfNeeded<HTMLButtonElement, ButtonProps>(
68
+ ({ onClick, loading: loadingProp, loadingStyle = "spinner", children, size, ...props }, ref) => {
69
+ const [handleClick, isLoading] = useAsyncCallback(async (e: React.MouseEvent<HTMLButtonElement>) => {
70
+ await onClick?.(e);
71
+ }, [onClick]);
72
+
73
+ const loading = loadingProp || isLoading;
74
+
75
+ return (
76
+ <OriginalButton
77
+ {...props}
78
+ ref={ref}
79
+ disabled={props.disabled || loading}
80
+ onClick={(e) => runAsynchronouslyWithAlert(handleClick(e))}
81
+ size={size}
82
+ className={cn("relative", loading && "[&>:not(.stack-button-do-not-hide-when-siblings-are)]:invisible", props.className)}
83
+ >
84
+ {loadingStyle === "spinner" && <Spinner className={cn("absolute inset-0 flex items-center justify-center stack-button-do-not-hide-when-siblings-are", !loading && "invisible")} />}
85
+ <Slottable>
86
+ {typeof children === "string" ? <span>{children}</span> : children}
87
+ </Slottable>
88
+ </OriginalButton>
89
+ );
90
+ }
91
+ );
92
+ Button.displayName = "Button";
93
+
94
+ export { Button, ButtonProps, buttonVariants };
95
+
@@ -0,0 +1,77 @@
1
+ "use client";
2
+
3
+ import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons";
4
+ import React from "react";
5
+ import { DayPicker } from "react-day-picker";
6
+
7
+ import { cn } from "../../lib/utils";
8
+ import { buttonVariants } from "./button";
9
+
10
+ export type CalendarProps = React.ComponentProps<typeof DayPicker>
11
+
12
+ function Calendar({
13
+ className,
14
+ classNames,
15
+ showOutsideDays = true,
16
+ ...props
17
+ }: CalendarProps) {
18
+ return (
19
+ <DayPicker
20
+ showOutsideDays={showOutsideDays}
21
+ className={cn("stack-scope p-3", className)}
22
+ classNames={{
23
+ months: "flex flex-col sm:flex-row",
24
+ month: "space-y-4",
25
+ month_caption: "flex justify-center pt-1 relative items-center",
26
+ caption_label: "text-sm font-medium",
27
+ nav: "absolute left-0 right-0",
28
+ button_previous: cn(
29
+ buttonVariants({ variant: "outline" }),
30
+ "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 absolute left-3 z-10"
31
+ ),
32
+ button_next: cn(
33
+ buttonVariants({ variant: "outline" }),
34
+ "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 absolute right-3 z-10"
35
+ ),
36
+ month_grid: "w-full border-collapse space-y-1",
37
+ weekdays: "flex",
38
+ weekday:
39
+ "text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
40
+ week: "flex w-full mt-2",
41
+ day: cn(
42
+ "relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].outside)]:bg-accent/50 [&:has([aria-selected].range_end)]:rounded-r-md",
43
+ props.mode === "range"
44
+ ? "[&:has(>.range_end)]:rounded-r-md [&:has(>.range_start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
45
+ : "[&:has([aria-selected])]:rounded-md"
46
+ ),
47
+ day_button: cn(
48
+ buttonVariants({ variant: "ghost" }),
49
+ "h-8 w-8 p-0 font-normal aria-selected:opacity-100 hover:bg-primary hover:text-primary-foreground"
50
+ ),
51
+ range_start: "range_start",
52
+ range_end: "range_end",
53
+ selected:
54
+ "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground rounded-md",
55
+ outside:
56
+ "opacity-50",
57
+ disabled: "text-muted-foreground opacity-50",
58
+ range_middle:
59
+ "aria-selected:bg-accent aria-selected:text-accent-foreground",
60
+ hidden: "invisible",
61
+ ...classNames,
62
+ }}
63
+ components={{
64
+ Chevron: (props) => {
65
+ if (props.orientation === "left") {
66
+ return <ChevronLeftIcon className="h-4 w-4" />;
67
+ }
68
+ return <ChevronRightIcon className="h-4 w-4" />;
69
+ },
70
+ }}
71
+ {...props}
72
+ />
73
+ );
74
+ }
75
+ Calendar.displayName = "Calendar";
76
+
77
+ export { Calendar };