@jinaan/uikit 0.0.0-stage → 0.1.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.
Files changed (89) hide show
  1. package/README.md +63 -2
  2. package/dist/agent-prompt.d.ts +16 -0
  3. package/dist/agent-prompt.d.ts.map +1 -0
  4. package/dist/avatar.d.ts +5 -0
  5. package/dist/avatar.d.ts.map +1 -0
  6. package/dist/badge.d.ts +11 -0
  7. package/dist/badge.d.ts.map +1 -0
  8. package/dist/button.d.ts +22 -0
  9. package/dist/button.d.ts.map +1 -0
  10. package/dist/callout.d.ts +12 -0
  11. package/dist/callout.d.ts.map +1 -0
  12. package/dist/card.d.ts +8 -0
  13. package/dist/card.d.ts.map +1 -0
  14. package/dist/checkbox.d.ts +3 -0
  15. package/dist/checkbox.d.ts.map +1 -0
  16. package/dist/choice-dialog.d.ts +34 -0
  17. package/dist/choice-dialog.d.ts.map +1 -0
  18. package/dist/code-block.d.ts +11 -0
  19. package/dist/code-block.d.ts.map +1 -0
  20. package/dist/data-table.d.ts +84 -0
  21. package/dist/data-table.d.ts.map +1 -0
  22. package/dist/dialog.d.ts +19 -0
  23. package/dist/dialog.d.ts.map +1 -0
  24. package/dist/dropdown-menu.d.ts +13 -0
  25. package/dist/dropdown-menu.d.ts.map +1 -0
  26. package/dist/empty-state.d.ts +11 -0
  27. package/dist/empty-state.d.ts.map +1 -0
  28. package/dist/index.d.ts +27 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +1522 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/input.d.ts +19 -0
  33. package/dist/input.d.ts.map +1 -0
  34. package/dist/kbd.d.ts +4 -0
  35. package/dist/kbd.d.ts.map +1 -0
  36. package/dist/label.d.ts +3 -0
  37. package/dist/label.d.ts.map +1 -0
  38. package/dist/radio-group.d.ts +35 -0
  39. package/dist/radio-group.d.ts.map +1 -0
  40. package/dist/segmented.d.ts +22 -0
  41. package/dist/segmented.d.ts.map +1 -0
  42. package/dist/select.d.ts +9 -0
  43. package/dist/select.d.ts.map +1 -0
  44. package/dist/separator.d.ts +3 -0
  45. package/dist/separator.d.ts.map +1 -0
  46. package/dist/skeleton.d.ts +3 -0
  47. package/dist/skeleton.d.ts.map +1 -0
  48. package/dist/spinner.d.ts +4 -0
  49. package/dist/spinner.d.ts.map +1 -0
  50. package/dist/table-empty-state.d.ts +15 -0
  51. package/dist/table-empty-state.d.ts.map +1 -0
  52. package/dist/table-surface.d.ts +38 -0
  53. package/dist/table-surface.d.ts.map +1 -0
  54. package/dist/table.d.ts +15 -0
  55. package/dist/table.d.ts.map +1 -0
  56. package/dist/tooltip.d.ts +6 -0
  57. package/dist/tooltip.d.ts.map +1 -0
  58. package/dist/utils.d.ts +4 -0
  59. package/dist/utils.d.ts.map +1 -0
  60. package/package.json +73 -4
  61. package/src/agent-prompt.tsx +60 -0
  62. package/src/avatar.tsx +46 -0
  63. package/src/badge.tsx +54 -0
  64. package/src/button.tsx +126 -0
  65. package/src/callout.tsx +74 -0
  66. package/src/card.tsx +61 -0
  67. package/src/checkbox.tsx +32 -0
  68. package/src/choice-dialog.tsx +110 -0
  69. package/src/code-block.tsx +53 -0
  70. package/src/data-table.tsx +589 -0
  71. package/src/dialog.tsx +123 -0
  72. package/src/dropdown-menu.tsx +103 -0
  73. package/src/empty-state.tsx +35 -0
  74. package/src/index.ts +26 -0
  75. package/src/input.tsx +171 -0
  76. package/src/kbd.tsx +16 -0
  77. package/src/label.tsx +19 -0
  78. package/src/radio-group.tsx +134 -0
  79. package/src/segmented.tsx +76 -0
  80. package/src/select.tsx +91 -0
  81. package/src/separator.tsx +22 -0
  82. package/src/skeleton.tsx +12 -0
  83. package/src/spinner.tsx +13 -0
  84. package/src/table-empty-state.tsx +51 -0
  85. package/src/table-surface.tsx +122 -0
  86. package/src/table.tsx +91 -0
  87. package/src/tooltip.tsx +32 -0
  88. package/src/utils.ts +7 -0
  89. package/styles.css +371 -0
@@ -0,0 +1,76 @@
1
+ import { useId, type ReactNode } from "react";
2
+
3
+ import { cn } from "./utils";
4
+
5
+ export interface SegmentedOption<T extends string> {
6
+ value: T;
7
+ label: ReactNode;
8
+ icon?: ReactNode;
9
+ }
10
+
11
+ interface SegmentedProps<T extends string> {
12
+ value: T;
13
+ onValueChange: (value: T) => void;
14
+ options: SegmentedOption<T>[];
15
+ size?: "sm" | "md";
16
+ className?: string;
17
+ "aria-label"?: string;
18
+ }
19
+
20
+ /**
21
+ * Single-choice control for a small set of options, styled after the Clerk
22
+ * dashboard: a recessed track with an elevated selected pill. Implemented as a
23
+ * `radiogroup` of native radios, so arrow-key roving and forms work natively.
24
+ */
25
+ export function Segmented<T extends string>({
26
+ value,
27
+ onValueChange,
28
+ options,
29
+ size = "sm",
30
+ className,
31
+ ...rest
32
+ }: SegmentedProps<T>) {
33
+ const name = useId();
34
+
35
+ return (
36
+ <div
37
+ role="radiogroup"
38
+ aria-label={rest["aria-label"]}
39
+ className={cn(
40
+ "inline-flex w-fit items-center rounded-lg border border-line bg-canvas",
41
+ size === "sm" ? "h-(--control-h-sm)" : "h-(--control-h-md)",
42
+ className,
43
+ )}
44
+ >
45
+ {options.map((option) => {
46
+ const active = option.value === value;
47
+ return (
48
+ <label
49
+ key={option.value}
50
+ className={cn(
51
+ "inline-flex h-full cursor-pointer items-center justify-center gap-1.5 rounded-md text-sm font-medium transition-colors",
52
+ "has-focus-visible:ring-2 has-focus-visible:ring-focus/40",
53
+ size === "sm"
54
+ ? "px-(--control-pad-x-sm) [&_svg]:size-(--control-icon-sm)"
55
+ : "px-(--control-pad-x-md) [&_svg]:size-(--control-icon-md)",
56
+ active
57
+ ? "btn-fill bg-(--button-secondary-bg) text-fg"
58
+ : "text-muted hover:text-fg",
59
+ )}
60
+ >
61
+ <input
62
+ type="radio"
63
+ name={name}
64
+ value={option.value}
65
+ checked={active}
66
+ onChange={() => onValueChange(option.value)}
67
+ className="sr-only"
68
+ />
69
+ {option.icon}
70
+ {option.label}
71
+ </label>
72
+ );
73
+ })}
74
+ </div>
75
+ );
76
+ }
package/src/select.tsx ADDED
@@ -0,0 +1,91 @@
1
+ import * as SelectPrimitive from "@radix-ui/react-select";
2
+ import { CaretDown, Check } from "@phosphor-icons/react";
3
+ import { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from "react";
4
+
5
+ import { cn } from "./utils";
6
+
7
+ export const Select = SelectPrimitive.Root;
8
+ export const SelectGroup = SelectPrimitive.Group;
9
+ export const SelectValue = SelectPrimitive.Value;
10
+
11
+ export const SelectTrigger = forwardRef<
12
+ ElementRef<typeof SelectPrimitive.Trigger>,
13
+ ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
14
+ >(({ className, children, ...props }, ref) => (
15
+ <SelectPrimitive.Trigger
16
+ ref={ref}
17
+ className={cn(
18
+ "flex h-9 w-full items-center justify-between gap-2 rounded-md border border-line bg-surface px-3 text-sm text-fg",
19
+ "focus:outline-none focus:border-primary focus:ring-2 focus:ring-focus/30 disabled:cursor-not-allowed disabled:opacity-50",
20
+ "[&>span]:truncate",
21
+ className,
22
+ )}
23
+ {...props}
24
+ >
25
+ {children}
26
+ <SelectPrimitive.Icon asChild>
27
+ <CaretDown className="size-3.5 text-subtle" />
28
+ </SelectPrimitive.Icon>
29
+ </SelectPrimitive.Trigger>
30
+ ));
31
+ SelectTrigger.displayName = "SelectTrigger";
32
+
33
+ export const SelectContent = forwardRef<
34
+ ElementRef<typeof SelectPrimitive.Content>,
35
+ ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
36
+ >(({ className, children, position = "popper", ...props }, ref) => (
37
+ <SelectPrimitive.Portal>
38
+ <SelectPrimitive.Content
39
+ ref={ref}
40
+ position={position}
41
+ className={cn(
42
+ "z-50 min-w-[8rem] overflow-hidden rounded-lg border border-line bg-surface p-1 shadow-md",
43
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
44
+ "data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0",
45
+ "data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95",
46
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
47
+ "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
48
+ "duration-150 ease-out",
49
+ className,
50
+ )}
51
+ {...props}
52
+ >
53
+ <SelectPrimitive.Viewport className="p-0">{children}</SelectPrimitive.Viewport>
54
+ </SelectPrimitive.Content>
55
+ </SelectPrimitive.Portal>
56
+ ));
57
+ SelectContent.displayName = "SelectContent";
58
+
59
+ export const SelectItem = forwardRef<
60
+ ElementRef<typeof SelectPrimitive.Item>,
61
+ ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
62
+ >(({ className, children, ...props }, ref) => (
63
+ <SelectPrimitive.Item
64
+ ref={ref}
65
+ className={cn(
66
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm text-fg outline-none focus:bg-elevated data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
67
+ className,
68
+ )}
69
+ {...props}
70
+ >
71
+ <span className="absolute left-2 flex size-4 items-center justify-center">
72
+ <SelectPrimitive.ItemIndicator>
73
+ <Check className="size-3.5" />
74
+ </SelectPrimitive.ItemIndicator>
75
+ </span>
76
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
77
+ </SelectPrimitive.Item>
78
+ ));
79
+ SelectItem.displayName = "SelectItem";
80
+
81
+ export const SelectLabel = forwardRef<
82
+ ElementRef<typeof SelectPrimitive.Label>,
83
+ ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
84
+ >(({ className, ...props }, ref) => (
85
+ <SelectPrimitive.Label
86
+ ref={ref}
87
+ className={cn("px-2 py-1.5 text-xs font-medium text-subtle", className)}
88
+ {...props}
89
+ />
90
+ ));
91
+ SelectLabel.displayName = "SelectLabel";
@@ -0,0 +1,22 @@
1
+ import * as SeparatorPrimitive from "@radix-ui/react-separator";
2
+ import { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from "react";
3
+
4
+ import { cn } from "./utils";
5
+
6
+ export const Separator = forwardRef<
7
+ ElementRef<typeof SeparatorPrimitive.Root>,
8
+ ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
9
+ >(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
10
+ <SeparatorPrimitive.Root
11
+ ref={ref}
12
+ decorative={decorative}
13
+ orientation={orientation}
14
+ className={cn(
15
+ "shrink-0 bg-line",
16
+ orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
17
+ className,
18
+ )}
19
+ {...props}
20
+ />
21
+ ));
22
+ Separator.displayName = "Separator";
@@ -0,0 +1,12 @@
1
+ import type { HTMLAttributes } from "react";
2
+
3
+ import { cn } from "./utils";
4
+
5
+ export function Skeleton({ className, ...props }: HTMLAttributes<HTMLDivElement>) {
6
+ return (
7
+ <div
8
+ className={cn("animate-pulse rounded-md bg-elevated", className)}
9
+ {...props}
10
+ />
11
+ );
12
+ }
@@ -0,0 +1,13 @@
1
+ import { CircleNotch } from "@phosphor-icons/react";
2
+
3
+ import { cn } from "./utils";
4
+
5
+ export function Spinner({ className }: { className?: string }) {
6
+ return (
7
+ <CircleNotch
8
+ className={cn("size-4 animate-spin text-muted", className)}
9
+ weight="bold"
10
+ aria-hidden
11
+ />
12
+ );
13
+ }
@@ -0,0 +1,51 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ import { cn } from "./utils";
4
+
5
+ interface TableEmptyStateProps {
6
+ icon?: ReactNode;
7
+ title: string;
8
+ description?: string;
9
+ action?: ReactNode;
10
+ className?: string;
11
+ }
12
+
13
+ /**
14
+ * Centered, contextual empty state for a table panel: an 80px icon chip, a
15
+ * large heading, supporting text, and an optional accent action.
16
+ */
17
+ export function TableEmptyState({
18
+ icon,
19
+ title,
20
+ description,
21
+ action,
22
+ className,
23
+ }: TableEmptyStateProps) {
24
+ return (
25
+ <div
26
+ className={cn(
27
+ "flex min-h-[22rem] flex-col items-center justify-center gap-5 px-6 py-16 text-center",
28
+ className,
29
+ )}
30
+ >
31
+ {icon ? (
32
+ <div className="flex size-[var(--table-empty-icon)] items-center justify-center rounded-2xl border border-table-empty-icon-border bg-table-empty-icon text-table-muted [&_svg]:size-8">
33
+ {icon}
34
+ </div>
35
+ ) : null}
36
+ <div className="space-y-2">
37
+ <p className="text-[1.75rem] font-semibold leading-tight text-table-header-active">
38
+ {title}
39
+ </p>
40
+ {description ? (
41
+ <p className="mx-auto max-w-md text-base leading-relaxed text-table-muted">
42
+ {description}
43
+ </p>
44
+ ) : null}
45
+ </div>
46
+ {action ? (
47
+ <div className="text-sm font-medium text-table-accent">{action}</div>
48
+ ) : null}
49
+ </div>
50
+ );
51
+ }
@@ -0,0 +1,122 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ import { Button } from "./button";
4
+ import { TableContainer, TablePanel } from "./table";
5
+ import { cn } from "./utils";
6
+
7
+ export type TableSurfaceTone = "default" | "muted" | "transparent";
8
+
9
+ export interface TableSurfaceAction {
10
+ label: string;
11
+ icon?: ReactNode;
12
+ variant?: "primary" | "outline" | "ghost" | "destructive";
13
+ onClick?: () => void;
14
+ href?: string;
15
+ disabled?: boolean;
16
+ }
17
+
18
+ interface TableSurfaceProps {
19
+ /** Header row content (a single row of column cells). */
20
+ header?: ReactNode;
21
+ /** Panel content: the body table, or an empty / loading / error state. */
22
+ children: ReactNode;
23
+ /** Optional action at the top-right of the header band. */
24
+ action?: ReactNode | TableSurfaceAction;
25
+ /** Surface preset; `className` / `headerClassName` / `panelClassName` win. */
26
+ tone?: TableSurfaceTone;
27
+ /** Outer shell overrides (background, radius, padding, …). */
28
+ className?: string;
29
+ /** Header band overrides (background, text color, …). */
30
+ headerClassName?: string;
31
+ /** Inner panel overrides (background, border color, …). */
32
+ panelClassName?: string;
33
+ /** Minimum panel height; keeps empty states from collapsing. */
34
+ minHeight?: string;
35
+ }
36
+
37
+ const SHELL_TONE: Record<TableSurfaceTone, string> = {
38
+ default: "bg-table-container",
39
+ muted: "bg-surface border border-line",
40
+ transparent: "bg-transparent",
41
+ };
42
+
43
+ const PANEL_TONE: Record<TableSurfaceTone, string> = {
44
+ default: "bg-table-panel border-table-panel-border",
45
+ muted: "bg-elevated border-line",
46
+ transparent: "bg-transparent border-line",
47
+ };
48
+
49
+ function isActionConfig(
50
+ action: ReactNode | TableSurfaceAction,
51
+ ): action is TableSurfaceAction {
52
+ return (
53
+ typeof action === "object" &&
54
+ action !== null &&
55
+ "label" in action &&
56
+ typeof (action as TableSurfaceAction).label === "string"
57
+ );
58
+ }
59
+
60
+ /**
61
+ * Layered table surface: an outer shell holding a header band and an inset
62
+ * inner panel. Header and panel share one horizontal scroller so their columns
63
+ * stay aligned; the optional action stays pinned to the header's top-right.
64
+ * Colors come from the `--table-*` tokens and can be adjusted per surface with
65
+ * `tone` plus `headerClassName` / `panelClassName`.
66
+ */
67
+ export function TableSurface({
68
+ header,
69
+ children,
70
+ action,
71
+ tone = "default",
72
+ className,
73
+ headerClassName,
74
+ panelClassName,
75
+ minHeight,
76
+ }: TableSurfaceProps) {
77
+ const actionNode = isActionConfig(action) ? (
78
+ <Button
79
+ variant={action.variant ?? "primary"}
80
+ size="xs"
81
+ leadingIcon={action.href ? undefined : action.icon}
82
+ onClick={action.onClick}
83
+ disabled={action.disabled}
84
+ asChild={Boolean(action.href)}
85
+ >
86
+ {action.href ? (
87
+ <a href={action.href}>
88
+ {action.icon}
89
+ {action.label}
90
+ </a>
91
+ ) : (
92
+ action.label
93
+ )}
94
+ </Button>
95
+ ) : (
96
+ action
97
+ );
98
+
99
+ return (
100
+ <TableContainer className={cn("relative", SHELL_TONE[tone], className)}>
101
+ <div className="overflow-x-auto">
102
+ {header ? (
103
+ // Header rows must not inherit the body row hover surface.
104
+ <div className={cn("[--table-row-hover:transparent]", headerClassName)}>
105
+ {header}
106
+ </div>
107
+ ) : null}
108
+ <TablePanel
109
+ className={cn(PANEL_TONE[tone], panelClassName)}
110
+ style={minHeight ? { minHeight } : undefined}
111
+ >
112
+ {children}
113
+ </TablePanel>
114
+ </div>
115
+ {actionNode ? (
116
+ <div className="pointer-events-none absolute right-4 top-(--table-inset) flex h-(--table-header-h) items-center">
117
+ <div className="pointer-events-auto">{actionNode}</div>
118
+ </div>
119
+ ) : null}
120
+ </TableContainer>
121
+ );
122
+ }
package/src/table.tsx ADDED
@@ -0,0 +1,91 @@
1
+ import type { HTMLAttributes, TdHTMLAttributes, ThHTMLAttributes } from "react";
2
+
3
+ import { cn } from "./utils";
4
+
5
+ /**
6
+ * Outer table shell: near-black surface with generous rounding that holds the
7
+ * header row and the inset inner panel. Surface contrast, not borders/shadows.
8
+ */
9
+ export function TableContainer({
10
+ className,
11
+ ...props
12
+ }: HTMLAttributes<HTMLDivElement>) {
13
+ return (
14
+ <div
15
+ className={cn(
16
+ "w-full overflow-hidden rounded-(--table-radius-outer) bg-table-container p-(--table-inset)",
17
+ className,
18
+ )}
19
+ {...props}
20
+ />
21
+ );
22
+ }
23
+
24
+ /** Inset panel holding the table body (and its empty/loading/error states). */
25
+ export function TablePanel({
26
+ className,
27
+ ...props
28
+ }: HTMLAttributes<HTMLDivElement>) {
29
+ return (
30
+ <div
31
+ className={cn(
32
+ "overflow-hidden rounded-(--table-radius-panel) border border-table-panel-border bg-table-panel",
33
+ className,
34
+ )}
35
+ {...props}
36
+ />
37
+ );
38
+ }
39
+
40
+ export function Table({ className, ...props }: HTMLAttributes<HTMLTableElement>) {
41
+ return (
42
+ <table
43
+ className={cn("w-full table-fixed border-collapse caption-bottom text-sm", className)}
44
+ {...props}
45
+ />
46
+ );
47
+ }
48
+
49
+ export function TableHeader({ className, ...props }: HTMLAttributes<HTMLTableSectionElement>) {
50
+ return <thead className={className} {...props} />;
51
+ }
52
+
53
+ export function TableBody({ className, ...props }: HTMLAttributes<HTMLTableSectionElement>) {
54
+ return <tbody className={cn("[&_tr:last-child]:border-0", className)} {...props} />;
55
+ }
56
+
57
+ export function TableRow({ className, ...props }: HTMLAttributes<HTMLTableRowElement>) {
58
+ return (
59
+ <tr
60
+ className={cn(
61
+ "border-b border-table-panel-border transition-colors last:border-0 hover:bg-table-row-hover data-[state=selected]:bg-table-row-selected",
62
+ className,
63
+ )}
64
+ {...props}
65
+ />
66
+ );
67
+ }
68
+
69
+ export function TableHead({ className, ...props }: ThHTMLAttributes<HTMLTableCellElement>) {
70
+ return (
71
+ <th
72
+ className={cn(
73
+ "h-(--table-header-h) whitespace-nowrap px-(--table-pad-x) text-left align-middle text-[0.8125rem] font-medium text-table-header",
74
+ className,
75
+ )}
76
+ {...props}
77
+ />
78
+ );
79
+ }
80
+
81
+ export function TableCell({ className, ...props }: TdHTMLAttributes<HTMLTableCellElement>) {
82
+ return (
83
+ <td
84
+ className={cn(
85
+ "px-(--table-pad-x) py-3 align-middle text-sm text-table-row data-numeric:font-numeric data-numeric:tabular-nums",
86
+ className,
87
+ )}
88
+ {...props}
89
+ />
90
+ );
91
+ }
@@ -0,0 +1,32 @@
1
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
2
+ import { forwardRef, type ComponentPropsWithoutRef, type ElementRef } from "react";
3
+
4
+ import { cn } from "./utils";
5
+
6
+ export const TooltipProvider = TooltipPrimitive.Provider;
7
+ export const Tooltip = TooltipPrimitive.Root;
8
+ export const TooltipTrigger = TooltipPrimitive.Trigger;
9
+
10
+ export const TooltipContent = forwardRef<
11
+ ElementRef<typeof TooltipPrimitive.Content>,
12
+ ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
13
+ >(({ className, sideOffset = 6, ...props }, ref) => (
14
+ <TooltipPrimitive.Portal>
15
+ <TooltipPrimitive.Content
16
+ ref={ref}
17
+ sideOffset={sideOffset}
18
+ className={cn(
19
+ "z-50 overflow-hidden rounded-md border border-line bg-elevated px-2 py-1 text-xs text-fg shadow-md",
20
+ "data-[state=delayed-open]:animate-in data-[state=closed]:animate-out",
21
+ "data-[state=delayed-open]:fade-in-0 data-[state=closed]:fade-out-0",
22
+ "data-[state=delayed-open]:zoom-in-95 data-[state=closed]:zoom-out-95",
23
+ "data-[side=bottom]:slide-in-from-top-1 data-[side=top]:slide-in-from-bottom-1",
24
+ "data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1",
25
+ "duration-150 ease-out",
26
+ className,
27
+ )}
28
+ {...props}
29
+ />
30
+ </TooltipPrimitive.Portal>
31
+ ));
32
+ TooltipContent.displayName = "TooltipContent";
package/src/utils.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ /** Merge conditional class names with Tailwind conflict resolution. */
5
+ export function cn(...inputs: ClassValue[]): string {
6
+ return twMerge(clsx(inputs));
7
+ }