@vvedantb/ui 0.0.0-stage → 0.1.1

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 (75) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +124 -4
  3. package/src/ai-elements/artifact.tsx +99 -0
  4. package/src/ai-elements/attachments.tsx +366 -0
  5. package/src/ai-elements/chain-of-thought.tsx +212 -0
  6. package/src/ai-elements/code-block.tsx +478 -0
  7. package/src/ai-elements/conversation.tsx +157 -0
  8. package/src/ai-elements/image.tsx +25 -0
  9. package/src/ai-elements/inline-citation.tsx +252 -0
  10. package/src/ai-elements/message.tsx +269 -0
  11. package/src/ai-elements/model-selector.tsx +211 -0
  12. package/src/ai-elements/prompt-input.tsx +1018 -0
  13. package/src/ai-elements/queue.tsx +251 -0
  14. package/src/ai-elements/reasoning.tsx +178 -0
  15. package/src/ai-elements/shimmer.tsx +56 -0
  16. package/src/ai-elements/sources.tsx +77 -0
  17. package/src/ai-elements/speech-input.tsx +290 -0
  18. package/src/ai-elements/suggestion.tsx +191 -0
  19. package/src/css.d.ts +2 -0
  20. package/src/index.ts +96 -0
  21. package/src/motion/presets.ts +35 -0
  22. package/src/motion/presets.vibot.ts +105 -0
  23. package/src/ui/_menu-classes.ts +30 -0
  24. package/src/ui/_menu-classes.vibot.ts +33 -0
  25. package/src/ui/accordion.tsx +81 -0
  26. package/src/ui/accordion.verve.tsx +56 -0
  27. package/src/ui/badge.tsx +34 -0
  28. package/src/ui/breadcrumb.tsx +83 -0
  29. package/src/ui/button-group.tsx +84 -0
  30. package/src/ui/button.tsx +57 -0
  31. package/src/ui/card.tsx +27 -0
  32. package/src/ui/carousel.tsx +211 -0
  33. package/src/ui/checkbox.tsx +27 -0
  34. package/src/ui/clear-input.tsx +170 -0
  35. package/src/ui/clear-input.vibot.tsx +76 -0
  36. package/src/ui/clearInputDissolve.ts +214 -0
  37. package/src/ui/collapsible.tsx +7 -0
  38. package/src/ui/command.tsx +148 -0
  39. package/src/ui/context-menu.tsx +173 -0
  40. package/src/ui/dialog.tsx +149 -0
  41. package/src/ui/dropdown-menu.tsx +178 -0
  42. package/src/ui/hover-card.tsx +31 -0
  43. package/src/ui/input-group.tsx +162 -0
  44. package/src/ui/input-group.verve.tsx +150 -0
  45. package/src/ui/input.tsx +21 -0
  46. package/src/ui/kbd.tsx +18 -0
  47. package/src/ui/label.tsx +20 -0
  48. package/src/ui/labeled-switch-row.tsx +53 -0
  49. package/src/ui/modalTransition.ts +85 -0
  50. package/src/ui/pagination.tsx +47 -0
  51. package/src/ui/popover.tsx +33 -0
  52. package/src/ui/progress.tsx +25 -0
  53. package/src/ui/radix-select.tsx +202 -0
  54. package/src/ui/scroll-area.tsx +55 -0
  55. package/src/ui/scroll-area.verve.tsx +43 -0
  56. package/src/ui/search-input.tsx +29 -0
  57. package/src/ui/select.tsx +163 -0
  58. package/src/ui/separator.tsx +28 -0
  59. package/src/ui/sheet.tsx +87 -0
  60. package/src/ui/skeleton.tsx +18 -0
  61. package/src/ui/sonner.css +16 -0
  62. package/src/ui/sonner.tsx +47 -0
  63. package/src/ui/spinner.tsx +27 -0
  64. package/src/ui/surface-classes.ts +55 -0
  65. package/src/ui/switch.tsx +26 -0
  66. package/src/ui/table.tsx +123 -0
  67. package/src/ui/tabs.tsx +140 -0
  68. package/src/ui/tabsSliding.ts +56 -0
  69. package/src/ui/textarea.tsx +21 -0
  70. package/src/ui/time-picker.tsx +170 -0
  71. package/src/ui/tooltip.tsx +25 -0
  72. package/src/ui/visually-hidden.tsx +3 -0
  73. package/src/utils/cn.ts +16 -0
  74. package/src/utils/ref.ts +10 -0
  75. package/README.md +0 -3
@@ -0,0 +1,105 @@
1
+ "use client";
2
+
3
+ import type { Transition, Variants } from "motion/react";
4
+
5
+ export const motionEase: [number, number, number, number] = [0.22, 1, 0.36, 1];
6
+
7
+ export const motionDuration = {
8
+ fast: 0.22,
9
+ base: 0.3,
10
+ slow: 0.36,
11
+ } as const;
12
+
13
+ export const motionTiming = {
14
+ route: 0.3,
15
+ sidebar: 0.28,
16
+ stagger: 0.03,
17
+ } as const;
18
+
19
+ export const motionDistance = {
20
+ routeX: 12,
21
+ pageY: 8,
22
+ } as const;
23
+
24
+ /** Critically damped default — Apple damping 1.0 / response ~0.35s */
25
+ export const defaultTransition: Transition = {
26
+ type: "spring",
27
+ bounce: 0,
28
+ duration: 0.35,
29
+ };
30
+
31
+ export const fastTransition: Transition = {
32
+ type: "spring",
33
+ bounce: 0,
34
+ duration: 0.25,
35
+ };
36
+
37
+ export const fade: Variants = {
38
+ hidden: { opacity: 0 },
39
+ show: { opacity: 1, transition: defaultTransition },
40
+ exit: {
41
+ opacity: 0,
42
+ transition: fastTransition,
43
+ },
44
+ };
45
+
46
+ export const fadeUp: Variants = {
47
+ hidden: { opacity: 0, y: motionDistance.pageY },
48
+ show: { opacity: 1, y: 0, transition: defaultTransition },
49
+ exit: {
50
+ opacity: 0,
51
+ y: 6,
52
+ transition: fastTransition,
53
+ },
54
+ };
55
+
56
+ export const routeSlideFade: Variants = {
57
+ hidden: { opacity: 0, x: -motionDistance.routeX },
58
+ show: {
59
+ opacity: 1,
60
+ x: 0,
61
+ transition: { type: "spring", bounce: 0, duration: motionTiming.route },
62
+ },
63
+ exit: {
64
+ opacity: 0,
65
+ x: -6,
66
+ transition: fastTransition,
67
+ },
68
+ };
69
+
70
+ export const scaleIn: Variants = {
71
+ hidden: { opacity: 0, scale: 0.98 },
72
+ show: { opacity: 1, scale: 1, transition: defaultTransition },
73
+ exit: {
74
+ opacity: 0,
75
+ scale: 0.98,
76
+ transition: fastTransition,
77
+ },
78
+ };
79
+
80
+ export function staggerContainer(
81
+ stagger = motionTiming.stagger,
82
+ delayChildren = 0,
83
+ ): Variants {
84
+ return {
85
+ hidden: { opacity: 0 },
86
+ show: {
87
+ opacity: 1,
88
+ transition: {
89
+ when: "beforeChildren",
90
+ delayChildren,
91
+ staggerChildren: stagger,
92
+ },
93
+ },
94
+ exit: {
95
+ opacity: 0,
96
+ transition: {
97
+ when: "afterChildren",
98
+ staggerChildren: Math.max(0.01, stagger / 2),
99
+ staggerDirection: -1,
100
+ },
101
+ },
102
+ };
103
+ }
104
+
105
+ export const staggerItem: Variants = fadeUp;
@@ -0,0 +1,30 @@
1
+ // eva-aligned floating overlay: tone + smooth-shadow-ring, never border+shadow
2
+ export const floatingSurfaceClass =
3
+ "rounded-lg bg-overlay text-overlay-foreground smooth-shadow-ring-lg max-sm:max-w-[calc(100vw-2rem)]";
4
+
5
+ export const floatingSurfaceAnimateClass = [
6
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
7
+ ].join(" ");
8
+
9
+ // shared floating menu surfaces, matches eva popover + smooth-shadow-ring-lg
10
+ export const menuContentClass = [
11
+ "z-50 min-w-[12rem] overflow-hidden p-1.5 outline-none max-sm:max-h-[85dvh] max-sm:overflow-y-auto",
12
+ floatingSurfaceClass,
13
+ floatingSurfaceAnimateClass,
14
+ ].join(" ");
15
+
16
+ export const menuSubTriggerClass =
17
+ "flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 max-sm:min-h-10 text-sm text-foreground outline-none transition-[background-color,color] focus:bg-default focus:text-foreground data-[highlighted]:bg-default data-[highlighted]:text-foreground data-[state=open]:bg-default data-[disabled]:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0";
18
+
19
+ export const menuItemClass =
20
+ "relative flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 max-sm:min-h-10 text-sm outline-none transition-[background-color,color] focus:bg-default focus:text-foreground data-[highlighted]:bg-default data-[highlighted]:text-foreground data-[disabled]:pointer-events-none data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0 [&_svg]:pointer-events-none";
21
+
22
+ export const menuCheckboxRadioItemClass =
23
+ "relative flex cursor-pointer select-none items-center rounded-md py-2 max-sm:min-h-10 pl-8 pr-2.5 text-sm outline-none transition-[background-color,color] focus:bg-default focus:text-foreground data-[highlighted]:bg-default data-[highlighted]:text-foreground data-[disabled]:pointer-events-none data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50";
24
+
25
+ export const menuLabelClass =
26
+ "px-2.5 py-1.5 text-xs font-semibold uppercase tracking-widest text-muted";
27
+
28
+ export const menuSeparatorClass = "-mx-1.5 my-1 h-px bg-separator";
29
+
30
+ export const menuShortcutClass = "ml-auto text-xs tracking-wide text-muted/90";
@@ -0,0 +1,33 @@
1
+ /** Eva-aligned floating overlay shell — tone + smooth-shadow-ring, no hairline. */
2
+ export const floatingSurfaceClass =
3
+ "rounded-lg bg-overlay text-overlay-foreground smooth-shadow-ring-lg";
4
+
5
+ export const floatingTooltipSurfaceClass =
6
+ "rounded-lg bg-overlay text-overlay-foreground smooth-shadow-ring-md";
7
+
8
+ export const floatingSurfaceAnimateClass = [
9
+ "origin-(--radix-popper-transform-origin) data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=open]:duration-200 data-[state=closed]:duration-150 data-[state=open]:ease-[cubic-bezier(0.32,0.72,0,1)] data-[state=closed]:ease-[cubic-bezier(0.32,0.72,0,1)]",
10
+ ].join(" ");
11
+
12
+ /** Shared floating menu surfaces — matches Eva's popover + smooth-shadow-ring-lg. */
13
+ export const menuContentClass = [
14
+ "z-50 min-w-48 overflow-hidden p-1.5 outline-hidden",
15
+ floatingSurfaceClass,
16
+ floatingSurfaceAnimateClass,
17
+ ].join(" ");
18
+
19
+ export const menuSubTriggerClass =
20
+ "flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-sm text-foreground outline-hidden transition-[background-color,color,transform] duration-150 ease-out active:scale-[0.99] focus:bg-default focus:text-foreground data-highlighted:bg-default data-highlighted:text-foreground data-[state=open]:bg-default data-disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0";
21
+
22
+ export const menuItemClass =
23
+ "relative flex cursor-pointer select-none items-center gap-2 rounded-md px-2.5 py-2 text-sm outline-hidden transition-[background-color,color,transform] duration-150 ease-out active:scale-[0.99] focus:bg-default focus:text-foreground data-highlighted:bg-default data-highlighted:text-foreground data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-disabled:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0 [&_svg]:pointer-events-none";
24
+
25
+ export const menuCheckboxRadioItemClass =
26
+ "relative flex cursor-pointer select-none items-center rounded-md py-2 pl-8 pr-2.5 text-sm outline-hidden transition-[background-color,color,transform] duration-150 ease-out active:scale-[0.99] focus:bg-default focus:text-foreground data-highlighted:bg-default data-highlighted:text-foreground data-disabled:pointer-events-none data-disabled:cursor-not-allowed data-disabled:opacity-50";
27
+
28
+ export const menuLabelClass =
29
+ "px-2.5 py-1.5 text-xs font-semibold uppercase tracking-widest text-muted";
30
+
31
+ export const menuSeparatorClass = "-mx-1.5 my-1 h-px bg-separator";
32
+
33
+ export const menuShortcutClass = "ml-auto text-xs tracking-wide text-muted/90";
@@ -0,0 +1,81 @@
1
+ import * as React from "react";
2
+ import { Accordion as AccordionPrimitive } from "radix-ui";
3
+ import { cn } from "../utils/cn";
4
+
5
+ function ChevronDown({ className }: { className?: string }) {
6
+ return (
7
+ <svg
8
+ xmlns="http://www.w3.org/2000/svg"
9
+ width="24"
10
+ height="24"
11
+ viewBox="0 0 24 24"
12
+ fill="none"
13
+ stroke="currentColor"
14
+ strokeWidth="2"
15
+ strokeLinecap="round"
16
+ strokeLinejoin="round"
17
+ className={className}
18
+ >
19
+ <path d="m6 9 6 6 6-6" />
20
+ </svg>
21
+ );
22
+ }
23
+
24
+ function Accordion({
25
+ ...props
26
+ }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
27
+ return <AccordionPrimitive.Root data-slot="accordion" {...props} />;
28
+ }
29
+
30
+ function AccordionItem({
31
+ className,
32
+ ...props
33
+ }: React.ComponentProps<typeof AccordionPrimitive.Item>) {
34
+ return (
35
+ <AccordionPrimitive.Item
36
+ data-slot="accordion-item"
37
+ className={cn("not-first:mt-6", className)}
38
+ {...props}
39
+ />
40
+ );
41
+ }
42
+
43
+ function AccordionTrigger({
44
+ className,
45
+ children,
46
+ ...props
47
+ }: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
48
+ return (
49
+ <AccordionPrimitive.Header className="flex">
50
+ <AccordionPrimitive.Trigger
51
+ data-slot="accordion-trigger"
52
+ className={cn(
53
+ "flex flex-1 items-center justify-between py-4 font-medium transition-[color,transform] duration-150 ease-out active:scale-[0.99] hover:underline [&[data-state=open]>svg]:rotate-180",
54
+ className,
55
+ )}
56
+ {...props}
57
+ >
58
+ {children}
59
+ <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-150 ease-out" />
60
+ </AccordionPrimitive.Trigger>
61
+ </AccordionPrimitive.Header>
62
+ );
63
+ }
64
+
65
+ function AccordionContent({
66
+ className,
67
+ children,
68
+ ...props
69
+ }: React.ComponentProps<typeof AccordionPrimitive.Content>) {
70
+ return (
71
+ <AccordionPrimitive.Content
72
+ data-slot="accordion-content"
73
+ className="overflow-hidden text-sm transition-[height] data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
74
+ {...props}
75
+ >
76
+ <div className={cn("pb-4 pt-0", className)}>{children}</div>
77
+ </AccordionPrimitive.Content>
78
+ );
79
+ }
80
+
81
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
@@ -0,0 +1,56 @@
1
+ import * as React from "react";
2
+ import * as AccordionPrimitive from "@radix-ui/react-accordion";
3
+ import { IconChevronDown } from "@tabler/icons-react";
4
+ import { cn } from "../utils/cn";
5
+
6
+ const Accordion = AccordionPrimitive.Root;
7
+
8
+ const AccordionItem = React.forwardRef<
9
+ React.ComponentRef<typeof AccordionPrimitive.Item>,
10
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
11
+ >(({ className, ...props }, ref) => (
12
+ <AccordionPrimitive.Item
13
+ ref={ref}
14
+ className={cn("rounded-lg", className)}
15
+ {...props}
16
+ />
17
+ ));
18
+ AccordionItem.displayName = "AccordionItem";
19
+
20
+ const AccordionTrigger = React.forwardRef<
21
+ React.ComponentRef<typeof AccordionPrimitive.Trigger>,
22
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
23
+ >(({ className, children, ...props }, ref) => (
24
+ <AccordionPrimitive.Header className="flex">
25
+ <AccordionPrimitive.Trigger
26
+ ref={ref}
27
+ className={cn(
28
+ "flex flex-1 items-center justify-between rounded-lg py-4 text-sm font-medium transition-[background-color,color,transform] duration-100 ease-out active:scale-[0.99] hover:bg-surface-tertiary/50 [&[data-state=open]>svg]:rotate-180",
29
+ className,
30
+ )}
31
+ {...props}
32
+ >
33
+ {children}
34
+ <IconChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
35
+ </AccordionPrimitive.Trigger>
36
+ </AccordionPrimitive.Header>
37
+ ));
38
+ AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
39
+
40
+ const AccordionContent = React.forwardRef<
41
+ React.ComponentRef<typeof AccordionPrimitive.Content>,
42
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
43
+ >(({ className, children, ...props }, ref) => (
44
+ <AccordionPrimitive.Content
45
+ ref={ref}
46
+ className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
47
+ {...props}
48
+ >
49
+ <div className={cn("pb-4 pt-0 text-muted-foreground", className)}>
50
+ {children}
51
+ </div>
52
+ </AccordionPrimitive.Content>
53
+ ));
54
+ AccordionContent.displayName = AccordionPrimitive.Content.displayName;
55
+
56
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
@@ -0,0 +1,34 @@
1
+ import { cva, type VariantProps } from "class-variance-authority";
2
+ import { cn } from "../utils/cn";
3
+
4
+ const badgeVariants = cva(
5
+ "inline-flex items-center gap-1 rounded-full border border-transparent px-2.5 py-1 text-xs font-medium tracking-normal transition-colors focus:outline-none focus:ring-2 focus:ring-focus-ring focus:ring-offset-2",
6
+ {
7
+ variants: {
8
+ variant: {
9
+ default: "border-border bg-default text-foreground",
10
+ secondary: "border-separator bg-default text-default-foreground",
11
+ destructive: "border-danger/25 bg-danger/12 text-danger",
12
+ outline: "border-separator bg-transparent text-muted",
13
+ success: "border-success/25 bg-success/12 text-success",
14
+ warning: "border-warning/25 bg-warning/12 text-warning",
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ variant: "default",
19
+ },
20
+ },
21
+ );
22
+
23
+ export interface BadgeProps
24
+ extends
25
+ React.HTMLAttributes<HTMLDivElement>,
26
+ VariantProps<typeof badgeVariants> {}
27
+
28
+ function Badge({ className, variant, ...props }: BadgeProps) {
29
+ return (
30
+ <div className={cn(badgeVariants({ variant }), className)} {...props} />
31
+ );
32
+ }
33
+
34
+ export { Badge };
@@ -0,0 +1,83 @@
1
+ import * as React from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cn } from "../utils/cn";
4
+
5
+ // breadcrumb, navigation trail for the current page location renders its children as a horizontal list with `/` separators automatically
6
+ // inserted between them designed to replace the page title + back button on
7
+ // detail pages: the parent route(s) act as the "up" affordance and the final
8
+ // segment identifies the current page example: <breadcrumb> <breadcrumblink aschild> <link to="/wiki">wiki</link> </breadcrumblink> <breadcrumbpage>overview</breadcrumbpage> </breadcrumb>
9
+ const Breadcrumb = React.forwardRef<
10
+ HTMLElement,
11
+ React.HTMLAttributes<HTMLElement>
12
+ >(({ className, children, ...props }, ref) => {
13
+ // Filter out falsy children (e.g. conditionally rendered segments) so the
14
+ // separator interleaving stays aligned with actual rendered items.
15
+ const items = React.Children.toArray(children).filter(Boolean);
16
+
17
+ return (
18
+ <nav
19
+ ref={ref}
20
+ aria-label="breadcrumb"
21
+ className={cn(
22
+ "flex min-w-0 items-center gap-1.5 text-xl font-semibold leading-tight tracking-tight",
23
+ className,
24
+ )}
25
+ {...props}
26
+ >
27
+ {items.map((item, index) => (
28
+ <React.Fragment key={index}>
29
+ {item}
30
+ {index < items.length - 1 && <BreadcrumbSeparator />}
31
+ </React.Fragment>
32
+ ))}
33
+ </nav>
34
+ );
35
+ });
36
+ Breadcrumb.displayName = "Breadcrumb";
37
+
38
+ // clickable breadcrumb segment (parent routes) muted + medium weight, one
39
+ // step quieter than the current page, shifts to foreground on hover pass
40
+ // `aschild` to render as your router's link component while keeping the
41
+ // styling, preserves type, safe routing
42
+ const BreadcrumbLink = React.forwardRef<
43
+ HTMLAnchorElement,
44
+ React.AnchorHTMLAttributes<HTMLAnchorElement> & { asChild?: boolean }
45
+ >(({ asChild = false, className, ...props }, ref) => {
46
+ const Comp = asChild ? Slot : "a";
47
+ return (
48
+ <Comp
49
+ ref={ref}
50
+ className={cn(
51
+ "min-w-0 truncate font-medium text-muted transition-colors hover:text-foreground",
52
+ className,
53
+ )}
54
+ {...props}
55
+ />
56
+ );
57
+ });
58
+ BreadcrumbLink.displayName = "BreadcrumbLink";
59
+
60
+ // current page segment (last in the trail) inherits the page-title weight
61
+ // (semibold) at foreground colour, and not clickable, signals "you are here"
62
+ const BreadcrumbPage = React.forwardRef<
63
+ HTMLSpanElement,
64
+ React.HTMLAttributes<HTMLSpanElement>
65
+ >(({ className, ...props }, ref) => (
66
+ <span
67
+ ref={ref}
68
+ aria-current="page"
69
+ className={cn("min-w-0 truncate text-foreground", className)}
70
+ {...props}
71
+ />
72
+ ));
73
+ BreadcrumbPage.displayName = "BreadcrumbPage";
74
+
75
+ // separator rendered automatically between breadcrumb children
76
+ const BreadcrumbSeparator = () => (
77
+ <span aria-hidden className="flex-shrink-0 text-muted/40">
78
+ /
79
+ </span>
80
+ );
81
+ BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
82
+
83
+ export { Breadcrumb, BreadcrumbLink, BreadcrumbPage };
@@ -0,0 +1,84 @@
1
+ import type * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { Slot } from "radix-ui";
4
+
5
+ import { cn } from "../utils/cn";
6
+ import { Separator } from "./separator";
7
+
8
+ const buttonGroupVariants = cva(
9
+ "flex w-fit items-stretch focus-visible:*:z-10 focus-visible:*:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 [&>[data-slot=select-trigger]:last-of-type]:has-[select[aria-hidden=true]:last-child]:rounded-r-md has-[>[data-slot=button-group]]:gap-2",
10
+ {
11
+ variants: {
12
+ orientation: {
13
+ horizontal:
14
+ "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none",
15
+ vertical:
16
+ "flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none",
17
+ },
18
+ },
19
+ defaultVariants: {
20
+ orientation: "horizontal",
21
+ },
22
+ },
23
+ );
24
+
25
+ function ButtonGroup({
26
+ className,
27
+ orientation,
28
+ ...props
29
+ }: React.ComponentProps<"div"> & VariantProps<typeof buttonGroupVariants>) {
30
+ return (
31
+ <div
32
+ role="group"
33
+ data-slot="button-group"
34
+ data-orientation={orientation}
35
+ className={cn(buttonGroupVariants({ orientation }), className)}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function ButtonGroupText({
42
+ className,
43
+ asChild = false,
44
+ ...props
45
+ }: React.ComponentProps<"div"> & {
46
+ asChild?: boolean;
47
+ }) {
48
+ const Comp = asChild ? Slot.Root : "div";
49
+
50
+ return (
51
+ <Comp
52
+ className={cn(
53
+ "bg-surface-secondary flex items-center gap-2 rounded-md px-4 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
54
+ className,
55
+ )}
56
+ {...props}
57
+ />
58
+ );
59
+ }
60
+
61
+ function ButtonGroupSeparator({
62
+ className,
63
+ orientation = "vertical",
64
+ ...props
65
+ }: React.ComponentProps<typeof Separator>) {
66
+ return (
67
+ <Separator
68
+ data-slot="button-group-separator"
69
+ orientation={orientation}
70
+ className={cn(
71
+ "bg-separator relative m-0! self-stretch data-[orientation=vertical]:h-auto",
72
+ className,
73
+ )}
74
+ {...props}
75
+ />
76
+ );
77
+ }
78
+
79
+ export {
80
+ ButtonGroup,
81
+ ButtonGroupSeparator,
82
+ ButtonGroupText,
83
+ buttonGroupVariants,
84
+ };
@@ -0,0 +1,57 @@
1
+ import * as React from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import { cn } from "../utils/cn";
5
+
6
+ const buttonVariants = cva(
7
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-medium tracking-normal transition-[transform,background-color,box-shadow,opacity,border-color] duration-200 ease-smooth focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50 active:scale-[0.96] [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default:
12
+ "bg-accent text-accent-foreground hover:-translate-y-0.5 hover:opacity-90",
13
+ destructive: "bg-danger text-danger-foreground hover:bg-danger/90",
14
+ outline:
15
+ "border border-border bg-transparent text-foreground hover:bg-default",
16
+ secondary: "bg-default text-default-foreground hover:bg-default/78",
17
+ ghost: "text-muted hover:bg-surface-tertiary hover:text-foreground",
18
+ link: "text-accent underline-offset-4 hover:underline active:scale-100",
19
+ },
20
+ size: {
21
+ default: "h-10 px-4 py-2",
22
+ sm: "h-8 rounded-lg px-3 text-xs max-sm:hit-target",
23
+ lg: "h-11 px-6",
24
+ icon: "h-10 w-10",
25
+ "icon-sm": "h-8 w-8 rounded-lg max-sm:hit-target",
26
+ "icon-xs": "h-7 w-7 rounded-md hit-target",
27
+ },
28
+ },
29
+ defaultVariants: {
30
+ variant: "default",
31
+ size: "default",
32
+ },
33
+ },
34
+ );
35
+
36
+ export interface ButtonProps
37
+ extends
38
+ React.ButtonHTMLAttributes<HTMLButtonElement>,
39
+ VariantProps<typeof buttonVariants> {
40
+ asChild?: boolean;
41
+ }
42
+
43
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
44
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
45
+ const Comp = asChild ? Slot : "button";
46
+ return (
47
+ <Comp
48
+ className={cn(buttonVariants({ variant, size, className }))}
49
+ ref={ref}
50
+ {...props}
51
+ />
52
+ );
53
+ },
54
+ );
55
+ Button.displayName = "Button";
56
+
57
+ export { Button };
@@ -0,0 +1,27 @@
1
+ import * as React from "react";
2
+ import { cn } from "../utils/cn";
3
+
4
+ const Card = React.forwardRef<
5
+ HTMLDivElement,
6
+ React.HTMLAttributes<HTMLDivElement>
7
+ >(({ className, ...props }, ref) => (
8
+ <div
9
+ ref={ref}
10
+ className={cn(
11
+ "rounded-lg bg-surface-card text-surface-foreground",
12
+ className,
13
+ )}
14
+ {...props}
15
+ />
16
+ ));
17
+ Card.displayName = "Card";
18
+
19
+ const CardContent = React.forwardRef<
20
+ HTMLDivElement,
21
+ React.HTMLAttributes<HTMLDivElement>
22
+ >(({ className, ...props }, ref) => (
23
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
24
+ ));
25
+ CardContent.displayName = "CardContent";
26
+
27
+ export { Card, CardContent };