@hexclave/ui 1.0.3 → 1.0.6
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/copy-button.d.ts +1 -1
- package/dist/components/simple-tooltip.js +2 -2
- package/dist/components/simple-tooltip.js.map +1 -1
- package/dist/components/ui/button.d.ts +2 -2
- package/dist/components/ui/form.d.ts +1 -1
- package/dist/components/ui/form.d.ts.map +1 -1
- package/dist/components/ui/resizable.d.ts +1 -1
- package/dist/components/ui/resizable.d.ts.map +1 -1
- package/dist/components/ui/typography.d.ts +1 -1
- package/dist/esm/components/copy-button.d.ts +1 -1
- package/dist/esm/components/simple-tooltip.js +3 -3
- package/dist/esm/components/simple-tooltip.js.map +1 -1
- package/dist/esm/components/ui/button.d.ts +2 -2
- package/dist/esm/components/ui/form.d.ts +1 -1
- package/dist/esm/components/ui/form.d.ts.map +1 -1
- package/dist/esm/components/ui/resizable.d.ts +1 -1
- package/dist/esm/components/ui/resizable.d.ts.map +1 -1
- package/dist/esm/components/ui/typography.d.ts +1 -1
- package/package.json +3 -2
- package/src/components/action-dialog.tsx +135 -0
- package/src/components/brand-icons.tsx +276 -0
- package/src/components/browser-frame/LICENSE +3 -0
- package/src/components/browser-frame/index.tsx +51 -0
- package/src/components/copy-button.tsx +36 -0
- package/src/components/copy-field.tsx +52 -0
- package/src/components/data-table/cells.tsx +133 -0
- package/src/components/data-table/column-header.tsx +52 -0
- package/src/components/data-table/data-table.tsx +318 -0
- package/src/components/data-table/faceted-filter.tsx +138 -0
- package/src/components/data-table/index.tsx +9 -0
- package/src/components/data-table/pagination.tsx +76 -0
- package/src/components/data-table/toolbar-items.tsx +13 -0
- package/src/components/data-table/toolbar.tsx +100 -0
- package/src/components/data-table/utils.tsx +7 -0
- package/src/components/data-table/view-options.tsx +64 -0
- package/src/components/simple-tooltip.tsx +48 -0
- package/src/components/ui/accordion.tsx +58 -0
- package/src/components/ui/alert.tsx +60 -0
- package/src/components/ui/aspect-ratio.tsx +7 -0
- package/src/components/ui/avatar.tsx +51 -0
- package/src/components/ui/badge.tsx +36 -0
- package/src/components/ui/breadcrumb.tsx +116 -0
- package/src/components/ui/button.tsx +95 -0
- package/src/components/ui/calendar.tsx +77 -0
- package/src/components/ui/card.tsx +88 -0
- package/src/components/ui/checkbox.tsx +31 -0
- package/src/components/ui/collapsible.tsx +11 -0
- package/src/components/ui/command.tsx +159 -0
- package/src/components/ui/context-menu.tsx +205 -0
- package/src/components/ui/dialog.tsx +135 -0
- package/src/components/ui/dropdown-menu.tsx +245 -0
- package/src/components/ui/form.tsx +173 -0
- package/src/components/ui/hover-card.tsx +30 -0
- package/src/components/ui/inline-code.tsx +40 -0
- package/src/components/ui/input-otp.tsx +73 -0
- package/src/components/ui/input.tsx +68 -0
- package/src/components/ui/label.tsx +40 -0
- package/src/components/ui/menubar.tsx +241 -0
- package/src/components/ui/navigation-menu.tsx +131 -0
- package/src/components/ui/password-input.tsx +50 -0
- package/src/components/ui/popover.tsx +34 -0
- package/src/components/ui/progress.tsx +29 -0
- package/src/components/ui/radio-group.tsx +45 -0
- package/src/components/ui/resizable.tsx +45 -0
- package/src/components/ui/scroll-area.tsx +49 -0
- package/src/components/ui/select.tsx +162 -0
- package/src/components/ui/separator.tsx +32 -0
- package/src/components/ui/sheet.tsx +139 -0
- package/src/components/ui/skeleton.tsx +23 -0
- package/src/components/ui/slider.tsx +29 -0
- package/src/components/ui/spinner.tsx +18 -0
- package/src/components/ui/switch.tsx +75 -0
- package/src/components/ui/table.tsx +117 -0
- package/src/components/ui/tabs.tsx +56 -0
- package/src/components/ui/textarea.tsx +24 -0
- package/src/components/ui/toast.tsx +135 -0
- package/src/components/ui/toaster.tsx +35 -0
- package/src/components/ui/toggle-group.tsx +62 -0
- package/src/components/ui/toggle.tsx +46 -0
- package/src/components/ui/tooltip.tsx +40 -0
- package/src/components/ui/typography.tsx +47 -0
- package/src/components/ui/use-toast.tsx +195 -0
- package/src/index.ts +54 -0
- 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,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 };
|