@alpic-ai/ui 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/accordion-card.d.mts +41 -0
- package/dist/components/accordion-card.mjs +61 -0
- package/dist/components/accordion.d.mts +29 -0
- package/dist/components/accordion.mjs +48 -0
- package/dist/components/alert.d.mts +40 -0
- package/dist/components/alert.mjs +63 -0
- package/dist/components/attachment-tile.d.mts +26 -0
- package/dist/components/attachment-tile.mjs +35 -0
- package/dist/components/avatar.d.mts +52 -0
- package/dist/components/avatar.mjs +81 -0
- package/dist/components/badge.d.mts +20 -0
- package/dist/components/badge.mjs +36 -0
- package/dist/components/breadcrumb.d.mts +42 -0
- package/dist/components/breadcrumb.mjs +79 -0
- package/dist/components/button.d.mts +29 -0
- package/dist/components/button.mjs +67 -0
- package/dist/components/card.d.mts +37 -0
- package/dist/components/card.mjs +54 -0
- package/dist/components/checkbox.d.mts +11 -0
- package/dist/components/checkbox.mjs +26 -0
- package/dist/components/collapsible.d.mts +16 -0
- package/dist/components/collapsible.mjs +24 -0
- package/dist/components/combobox.d.mts +86 -0
- package/dist/components/combobox.mjs +207 -0
- package/dist/components/command.d.mts +38 -0
- package/dist/components/command.mjs +68 -0
- package/dist/components/copyable.d.mts +22 -0
- package/dist/components/copyable.mjs +33 -0
- package/dist/components/description-list.d.mts +22 -0
- package/dist/components/description-list.mjs +34 -0
- package/dist/components/dialog.d.mts +61 -0
- package/dist/components/dialog.mjs +110 -0
- package/dist/components/dropdown-menu.d.mts +72 -0
- package/dist/components/dropdown-menu.mjs +122 -0
- package/dist/components/input-group.d.mts +25 -0
- package/dist/components/input-group.mjs +43 -0
- package/dist/components/input.d.mts +27 -0
- package/dist/components/input.mjs +90 -0
- package/dist/components/label.d.mts +11 -0
- package/dist/components/label.mjs +14 -0
- package/dist/components/pagination.d.mts +18 -0
- package/dist/components/pagination.mjs +42 -0
- package/dist/components/popover.d.mts +22 -0
- package/dist/components/popover.mjs +34 -0
- package/dist/components/radio-group.d.mts +15 -0
- package/dist/components/radio-group.mjs +26 -0
- package/dist/components/scroll-area.d.mts +17 -0
- package/dist/components/scroll-area.mjs +35 -0
- package/dist/components/select-trigger-variants.d.mts +13 -0
- package/dist/components/select-trigger-variants.mjs +23 -0
- package/dist/components/select.d.mts +51 -0
- package/dist/components/select.mjs +95 -0
- package/dist/components/separator.d.mts +13 -0
- package/dist/components/separator.mjs +16 -0
- package/dist/components/sheet.d.mts +43 -0
- package/dist/components/sheet.mjs +74 -0
- package/dist/components/sidebar.d.mts +163 -0
- package/dist/components/sidebar.mjs +378 -0
- package/dist/components/skeleton.d.mts +16 -0
- package/dist/components/skeleton.mjs +21 -0
- package/dist/components/sonner.d.mts +29 -0
- package/dist/components/sonner.mjs +76 -0
- package/dist/components/spinner.d.mts +30 -0
- package/dist/components/spinner.mjs +46 -0
- package/dist/components/status-dot.d.mts +19 -0
- package/dist/components/status-dot.mjs +34 -0
- package/dist/components/switch.d.mts +11 -0
- package/dist/components/switch.mjs +18 -0
- package/dist/components/table.d.mts +38 -0
- package/dist/components/table.mjs +65 -0
- package/dist/components/tabs.d.mts +58 -0
- package/dist/components/tabs.mjs +119 -0
- package/dist/components/tag.d.mts +35 -0
- package/dist/components/tag.mjs +65 -0
- package/dist/components/textarea.d.mts +21 -0
- package/dist/components/textarea.mjs +44 -0
- package/dist/components/toggle-group.d.mts +28 -0
- package/dist/components/toggle-group.mjs +72 -0
- package/dist/components/tooltip-icon-button.d.mts +12 -0
- package/dist/components/tooltip-icon-button.mjs +27 -0
- package/dist/components/tooltip.d.mts +23 -0
- package/dist/components/tooltip.mjs +35 -0
- package/dist/hooks/use-copy-to-clipboard.d.mts +11 -0
- package/dist/hooks/use-copy-to-clipboard.mjs +24 -0
- package/dist/hooks/use-mobile.d.mts +4 -0
- package/dist/hooks/use-mobile.mjs +16 -0
- package/dist/lib/cn.d.mts +6 -0
- package/dist/lib/cn.mjs +8 -0
- package/package.json +88 -0
- package/src/components/accordion-card.tsx +103 -0
- package/src/components/accordion.tsx +63 -0
- package/src/components/alert.tsx +74 -0
- package/src/components/attachment-tile.tsx +68 -0
- package/src/components/avatar.tsx +127 -0
- package/src/components/badge.tsx +41 -0
- package/src/components/breadcrumb.tsx +98 -0
- package/src/components/button.tsx +106 -0
- package/src/components/card.tsx +62 -0
- package/src/components/checkbox.tsx +35 -0
- package/src/components/collapsible.tsx +18 -0
- package/src/components/combobox.tsx +393 -0
- package/src/components/command.tsx +112 -0
- package/src/components/copyable.tsx +47 -0
- package/src/components/description-list.tsx +36 -0
- package/src/components/dialog.tsx +161 -0
- package/src/components/dropdown-menu.tsx +234 -0
- package/src/components/input-group.tsx +97 -0
- package/src/components/input.tsx +145 -0
- package/src/components/label.tsx +20 -0
- package/src/components/pagination.tsx +53 -0
- package/src/components/popover.tsx +49 -0
- package/src/components/radio-group.tsx +33 -0
- package/src/components/scroll-area.tsx +48 -0
- package/src/components/select-trigger-variants.ts +28 -0
- package/src/components/select.tsx +186 -0
- package/src/components/separator.tsx +30 -0
- package/src/components/sheet.tsx +112 -0
- package/src/components/sidebar.tsx +682 -0
- package/src/components/skeleton.tsx +24 -0
- package/src/components/sonner.tsx +91 -0
- package/src/components/spinner.tsx +62 -0
- package/src/components/status-dot.tsx +33 -0
- package/src/components/switch.tsx +33 -0
- package/src/components/table.tsx +89 -0
- package/src/components/tabs.tsx +226 -0
- package/src/components/tag.tsx +82 -0
- package/src/components/textarea.tsx +70 -0
- package/src/components/toggle-group.tsx +96 -0
- package/src/components/tooltip-icon-button.tsx +33 -0
- package/src/components/tooltip.tsx +54 -0
- package/src/hooks/use-copy-to-clipboard.ts +27 -0
- package/src/hooks/use-mobile.ts +17 -0
- package/src/lib/cn.ts +6 -0
- package/src/styles/tokens.css +352 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { FileText, XIcon } from "lucide-react";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
|
|
6
|
+
import { cn } from "../lib/cn";
|
|
7
|
+
import { TooltipIconButton } from "./tooltip-icon-button";
|
|
8
|
+
|
|
9
|
+
interface AttachmentTileProps {
|
|
10
|
+
/** Source URL for the attachment thumbnail */
|
|
11
|
+
src?: string;
|
|
12
|
+
/** Whether the attachment is an image */
|
|
13
|
+
isImage?: boolean;
|
|
14
|
+
/** Accessible label describing the attachment */
|
|
15
|
+
label?: string;
|
|
16
|
+
/** Called when the remove button is clicked */
|
|
17
|
+
onRemove?: () => void;
|
|
18
|
+
/** Called when the tile is clicked (e.g. to open a preview) */
|
|
19
|
+
onClick?: () => void;
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function AttachmentTile({ src, isImage, label, onRemove, onClick, className }: AttachmentTileProps) {
|
|
24
|
+
const [hasError, setHasError] = useState(false);
|
|
25
|
+
const showImage = isImage && src && !hasError;
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className={cn("group relative w-fit", className)}>
|
|
29
|
+
<button
|
|
30
|
+
type="button"
|
|
31
|
+
className={cn(
|
|
32
|
+
"bg-muted flex size-18 cursor-pointer items-center justify-center overflow-hidden rounded-[14px] border transition-opacity",
|
|
33
|
+
"[@media(hover:hover)]:hover:opacity-75",
|
|
34
|
+
)}
|
|
35
|
+
onClick={onClick}
|
|
36
|
+
aria-label={label ?? `${isImage ? "Image" : "File"} attachment`}
|
|
37
|
+
>
|
|
38
|
+
{showImage ? (
|
|
39
|
+
<img
|
|
40
|
+
src={src}
|
|
41
|
+
alt="Attachment preview"
|
|
42
|
+
className="size-full object-cover"
|
|
43
|
+
onError={() => setHasError(true)}
|
|
44
|
+
/>
|
|
45
|
+
) : (
|
|
46
|
+
<FileText className="text-muted-foreground size-8" />
|
|
47
|
+
)}
|
|
48
|
+
</button>
|
|
49
|
+
{onRemove && (
|
|
50
|
+
<TooltipIconButton
|
|
51
|
+
tooltip="Remove file"
|
|
52
|
+
size="icon-rounded"
|
|
53
|
+
variant="primary"
|
|
54
|
+
onClick={onRemove}
|
|
55
|
+
className={cn(
|
|
56
|
+
"absolute -top-1.5 -right-1.5 z-10 size-5",
|
|
57
|
+
"opacity-0 transition-opacity group-hover:opacity-100 focus:opacity-100",
|
|
58
|
+
)}
|
|
59
|
+
>
|
|
60
|
+
<XIcon className="size-3 dark:stroke-[2.5px]" />
|
|
61
|
+
</TooltipIconButton>
|
|
62
|
+
)}
|
|
63
|
+
</div>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export type { AttachmentTileProps };
|
|
68
|
+
export { AttachmentTile };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import type * as React from "react";
|
|
6
|
+
|
|
7
|
+
import { cn } from "../lib/cn";
|
|
8
|
+
|
|
9
|
+
const avatarVariants = cva("relative inline-flex shrink-0", {
|
|
10
|
+
variants: {
|
|
11
|
+
size: {
|
|
12
|
+
xs: "size-6",
|
|
13
|
+
sm: "size-8",
|
|
14
|
+
md: "size-10",
|
|
15
|
+
lg: "size-12",
|
|
16
|
+
xl: "size-14",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: {
|
|
20
|
+
size: "md",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const avatarIndicatorVariants = cva(
|
|
25
|
+
"absolute -bottom-px -right-px rounded-full bg-avatar-online border-2 border-background",
|
|
26
|
+
{
|
|
27
|
+
variants: {
|
|
28
|
+
size: {
|
|
29
|
+
xs: "size-2",
|
|
30
|
+
sm: "size-2.5",
|
|
31
|
+
md: "size-3",
|
|
32
|
+
lg: "size-3",
|
|
33
|
+
xl: "size-3.5",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
defaultVariants: {
|
|
37
|
+
size: "md",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
type AvatarSize = NonNullable<VariantProps<typeof avatarVariants>["size"]>;
|
|
43
|
+
type AvatarStatus = "online";
|
|
44
|
+
|
|
45
|
+
interface AvatarProps extends React.ComponentProps<typeof AvatarPrimitive.Root>, VariantProps<typeof avatarVariants> {
|
|
46
|
+
status?: AvatarStatus;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function Avatar({ size, status, className, children, ...props }: AvatarProps) {
|
|
50
|
+
return (
|
|
51
|
+
<span className={cn(avatarVariants({ size }), className)}>
|
|
52
|
+
<AvatarPrimitive.Root
|
|
53
|
+
data-slot="avatar"
|
|
54
|
+
className="size-full overflow-hidden rounded-full border border-avatar-border"
|
|
55
|
+
{...props}
|
|
56
|
+
>
|
|
57
|
+
{children}
|
|
58
|
+
</AvatarPrimitive.Root>
|
|
59
|
+
{status === "online" && <span aria-label="Online" role="img" className={avatarIndicatorVariants({ size })} />}
|
|
60
|
+
</span>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function AvatarImage({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
|
|
65
|
+
return (
|
|
66
|
+
<AvatarPrimitive.Image
|
|
67
|
+
data-slot="avatar-image"
|
|
68
|
+
className={cn("aspect-square size-full object-cover", className)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function AvatarFallback({ className, ...props }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
|
|
75
|
+
return (
|
|
76
|
+
<AvatarPrimitive.Fallback
|
|
77
|
+
data-slot="avatar-fallback"
|
|
78
|
+
className={cn(
|
|
79
|
+
"flex size-full items-center justify-center rounded-full bg-muted type-text-sm font-medium text-muted-foreground",
|
|
80
|
+
className,
|
|
81
|
+
)}
|
|
82
|
+
{...props}
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─── AvatarLabelGroup ─────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
interface AvatarLabelGroupProps extends React.ComponentProps<"div"> {
|
|
90
|
+
size?: "sm" | "md";
|
|
91
|
+
src?: string;
|
|
92
|
+
alt?: string;
|
|
93
|
+
fallback?: string;
|
|
94
|
+
name: string;
|
|
95
|
+
label?: string;
|
|
96
|
+
status?: AvatarStatus;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function AvatarLabelGroup({
|
|
100
|
+
size = "md",
|
|
101
|
+
src,
|
|
102
|
+
alt,
|
|
103
|
+
fallback,
|
|
104
|
+
name,
|
|
105
|
+
label,
|
|
106
|
+
status,
|
|
107
|
+
className,
|
|
108
|
+
...props
|
|
109
|
+
}: AvatarLabelGroupProps) {
|
|
110
|
+
const avatarSize = size === "sm" ? ("sm" as const) : ("md" as const);
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div className={cn("flex items-center gap-2", className)} {...props}>
|
|
114
|
+
<Avatar size={avatarSize} status={status}>
|
|
115
|
+
{src && <AvatarImage src={src} alt={alt ?? name} />}
|
|
116
|
+
<AvatarFallback>{fallback ?? name.slice(0, 2).toUpperCase()}</AvatarFallback>
|
|
117
|
+
</Avatar>
|
|
118
|
+
<div className="flex min-w-0 flex-col">
|
|
119
|
+
<span className="type-text-sm font-semibold text-foreground truncate">{name}</span>
|
|
120
|
+
{label && <span className="type-text-sm text-subtle-foreground truncate">{label}</span>}
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export type { AvatarSize, AvatarStatus };
|
|
127
|
+
export { Avatar, AvatarFallback, AvatarImage, AvatarLabelGroup };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@alpic-ai/ui/lib/cn";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
|
|
6
|
+
const badgeVariants = cva(
|
|
7
|
+
"inline-flex items-center justify-center rounded-full border font-medium whitespace-nowrap shrink-0 [&>svg]:shrink-0 [&>svg]:pointer-events-none",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
secondary: "border-border bg-background text-muted-foreground",
|
|
12
|
+
primary: "border-transparent bg-primary text-primary-foreground",
|
|
13
|
+
success: "border-badge-success/30 bg-badge-success/10 text-badge-success",
|
|
14
|
+
warning: "border-badge-warning/30 bg-badge-warning/10 text-badge-warning",
|
|
15
|
+
error: "border-badge-error/30 bg-badge-error/10 text-badge-error",
|
|
16
|
+
},
|
|
17
|
+
size: {
|
|
18
|
+
sm: "px-1.5 py-px gap-0.5 type-text-xs [&>svg]:size-2.5",
|
|
19
|
+
md: "px-[9px] py-0.5 gap-1 type-text-sm [&>svg]:size-3",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
variant: "secondary",
|
|
24
|
+
size: "md",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
interface BadgeProps extends React.ComponentProps<"span">, VariantProps<typeof badgeVariants> {}
|
|
30
|
+
|
|
31
|
+
function Badge({ className, variant, size, children, ...props }: BadgeProps) {
|
|
32
|
+
return (
|
|
33
|
+
<span className={cn(badgeVariants({ variant, size }), className)} {...props}>
|
|
34
|
+
{children}
|
|
35
|
+
</span>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type BadgeVariant = NonNullable<VariantProps<typeof badgeVariants>["variant"]>;
|
|
40
|
+
|
|
41
|
+
export { Badge, type BadgeVariant, badgeVariants };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@alpic-ai/ui/lib/cn";
|
|
4
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
5
|
+
import { ChevronDown, Ellipsis } from "lucide-react";
|
|
6
|
+
import type * as React from "react";
|
|
7
|
+
|
|
8
|
+
function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
|
|
9
|
+
return <nav data-slot="breadcrumb" aria-label="Breadcrumb" className={className} {...props} />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
|
13
|
+
return <ol data-slot="breadcrumb-list" className={cn("flex items-center gap-2", className)} {...props} />;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
|
17
|
+
return (
|
|
18
|
+
<li
|
|
19
|
+
data-slot="breadcrumb-item"
|
|
20
|
+
className={cn("inline-flex items-center gap-0.5 rounded-sm px-1 py-0.5", className)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function BreadcrumbLink({ asChild, className, ...props }: React.ComponentProps<"a"> & { asChild?: boolean }) {
|
|
27
|
+
const Comp = asChild ? Slot : "a";
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Comp
|
|
31
|
+
data-slot="breadcrumb-link"
|
|
32
|
+
className={cn(
|
|
33
|
+
"type-text-sm font-medium text-subtle-foreground transition-colors [@media(hover:hover)]:hover:text-muted-foreground",
|
|
34
|
+
className,
|
|
35
|
+
)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
|
42
|
+
return (
|
|
43
|
+
<span
|
|
44
|
+
data-slot="breadcrumb-page"
|
|
45
|
+
aria-current="page"
|
|
46
|
+
className={cn("type-text-sm font-medium text-foreground", className)}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function BreadcrumbSeparator({ children, className, ...props }: React.ComponentProps<"li">) {
|
|
53
|
+
return (
|
|
54
|
+
<li
|
|
55
|
+
data-slot="breadcrumb-separator"
|
|
56
|
+
role="presentation"
|
|
57
|
+
aria-hidden="true"
|
|
58
|
+
className={cn("flex items-center text-subtle-foreground", className)}
|
|
59
|
+
{...props}
|
|
60
|
+
>
|
|
61
|
+
{children ?? <span className="inline-block size-4 text-center leading-4 select-none">/</span>}
|
|
62
|
+
</li>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function BreadcrumbChevron({ className, ...props }: React.ComponentProps<"span">) {
|
|
67
|
+
return (
|
|
68
|
+
<span data-slot="breadcrumb-chevron" className={cn("text-subtle-foreground [&>svg]:size-4", className)} {...props}>
|
|
69
|
+
<ChevronDown />
|
|
70
|
+
</span>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function BreadcrumbEllipsis({ className, ...props }: React.ComponentProps<"span">) {
|
|
75
|
+
return (
|
|
76
|
+
<span
|
|
77
|
+
data-slot="breadcrumb-ellipsis"
|
|
78
|
+
role="presentation"
|
|
79
|
+
aria-hidden="true"
|
|
80
|
+
className={cn("flex size-9 items-center justify-center", className)}
|
|
81
|
+
{...props}
|
|
82
|
+
>
|
|
83
|
+
<Ellipsis className="size-4" />
|
|
84
|
+
<span className="sr-only">More</span>
|
|
85
|
+
</span>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
Breadcrumb,
|
|
91
|
+
BreadcrumbChevron,
|
|
92
|
+
BreadcrumbEllipsis,
|
|
93
|
+
BreadcrumbItem,
|
|
94
|
+
BreadcrumbLink,
|
|
95
|
+
BreadcrumbList,
|
|
96
|
+
BreadcrumbPage,
|
|
97
|
+
BreadcrumbSeparator,
|
|
98
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import { Loader2 } from "lucide-react";
|
|
6
|
+
import type * as React from "react";
|
|
7
|
+
|
|
8
|
+
import { cn } from "../lib/cn";
|
|
9
|
+
|
|
10
|
+
const buttonVariants = cva(
|
|
11
|
+
[
|
|
12
|
+
"inline-flex items-center justify-center gap-1 whitespace-nowrap",
|
|
13
|
+
"h-8 px-2 rounded-md",
|
|
14
|
+
"font-medium",
|
|
15
|
+
"transition-colors",
|
|
16
|
+
"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
17
|
+
"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0",
|
|
18
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
19
|
+
].join(" "),
|
|
20
|
+
{
|
|
21
|
+
variants: {
|
|
22
|
+
variant: {
|
|
23
|
+
primary: ["bg-primary text-primary-foreground", "[@media(hover:hover)]:hover:bg-primary-hover"].join(" "),
|
|
24
|
+
secondary: [
|
|
25
|
+
"border border-border bg-background text-muted-foreground",
|
|
26
|
+
"[@media(hover:hover)]:hover:bg-background-hover [@media(hover:hover)]:hover:text-muted-foreground-hover",
|
|
27
|
+
].join(" "),
|
|
28
|
+
tertiary: [
|
|
29
|
+
"text-muted-foreground",
|
|
30
|
+
"[@media(hover:hover)]:hover:bg-background-hover [@media(hover:hover)]:hover:text-muted-foreground-hover",
|
|
31
|
+
"focus-visible:bg-background",
|
|
32
|
+
].join(" "),
|
|
33
|
+
link: [
|
|
34
|
+
"h-auto px-0 rounded-xs underline-offset-4",
|
|
35
|
+
"text-link",
|
|
36
|
+
"[@media(hover:hover)]:hover:underline",
|
|
37
|
+
"focus-visible:bg-background",
|
|
38
|
+
].join(" "),
|
|
39
|
+
"link-muted": [
|
|
40
|
+
"h-auto px-0 rounded-xs underline-offset-4",
|
|
41
|
+
"text-link-muted",
|
|
42
|
+
"[@media(hover:hover)]:hover:underline",
|
|
43
|
+
"focus-visible:bg-background",
|
|
44
|
+
].join(" "),
|
|
45
|
+
destructive: [
|
|
46
|
+
"bg-destructive text-destructive-foreground",
|
|
47
|
+
"[@media(hover:hover)]:hover:bg-destructive-hover",
|
|
48
|
+
].join(" "),
|
|
49
|
+
},
|
|
50
|
+
size: {
|
|
51
|
+
default: "type-text-sm",
|
|
52
|
+
icon: "size-8 p-0 type-text-sm",
|
|
53
|
+
"icon-rounded": "size-8 p-0 rounded-full type-text-sm",
|
|
54
|
+
pill: "h-7 px-3 rounded-full type-text-xs",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
defaultVariants: {
|
|
58
|
+
variant: "primary",
|
|
59
|
+
size: "default",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
interface ButtonProps extends React.ComponentProps<"button">, VariantProps<typeof buttonVariants> {
|
|
65
|
+
asChild?: boolean;
|
|
66
|
+
loading?: boolean;
|
|
67
|
+
icon?: React.ReactNode;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function Button({
|
|
71
|
+
className,
|
|
72
|
+
variant,
|
|
73
|
+
size,
|
|
74
|
+
type = "button",
|
|
75
|
+
asChild = false,
|
|
76
|
+
loading = false,
|
|
77
|
+
icon,
|
|
78
|
+
disabled,
|
|
79
|
+
children,
|
|
80
|
+
...props
|
|
81
|
+
}: ButtonProps) {
|
|
82
|
+
const Comp = asChild ? Slot : "button";
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<Comp
|
|
86
|
+
data-slot="button"
|
|
87
|
+
className={cn(buttonVariants({ variant, size }), className)}
|
|
88
|
+
type={type}
|
|
89
|
+
disabled={disabled || loading}
|
|
90
|
+
aria-busy={loading || undefined}
|
|
91
|
+
{...props}
|
|
92
|
+
>
|
|
93
|
+
{asChild ? (
|
|
94
|
+
children
|
|
95
|
+
) : (
|
|
96
|
+
<>
|
|
97
|
+
{loading ? <Loader2 className="motion-safe:animate-spin" /> : icon}
|
|
98
|
+
{children}
|
|
99
|
+
</>
|
|
100
|
+
)}
|
|
101
|
+
</Comp>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type { ButtonProps };
|
|
106
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../lib/cn";
|
|
4
|
+
|
|
5
|
+
function Card({ className, hoverable, ...props }: React.ComponentProps<"div"> & { hoverable?: boolean }) {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
data-slot="card"
|
|
9
|
+
className={cn(
|
|
10
|
+
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6",
|
|
11
|
+
hoverable && "transition-shadow [@media(hover:hover)]:hover:shadow-lg",
|
|
12
|
+
className,
|
|
13
|
+
)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
data-slot="card-header"
|
|
23
|
+
className={cn(
|
|
24
|
+
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
|
25
|
+
className,
|
|
26
|
+
)}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
33
|
+
return <div data-slot="card-title" className={cn("type-text-md font-semibold leading-none", className)} {...props} />;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
|
37
|
+
return (
|
|
38
|
+
<div data-slot="card-description" className={cn("type-text-sm text-muted-foreground", className)} {...props} />
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
|
|
43
|
+
return (
|
|
44
|
+
<div
|
|
45
|
+
data-slot="card-action"
|
|
46
|
+
className={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
53
|
+
return <div data-slot="card-content" className={cn("px-6", className)} {...props} />;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
57
|
+
return (
|
|
58
|
+
<div data-slot="card-footer" className={cn("flex items-center px-6 [.border-t]:pt-6", className)} {...props} />
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
4
|
+
import { CheckIcon, MinusIcon } from "lucide-react";
|
|
5
|
+
import type * as React from "react";
|
|
6
|
+
|
|
7
|
+
import { cn } from "../lib/cn";
|
|
8
|
+
|
|
9
|
+
function Checkbox({ className, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
|
|
10
|
+
return (
|
|
11
|
+
<CheckboxPrimitive.Root
|
|
12
|
+
data-slot="checkbox"
|
|
13
|
+
className={cn(
|
|
14
|
+
"peer size-4 shrink-0 rounded-xs border border-border bg-background",
|
|
15
|
+
"not-disabled:data-[state=checked]:border-primary not-disabled:data-[state=checked]:bg-primary not-disabled:data-[state=checked]:text-primary-foreground",
|
|
16
|
+
"not-disabled:data-[state=indeterminate]:border-primary not-disabled:data-[state=indeterminate]:bg-primary not-disabled:data-[state=indeterminate]:text-primary-foreground",
|
|
17
|
+
"transition-colors",
|
|
18
|
+
"outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
19
|
+
"disabled:pointer-events-none disabled:bg-muted disabled:border-border disabled:text-border",
|
|
20
|
+
className,
|
|
21
|
+
)}
|
|
22
|
+
{...props}
|
|
23
|
+
>
|
|
24
|
+
<CheckboxPrimitive.Indicator
|
|
25
|
+
data-slot="checkbox-indicator"
|
|
26
|
+
className="flex items-center justify-center text-current [&[data-state=indeterminate]>svg:first-child]:hidden [&[data-state=checked]>svg:last-child]:hidden"
|
|
27
|
+
>
|
|
28
|
+
<CheckIcon className="size-3" strokeWidth={4} />
|
|
29
|
+
<MinusIcon className="size-3" strokeWidth={4} />
|
|
30
|
+
</CheckboxPrimitive.Indicator>
|
|
31
|
+
</CheckboxPrimitive.Root>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { Checkbox };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
|
|
4
|
+
import type * as React from "react";
|
|
5
|
+
|
|
6
|
+
function Collapsible({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
|
|
7
|
+
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function CollapsibleTrigger({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
|
|
11
|
+
return <CollapsiblePrimitive.CollapsibleTrigger data-slot="collapsible-trigger" {...props} />;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function CollapsibleContent({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
|
|
15
|
+
return <CollapsiblePrimitive.CollapsibleContent data-slot="collapsible-content" {...props} />;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { Collapsible, CollapsibleContent, CollapsibleTrigger };
|