@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.
- package/README.md +63 -2
- package/dist/agent-prompt.d.ts +16 -0
- package/dist/agent-prompt.d.ts.map +1 -0
- package/dist/avatar.d.ts +5 -0
- package/dist/avatar.d.ts.map +1 -0
- package/dist/badge.d.ts +11 -0
- package/dist/badge.d.ts.map +1 -0
- package/dist/button.d.ts +22 -0
- package/dist/button.d.ts.map +1 -0
- package/dist/callout.d.ts +12 -0
- package/dist/callout.d.ts.map +1 -0
- package/dist/card.d.ts +8 -0
- package/dist/card.d.ts.map +1 -0
- package/dist/checkbox.d.ts +3 -0
- package/dist/checkbox.d.ts.map +1 -0
- package/dist/choice-dialog.d.ts +34 -0
- package/dist/choice-dialog.d.ts.map +1 -0
- package/dist/code-block.d.ts +11 -0
- package/dist/code-block.d.ts.map +1 -0
- package/dist/data-table.d.ts +84 -0
- package/dist/data-table.d.ts.map +1 -0
- package/dist/dialog.d.ts +19 -0
- package/dist/dialog.d.ts.map +1 -0
- package/dist/dropdown-menu.d.ts +13 -0
- package/dist/dropdown-menu.d.ts.map +1 -0
- package/dist/empty-state.d.ts +11 -0
- package/dist/empty-state.d.ts.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1522 -0
- package/dist/index.js.map +1 -0
- package/dist/input.d.ts +19 -0
- package/dist/input.d.ts.map +1 -0
- package/dist/kbd.d.ts +4 -0
- package/dist/kbd.d.ts.map +1 -0
- package/dist/label.d.ts +3 -0
- package/dist/label.d.ts.map +1 -0
- package/dist/radio-group.d.ts +35 -0
- package/dist/radio-group.d.ts.map +1 -0
- package/dist/segmented.d.ts +22 -0
- package/dist/segmented.d.ts.map +1 -0
- package/dist/select.d.ts +9 -0
- package/dist/select.d.ts.map +1 -0
- package/dist/separator.d.ts +3 -0
- package/dist/separator.d.ts.map +1 -0
- package/dist/skeleton.d.ts +3 -0
- package/dist/skeleton.d.ts.map +1 -0
- package/dist/spinner.d.ts +4 -0
- package/dist/spinner.d.ts.map +1 -0
- package/dist/table-empty-state.d.ts +15 -0
- package/dist/table-empty-state.d.ts.map +1 -0
- package/dist/table-surface.d.ts +38 -0
- package/dist/table-surface.d.ts.map +1 -0
- package/dist/table.d.ts +15 -0
- package/dist/table.d.ts.map +1 -0
- package/dist/tooltip.d.ts +6 -0
- package/dist/tooltip.d.ts.map +1 -0
- package/dist/utils.d.ts +4 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +73 -4
- package/src/agent-prompt.tsx +60 -0
- package/src/avatar.tsx +46 -0
- package/src/badge.tsx +54 -0
- package/src/button.tsx +126 -0
- package/src/callout.tsx +74 -0
- package/src/card.tsx +61 -0
- package/src/checkbox.tsx +32 -0
- package/src/choice-dialog.tsx +110 -0
- package/src/code-block.tsx +53 -0
- package/src/data-table.tsx +589 -0
- package/src/dialog.tsx +123 -0
- package/src/dropdown-menu.tsx +103 -0
- package/src/empty-state.tsx +35 -0
- package/src/index.ts +26 -0
- package/src/input.tsx +171 -0
- package/src/kbd.tsx +16 -0
- package/src/label.tsx +19 -0
- package/src/radio-group.tsx +134 -0
- package/src/segmented.tsx +76 -0
- package/src/select.tsx +91 -0
- package/src/separator.tsx +22 -0
- package/src/skeleton.tsx +12 -0
- package/src/spinner.tsx +13 -0
- package/src/table-empty-state.tsx +51 -0
- package/src/table-surface.tsx +122 -0
- package/src/table.tsx +91 -0
- package/src/tooltip.tsx +32 -0
- package/src/utils.ts +7 -0
- 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";
|
package/src/skeleton.tsx
ADDED
|
@@ -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
|
+
}
|
package/src/spinner.tsx
ADDED
|
@@ -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
|
+
}
|
package/src/tooltip.tsx
ADDED
|
@@ -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