@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,161 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import { XIcon } from "lucide-react";
|
|
6
|
+
import type * as React from "react";
|
|
7
|
+
import { cn } from "../lib/cn";
|
|
8
|
+
import { Button, type ButtonProps } from "./button";
|
|
9
|
+
|
|
10
|
+
function Dialog({ ...props }: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
|
11
|
+
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function DialogTrigger({ ...props }: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
|
15
|
+
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function DialogPortal({ ...props }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
|
19
|
+
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function DialogOverlay({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
|
23
|
+
return (
|
|
24
|
+
<DialogPrimitive.Overlay
|
|
25
|
+
data-slot="dialog-overlay"
|
|
26
|
+
className={cn(
|
|
27
|
+
"fixed inset-0 z-50 bg-overlay",
|
|
28
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
29
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
30
|
+
className,
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const dialogContentVariants = cva(
|
|
38
|
+
[
|
|
39
|
+
"bg-background fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2",
|
|
40
|
+
"overflow-y-auto rounded-2xl px-6 shadow-lg outline-none",
|
|
41
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
42
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
43
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
44
|
+
"duration-200",
|
|
45
|
+
].join(" "),
|
|
46
|
+
{
|
|
47
|
+
variants: {
|
|
48
|
+
size: {
|
|
49
|
+
sm: "max-w-[400px]",
|
|
50
|
+
lg: "max-w-[544px]",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
defaultVariants: {
|
|
54
|
+
size: "sm",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
interface DialogContentProps
|
|
60
|
+
extends React.ComponentProps<typeof DialogPrimitive.Content>,
|
|
61
|
+
VariantProps<typeof dialogContentVariants> {
|
|
62
|
+
showCloseButton?: boolean;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function DialogContent({ className, children, size, showCloseButton = true, ...props }: DialogContentProps) {
|
|
66
|
+
return (
|
|
67
|
+
<DialogPortal>
|
|
68
|
+
<DialogOverlay />
|
|
69
|
+
<DialogPrimitive.Content
|
|
70
|
+
data-slot="dialog-content"
|
|
71
|
+
className={cn(dialogContentVariants({ size }), className)}
|
|
72
|
+
{...props}
|
|
73
|
+
>
|
|
74
|
+
{children}
|
|
75
|
+
{showCloseButton && (
|
|
76
|
+
<DialogPrimitive.Close
|
|
77
|
+
data-slot="dialog-close"
|
|
78
|
+
className={cn(
|
|
79
|
+
"absolute top-3 right-4 flex size-8 items-center justify-center rounded-md",
|
|
80
|
+
"text-subtle-foreground transition-colors",
|
|
81
|
+
"[@media(hover:hover)]:hover:text-foreground",
|
|
82
|
+
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none",
|
|
83
|
+
"disabled:pointer-events-none",
|
|
84
|
+
)}
|
|
85
|
+
>
|
|
86
|
+
<XIcon className="size-4" />
|
|
87
|
+
<span className="sr-only">Close</span>
|
|
88
|
+
</DialogPrimitive.Close>
|
|
89
|
+
)}
|
|
90
|
+
</DialogPrimitive.Content>
|
|
91
|
+
</DialogPortal>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
96
|
+
return <div data-slot="dialog-header" className={cn("flex flex-col gap-0.5 pt-6 pr-10", className)} {...props} />;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const dialogFooterVariants = cva("pb-6 pt-6", {
|
|
100
|
+
variants: {
|
|
101
|
+
layout: {
|
|
102
|
+
horizontal: "flex gap-3 [&>*]:flex-1",
|
|
103
|
+
vertical: "flex flex-col gap-3",
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
defaultVariants: {
|
|
107
|
+
layout: "horizontal",
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
interface DialogFooterProps extends React.ComponentProps<"div">, VariantProps<typeof dialogFooterVariants> {}
|
|
112
|
+
|
|
113
|
+
function DialogFooter({ className, layout, ...props }: DialogFooterProps) {
|
|
114
|
+
return <div data-slot="dialog-footer" className={cn(dialogFooterVariants({ layout }), className)} {...props} />;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function DialogTitle({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
|
118
|
+
return (
|
|
119
|
+
<DialogPrimitive.Title
|
|
120
|
+
data-slot="dialog-title"
|
|
121
|
+
className={cn("type-text-md font-semibold text-foreground", className)}
|
|
122
|
+
{...props}
|
|
123
|
+
/>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function DialogClose({ children = "Cancel", ...props }: Omit<ButtonProps, "variant" | "asChild">) {
|
|
128
|
+
return (
|
|
129
|
+
<DialogPrimitive.Close data-slot="dialog-cancel" asChild>
|
|
130
|
+
<Button variant="secondary" {...props}>
|
|
131
|
+
{children}
|
|
132
|
+
</Button>
|
|
133
|
+
</DialogPrimitive.Close>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function DialogDescription({ className, ...props }: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
|
138
|
+
return (
|
|
139
|
+
<DialogPrimitive.Description
|
|
140
|
+
data-slot="dialog-description"
|
|
141
|
+
className={cn("type-text-sm text-subtle-foreground", className)}
|
|
142
|
+
{...props}
|
|
143
|
+
/>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export type { DialogContentProps, DialogFooterProps };
|
|
148
|
+
export {
|
|
149
|
+
Dialog,
|
|
150
|
+
DialogClose,
|
|
151
|
+
DialogContent,
|
|
152
|
+
DialogDescription,
|
|
153
|
+
DialogFooter,
|
|
154
|
+
DialogHeader,
|
|
155
|
+
DialogOverlay,
|
|
156
|
+
DialogPortal,
|
|
157
|
+
DialogTitle,
|
|
158
|
+
DialogTrigger,
|
|
159
|
+
dialogContentVariants,
|
|
160
|
+
dialogFooterVariants,
|
|
161
|
+
};
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
|
+
import { ChevronRightIcon } from "lucide-react";
|
|
6
|
+
import type * as React from "react";
|
|
7
|
+
|
|
8
|
+
import { cn } from "../lib/cn";
|
|
9
|
+
|
|
10
|
+
function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
|
11
|
+
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function DropdownMenuPortal({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
|
|
15
|
+
return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function DropdownMenuTrigger({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
|
|
19
|
+
return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function DropdownMenuContent({
|
|
23
|
+
className,
|
|
24
|
+
sideOffset = 4,
|
|
25
|
+
...props
|
|
26
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
|
|
27
|
+
return (
|
|
28
|
+
<DropdownMenuPrimitive.Portal>
|
|
29
|
+
<DropdownMenuPrimitive.Content
|
|
30
|
+
data-slot="dropdown-menu-content"
|
|
31
|
+
sideOffset={sideOffset}
|
|
32
|
+
className={cn(
|
|
33
|
+
"bg-popover text-popover-foreground",
|
|
34
|
+
"z-50 min-w-60 overflow-x-hidden overflow-y-auto rounded-md border border-border-secondary py-1 shadow-lg",
|
|
35
|
+
"max-h-(--radix-dropdown-menu-content-available-height)",
|
|
36
|
+
"origin-(--radix-dropdown-menu-content-transform-origin)",
|
|
37
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
38
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
39
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
40
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
41
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
42
|
+
className,
|
|
43
|
+
)}
|
|
44
|
+
{...props}
|
|
45
|
+
/>
|
|
46
|
+
</DropdownMenuPrimitive.Portal>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
|
51
|
+
return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* ── Item ─────────────────────────────────────────────────────────────────── */
|
|
55
|
+
|
|
56
|
+
const dropdownMenuItemVariants = cva(
|
|
57
|
+
[
|
|
58
|
+
"relative flex cursor-default items-center gap-2 rounded-sm px-2.5 py-2 outline-hidden select-none",
|
|
59
|
+
"type-text-sm font-semibold",
|
|
60
|
+
"mx-1.5 my-px",
|
|
61
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
62
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
63
|
+
].join(" "),
|
|
64
|
+
{
|
|
65
|
+
variants: {
|
|
66
|
+
variant: {
|
|
67
|
+
default: [
|
|
68
|
+
"text-muted-foreground",
|
|
69
|
+
"[&_svg:not([class*='text-'])]:text-muted-foreground",
|
|
70
|
+
"[@media(hover:hover)]:hover:bg-background-hover [@media(hover:hover)]:hover:text-muted-foreground-hover",
|
|
71
|
+
"focus:bg-background-hover focus:text-muted-foreground-hover",
|
|
72
|
+
].join(" "),
|
|
73
|
+
destructive: [
|
|
74
|
+
"text-destructive",
|
|
75
|
+
"[&_svg]:!text-destructive",
|
|
76
|
+
"[@media(hover:hover)]:hover:bg-destructive/10 dark:[@media(hover:hover)]:hover:bg-destructive/20",
|
|
77
|
+
"focus:bg-destructive/10 dark:focus:bg-destructive/20",
|
|
78
|
+
].join(" "),
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
defaultVariants: {
|
|
82
|
+
variant: "default",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
function DropdownMenuItem({
|
|
88
|
+
className,
|
|
89
|
+
inset,
|
|
90
|
+
variant = "default",
|
|
91
|
+
...props
|
|
92
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> &
|
|
93
|
+
VariantProps<typeof dropdownMenuItemVariants> & {
|
|
94
|
+
inset?: boolean;
|
|
95
|
+
}) {
|
|
96
|
+
return (
|
|
97
|
+
<DropdownMenuPrimitive.Item
|
|
98
|
+
data-slot="dropdown-menu-item"
|
|
99
|
+
data-inset={inset}
|
|
100
|
+
className={cn(dropdownMenuItemVariants({ variant }), inset && "pl-8", className)}
|
|
101
|
+
{...props}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* ── Label ────────────────────────────────────────────────────────────────── */
|
|
107
|
+
|
|
108
|
+
function DropdownMenuLabel({
|
|
109
|
+
className,
|
|
110
|
+
inset,
|
|
111
|
+
...props
|
|
112
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
|
113
|
+
inset?: boolean;
|
|
114
|
+
}) {
|
|
115
|
+
return (
|
|
116
|
+
<DropdownMenuPrimitive.Label
|
|
117
|
+
data-slot="dropdown-menu-label"
|
|
118
|
+
className={cn("px-2.5 py-1.5 type-text-sm font-semibold text-foreground", inset && "pl-8", className)}
|
|
119
|
+
{...props}
|
|
120
|
+
/>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* ── Header (avatar group) ────────────────────────────────────────────────── */
|
|
125
|
+
|
|
126
|
+
function DropdownMenuHeader({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
127
|
+
return (
|
|
128
|
+
<div
|
|
129
|
+
data-slot="dropdown-menu-header"
|
|
130
|
+
className={cn("flex items-center gap-2 border-b border-border-secondary p-3", className)}
|
|
131
|
+
{...props}
|
|
132
|
+
>
|
|
133
|
+
{children}
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* ── Separator ────────────────────────────────────────────────────────────── */
|
|
139
|
+
|
|
140
|
+
function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
|
141
|
+
return (
|
|
142
|
+
<DropdownMenuPrimitive.Separator
|
|
143
|
+
data-slot="dropdown-menu-separator"
|
|
144
|
+
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
|
145
|
+
{...props}
|
|
146
|
+
/>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* ── Shortcut ─────────────────────────────────────────────────────────────── */
|
|
151
|
+
|
|
152
|
+
function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
|
|
153
|
+
return (
|
|
154
|
+
<span
|
|
155
|
+
data-slot="dropdown-menu-shortcut"
|
|
156
|
+
className={cn(
|
|
157
|
+
"ml-auto shrink-0 rounded-xs border border-border px-1 py-px type-text-xs font-medium text-subtle-foreground",
|
|
158
|
+
className,
|
|
159
|
+
)}
|
|
160
|
+
{...props}
|
|
161
|
+
/>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* ── Sub menu ─────────────────────────────────────────────────────────────── */
|
|
166
|
+
|
|
167
|
+
function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
|
|
168
|
+
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function DropdownMenuSubTrigger({
|
|
172
|
+
className,
|
|
173
|
+
inset,
|
|
174
|
+
children,
|
|
175
|
+
...props
|
|
176
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
177
|
+
inset?: boolean;
|
|
178
|
+
}) {
|
|
179
|
+
return (
|
|
180
|
+
<DropdownMenuPrimitive.SubTrigger
|
|
181
|
+
data-slot="dropdown-menu-sub-trigger"
|
|
182
|
+
className={cn(
|
|
183
|
+
dropdownMenuItemVariants({ variant: "default" }),
|
|
184
|
+
"data-[state=open]:bg-background-hover data-[state=open]:text-muted-foreground-hover",
|
|
185
|
+
inset && "pl-8",
|
|
186
|
+
className,
|
|
187
|
+
)}
|
|
188
|
+
{...props}
|
|
189
|
+
>
|
|
190
|
+
{children}
|
|
191
|
+
<ChevronRightIcon className="ml-auto size-4" />
|
|
192
|
+
</DropdownMenuPrimitive.SubTrigger>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function DropdownMenuSubContent({
|
|
197
|
+
className,
|
|
198
|
+
...props
|
|
199
|
+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
|
|
200
|
+
return (
|
|
201
|
+
<DropdownMenuPrimitive.SubContent
|
|
202
|
+
data-slot="dropdown-menu-sub-content"
|
|
203
|
+
className={cn(
|
|
204
|
+
"bg-popover text-popover-foreground",
|
|
205
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-border-secondary py-1 shadow-lg",
|
|
206
|
+
"origin-(--radix-dropdown-menu-content-transform-origin)",
|
|
207
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
208
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
209
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
210
|
+
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2",
|
|
211
|
+
"data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
212
|
+
className,
|
|
213
|
+
)}
|
|
214
|
+
{...props}
|
|
215
|
+
/>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export {
|
|
220
|
+
DropdownMenu,
|
|
221
|
+
DropdownMenuContent,
|
|
222
|
+
DropdownMenuGroup,
|
|
223
|
+
DropdownMenuHeader,
|
|
224
|
+
DropdownMenuItem,
|
|
225
|
+
DropdownMenuLabel,
|
|
226
|
+
DropdownMenuPortal,
|
|
227
|
+
DropdownMenuSeparator,
|
|
228
|
+
DropdownMenuShortcut,
|
|
229
|
+
DropdownMenuSub,
|
|
230
|
+
DropdownMenuSubContent,
|
|
231
|
+
DropdownMenuSubTrigger,
|
|
232
|
+
DropdownMenuTrigger,
|
|
233
|
+
dropdownMenuItemVariants,
|
|
234
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
import type * as React from "react";
|
|
5
|
+
import { cn } from "../lib/cn";
|
|
6
|
+
|
|
7
|
+
function InputGroup({ className, children, ...props }: React.ComponentProps<"fieldset">) {
|
|
8
|
+
return (
|
|
9
|
+
<fieldset
|
|
10
|
+
data-slot="input-group"
|
|
11
|
+
className={cn(
|
|
12
|
+
"group/input-group relative flex w-full items-center rounded-md border bg-background",
|
|
13
|
+
"h-9 min-w-0 has-[>textarea]:h-auto",
|
|
14
|
+
"transition-colors",
|
|
15
|
+
|
|
16
|
+
// Variants based on alignment.
|
|
17
|
+
"has-[>[data-align=inline-start]]:[&>input]:pl-2",
|
|
18
|
+
"has-[>[data-align=inline-end]]:[&>input]:pr-2",
|
|
19
|
+
"has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
|
|
20
|
+
"has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
|
|
21
|
+
|
|
22
|
+
// Focus state — matches Input component pattern.
|
|
23
|
+
"has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:border-2",
|
|
24
|
+
|
|
25
|
+
// Error state.
|
|
26
|
+
"has-[[data-slot][aria-invalid=true]]:border-border-error",
|
|
27
|
+
|
|
28
|
+
// Disabled state.
|
|
29
|
+
"has-[[data-slot=input-group-control]:disabled]:bg-disabled has-[[data-slot=input-group-control]:disabled]:text-disabled-foreground has-[[data-slot=input-group-control]:disabled]:cursor-not-allowed",
|
|
30
|
+
|
|
31
|
+
className,
|
|
32
|
+
)}
|
|
33
|
+
{...props}
|
|
34
|
+
>
|
|
35
|
+
{children}
|
|
36
|
+
</fieldset>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const inputGroupAddonVariants = cva(
|
|
41
|
+
"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 type-text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 group-disabled/input-group:opacity-50",
|
|
42
|
+
{
|
|
43
|
+
variants: {
|
|
44
|
+
align: {
|
|
45
|
+
"inline-start": "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
|
|
46
|
+
"inline-end": "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
|
|
47
|
+
"block-start":
|
|
48
|
+
"order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5",
|
|
49
|
+
"block-end": "order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
defaultVariants: {
|
|
53
|
+
align: "inline-start",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
function InputGroupAddon({
|
|
59
|
+
className,
|
|
60
|
+
align = "inline-start",
|
|
61
|
+
...props
|
|
62
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
|
|
63
|
+
return (
|
|
64
|
+
// biome-ignore lint/a11y/noStaticElementInteractions: click delegates focus to the associated input control
|
|
65
|
+
// biome-ignore lint/a11y/useKeyWithClickEvents: click delegates focus to the associated input control
|
|
66
|
+
<div
|
|
67
|
+
data-slot="input-group-addon"
|
|
68
|
+
data-align={align}
|
|
69
|
+
className={cn(inputGroupAddonVariants({ align }), className)}
|
|
70
|
+
onClick={(event) => {
|
|
71
|
+
if ((event.target as HTMLElement).closest("button")) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const control = event.currentTarget.parentElement?.querySelector<HTMLElement>("textarea, input");
|
|
75
|
+
control?.focus();
|
|
76
|
+
}}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function InputGroupTextarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
|
83
|
+
return (
|
|
84
|
+
<textarea
|
|
85
|
+
data-slot="input-group-control"
|
|
86
|
+
className={cn(
|
|
87
|
+
"w-full flex-1 resize-none rounded-none border-0 bg-transparent px-3 py-3 shadow-none outline-none",
|
|
88
|
+
"type-text-md text-foreground placeholder:text-placeholder",
|
|
89
|
+
"focus-visible:ring-0",
|
|
90
|
+
className,
|
|
91
|
+
)}
|
|
92
|
+
{...props}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export { InputGroup, InputGroupAddon, InputGroupTextarea };
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../lib/cn";
|
|
6
|
+
import { Label } from "./label";
|
|
7
|
+
|
|
8
|
+
const inputSizeStyles = {
|
|
9
|
+
sm: {
|
|
10
|
+
standalone: "h-8 px-2 py-1.5 type-text-sm",
|
|
11
|
+
wrapper: "h-8",
|
|
12
|
+
inner: "px-2 py-1.5",
|
|
13
|
+
text: "type-text-sm",
|
|
14
|
+
leadingText: "pl-2 pr-2 type-text-sm",
|
|
15
|
+
leadingIcon: "pl-2 [&_svg]:size-4",
|
|
16
|
+
},
|
|
17
|
+
md: {
|
|
18
|
+
standalone: "h-8 px-2.5 py-1.5 type-text-md",
|
|
19
|
+
wrapper: "h-8",
|
|
20
|
+
inner: "px-2.5 py-1.5",
|
|
21
|
+
text: "type-text-md",
|
|
22
|
+
leadingText: "pl-2.5 pr-2 type-text-md",
|
|
23
|
+
leadingIcon: "pl-2.5 [&_svg]:size-5",
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
interface InputProps extends Omit<React.ComponentProps<"input">, "size"> {
|
|
28
|
+
label?: string;
|
|
29
|
+
required?: boolean;
|
|
30
|
+
hint?: string;
|
|
31
|
+
error?: string;
|
|
32
|
+
leadingText?: string;
|
|
33
|
+
leadingIcon?: React.ReactNode;
|
|
34
|
+
size?: "sm" | "md";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function Input({
|
|
38
|
+
className,
|
|
39
|
+
id,
|
|
40
|
+
label,
|
|
41
|
+
required,
|
|
42
|
+
hint,
|
|
43
|
+
error,
|
|
44
|
+
leadingText,
|
|
45
|
+
leadingIcon,
|
|
46
|
+
size = "md",
|
|
47
|
+
...props
|
|
48
|
+
}: InputProps) {
|
|
49
|
+
const generatedId = React.useId();
|
|
50
|
+
const fieldId = id ?? generatedId;
|
|
51
|
+
|
|
52
|
+
const hasAddons = leadingText || leadingIcon;
|
|
53
|
+
const sizes = inputSizeStyles[size];
|
|
54
|
+
|
|
55
|
+
const inputElement = (
|
|
56
|
+
<input
|
|
57
|
+
id={fieldId}
|
|
58
|
+
data-slot="input"
|
|
59
|
+
className={cn(
|
|
60
|
+
"w-full min-w-0 bg-transparent outline-none",
|
|
61
|
+
sizes.text,
|
|
62
|
+
"text-foreground placeholder:text-placeholder",
|
|
63
|
+
"disabled:cursor-not-allowed",
|
|
64
|
+
!hasAddons && [
|
|
65
|
+
"flex",
|
|
66
|
+
sizes.standalone,
|
|
67
|
+
"bg-background border border-border rounded-md",
|
|
68
|
+
"transition-colors",
|
|
69
|
+
"focus-visible:border-ring focus-visible:border-2",
|
|
70
|
+
"disabled:bg-disabled disabled:text-disabled-foreground",
|
|
71
|
+
"aria-invalid:border-border-error",
|
|
72
|
+
error && "border-border-error",
|
|
73
|
+
],
|
|
74
|
+
hasAddons && "flex-1 min-h-0",
|
|
75
|
+
className,
|
|
76
|
+
)}
|
|
77
|
+
required={required}
|
|
78
|
+
aria-invalid={error ? true : undefined}
|
|
79
|
+
aria-describedby={fieldId && (hint || error) ? `${fieldId}-description` : undefined}
|
|
80
|
+
{...props}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const wrappedInput = hasAddons ? (
|
|
85
|
+
<div
|
|
86
|
+
className={cn(
|
|
87
|
+
"flex items-center w-full overflow-hidden",
|
|
88
|
+
sizes.wrapper,
|
|
89
|
+
"bg-background border border-border rounded-md",
|
|
90
|
+
"transition-colors",
|
|
91
|
+
"has-[input:focus-visible]:border-ring has-[input:focus-visible]:border-2",
|
|
92
|
+
"has-[input:disabled]:bg-disabled has-[input:disabled]:text-disabled-foreground has-[input:disabled]:cursor-not-allowed",
|
|
93
|
+
error && "border-border-error",
|
|
94
|
+
)}
|
|
95
|
+
>
|
|
96
|
+
{leadingIcon && (
|
|
97
|
+
<span className={cn("flex items-center text-subtle-foreground [&_svg]:shrink-0", sizes.leadingIcon)}>
|
|
98
|
+
{leadingIcon}
|
|
99
|
+
</span>
|
|
100
|
+
)}
|
|
101
|
+
{leadingText && (
|
|
102
|
+
<span
|
|
103
|
+
className={cn(
|
|
104
|
+
"flex items-center text-subtle-foreground whitespace-nowrap border-r border-border self-stretch",
|
|
105
|
+
sizes.leadingText,
|
|
106
|
+
)}
|
|
107
|
+
>
|
|
108
|
+
{leadingText}
|
|
109
|
+
</span>
|
|
110
|
+
)}
|
|
111
|
+
<div className={cn("flex flex-1 items-center min-w-0", sizes.inner)}>{inputElement}</div>
|
|
112
|
+
</div>
|
|
113
|
+
) : (
|
|
114
|
+
inputElement
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<div className="flex flex-col gap-1.5">
|
|
119
|
+
{label && (
|
|
120
|
+
<div className="flex items-center gap-0.5">
|
|
121
|
+
<Label htmlFor={fieldId} className="type-text-sm font-medium text-muted-foreground">
|
|
122
|
+
{label}
|
|
123
|
+
</Label>
|
|
124
|
+
{required && (
|
|
125
|
+
<span aria-hidden className="type-text-sm font-medium text-required">
|
|
126
|
+
*
|
|
127
|
+
</span>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
131
|
+
{wrappedInput}
|
|
132
|
+
{(hint || error) && (
|
|
133
|
+
<p
|
|
134
|
+
id={fieldId ? `${fieldId}-description` : undefined}
|
|
135
|
+
className={cn("type-text-sm", error ? "text-destructive" : "text-subtle-foreground")}
|
|
136
|
+
>
|
|
137
|
+
{error ?? hint}
|
|
138
|
+
</p>
|
|
139
|
+
)}
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export type { InputProps };
|
|
145
|
+
export { Input };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@alpic-ai/ui/lib/cn";
|
|
4
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
5
|
+
import type * as React from "react";
|
|
6
|
+
|
|
7
|
+
function Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
|
8
|
+
return (
|
|
9
|
+
<LabelPrimitive.Root
|
|
10
|
+
data-slot="label"
|
|
11
|
+
className={cn(
|
|
12
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
13
|
+
className,
|
|
14
|
+
)}
|
|
15
|
+
{...props}
|
|
16
|
+
/>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { Label };
|