@sero-ai/ui 0.3.1 → 0.4.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/.turbo/turbo-build.log +628 -0
- package/.turbo/turbo-typecheck.log +1 -1
- package/CHANGELOG.md +38 -0
- package/dist/components/context-editor/CapabilitySection.cjs +57 -0
- package/dist/components/context-editor/CapabilitySection.d.cts +20 -0
- package/dist/components/context-editor/CapabilitySection.d.ts +20 -0
- package/dist/components/context-editor/CapabilitySection.js +57 -0
- package/dist/components/context-editor/ContextEditor.cjs +108 -0
- package/dist/components/context-editor/ContextEditor.d.cts +26 -0
- package/dist/components/context-editor/ContextEditor.d.ts +26 -0
- package/dist/components/context-editor/ContextEditor.js +108 -0
- package/dist/components/context-editor/PresetBar.cjs +71 -0
- package/dist/components/context-editor/PresetBar.d.cts +19 -0
- package/dist/components/context-editor/PresetBar.d.ts +19 -0
- package/dist/components/context-editor/PresetBar.js +71 -0
- package/dist/components/context-editor/SkillsSection.cjs +31 -0
- package/dist/components/context-editor/SkillsSection.d.cts +14 -0
- package/dist/components/context-editor/SkillsSection.d.ts +14 -0
- package/dist/components/context-editor/SkillsSection.js +31 -0
- package/dist/components/context-editor/SystemPromptSection.cjs +58 -0
- package/dist/components/context-editor/SystemPromptSection.d.cts +25 -0
- package/dist/components/context-editor/SystemPromptSection.d.ts +25 -0
- package/dist/components/context-editor/SystemPromptSection.js +58 -0
- package/dist/components/context-editor/ToolsSection.cjs +31 -0
- package/dist/components/context-editor/ToolsSection.d.cts +14 -0
- package/dist/components/context-editor/ToolsSection.d.ts +14 -0
- package/dist/components/context-editor/ToolsSection.js +31 -0
- package/dist/components/context-editor/index.cjs +8 -0
- package/dist/components/context-editor/index.d.cts +5 -0
- package/dist/components/context-editor/index.d.ts +5 -0
- package/dist/components/context-editor/index.js +8 -0
- package/dist/components/context-editor/parts.cjs +184 -0
- package/dist/components/context-editor/parts.d.cts +32 -0
- package/dist/components/context-editor/parts.d.ts +32 -0
- package/dist/components/context-editor/parts.js +184 -0
- package/dist/components/context-editor/use-context-editor-model.cjs +194 -0
- package/dist/components/context-editor/use-context-editor-model.d.cts +57 -0
- package/dist/components/context-editor/use-context-editor-model.d.ts +57 -0
- package/dist/components/context-editor/use-context-editor-model.js +194 -0
- package/dist/components/dashboard/activity-list.cjs +59 -0
- package/dist/components/dashboard/activity-list.d.cts +27 -0
- package/dist/components/dashboard/activity-list.d.ts +27 -0
- package/dist/components/dashboard/activity-list.js +59 -0
- package/dist/components/dashboard/catalog.cjs +322 -0
- package/dist/components/dashboard/catalog.d.cts +14 -0
- package/dist/components/dashboard/catalog.d.ts +14 -0
- package/dist/components/dashboard/catalog.js +322 -0
- package/dist/components/dashboard/data-boundary.cjs +29 -0
- package/dist/components/dashboard/data-boundary.d.cts +21 -0
- package/dist/components/dashboard/data-boundary.d.ts +21 -0
- package/dist/components/dashboard/data-boundary.js +29 -0
- package/dist/components/dashboard/empty-state.cjs +49 -0
- package/dist/components/dashboard/empty-state.d.cts +20 -0
- package/dist/components/dashboard/empty-state.d.ts +20 -0
- package/dist/components/dashboard/empty-state.js +49 -0
- package/dist/components/dashboard/icon-button.cjs +40 -0
- package/dist/components/dashboard/icon-button.d.cts +24 -0
- package/dist/components/dashboard/icon-button.d.ts +24 -0
- package/dist/components/dashboard/icon-button.js +40 -0
- package/dist/components/dashboard/index.cjs +15 -0
- package/dist/components/dashboard/index.d.cts +24 -0
- package/dist/components/dashboard/index.d.ts +24 -0
- package/dist/components/dashboard/index.js +15 -0
- package/dist/components/dashboard/item-list.cjs +71 -0
- package/dist/components/dashboard/item-list.d.cts +28 -0
- package/dist/components/dashboard/item-list.d.ts +28 -0
- package/dist/components/dashboard/item-list.js +71 -0
- package/dist/components/dashboard/key-value.cjs +39 -0
- package/dist/components/dashboard/key-value.d.cts +17 -0
- package/dist/components/dashboard/key-value.d.ts +17 -0
- package/dist/components/dashboard/key-value.js +39 -0
- package/dist/components/dashboard/layout.cjs +193 -0
- package/dist/components/dashboard/layout.d.cts +91 -0
- package/dist/components/dashboard/layout.d.ts +91 -0
- package/dist/components/dashboard/layout.js +193 -0
- package/dist/components/dashboard/metric.cjs +98 -0
- package/dist/components/dashboard/metric.d.cts +44 -0
- package/dist/components/dashboard/metric.d.ts +44 -0
- package/dist/components/dashboard/metric.js +98 -0
- package/dist/components/dashboard/progress-ring.cjs +77 -0
- package/dist/components/dashboard/progress-ring.d.cts +25 -0
- package/dist/components/dashboard/progress-ring.d.ts +25 -0
- package/dist/components/dashboard/progress-ring.js +77 -0
- package/dist/components/dashboard/skeletons.cjs +54 -0
- package/dist/components/dashboard/skeletons.d.cts +15 -0
- package/dist/components/dashboard/skeletons.d.ts +15 -0
- package/dist/components/dashboard/skeletons.js +54 -0
- package/dist/components/dashboard/spacing.cjs +10 -0
- package/dist/components/dashboard/spacing.d.cts +4 -0
- package/dist/components/dashboard/spacing.d.ts +4 -0
- package/dist/components/dashboard/spacing.js +10 -0
- package/dist/components/dashboard/stale-indicator.cjs +42 -0
- package/dist/components/dashboard/stale-indicator.d.cts +15 -0
- package/dist/components/dashboard/stale-indicator.d.ts +15 -0
- package/dist/components/dashboard/stale-indicator.js +42 -0
- package/dist/components/dashboard/status.cjs +62 -0
- package/dist/components/dashboard/status.d.cts +15 -0
- package/dist/components/dashboard/status.d.ts +15 -0
- package/dist/components/dashboard/status.js +62 -0
- package/dist/components/dashboard/tone.cjs +26 -0
- package/dist/components/dashboard/tone.d.cts +9 -0
- package/dist/components/dashboard/tone.d.ts +9 -0
- package/dist/components/dashboard/tone.js +26 -0
- package/dist/components/dashboard/typography.cjs +99 -0
- package/dist/components/dashboard/typography.d.cts +57 -0
- package/dist/components/dashboard/typography.d.ts +57 -0
- package/dist/components/dashboard/typography.js +99 -0
- package/dist/components/reference/ActivityExample.cjs +47 -0
- package/dist/components/reference/ActivityExample.d.cts +5 -0
- package/dist/components/reference/ActivityExample.d.ts +5 -0
- package/dist/components/reference/ActivityExample.js +47 -0
- package/dist/components/reference/ResourceExample.cjs +54 -0
- package/dist/components/reference/ResourceExample.d.cts +5 -0
- package/dist/components/reference/ResourceExample.d.ts +5 -0
- package/dist/components/reference/ResourceExample.js +54 -0
- package/dist/components/reference/SchedulerExample.cjs +68 -0
- package/dist/components/reference/SchedulerExample.d.cts +5 -0
- package/dist/components/reference/SchedulerExample.d.ts +5 -0
- package/dist/components/reference/SchedulerExample.js +68 -0
- package/dist/components/reference/StarterExample.cjs +34 -0
- package/dist/components/reference/StarterExample.d.cts +5 -0
- package/dist/components/reference/StarterExample.d.ts +5 -0
- package/dist/components/reference/StarterExample.js +34 -0
- package/dist/components/reference/index.cjs +10 -0
- package/dist/components/reference/index.d.cts +5 -0
- package/dist/components/reference/index.d.ts +5 -0
- package/dist/components/reference/index.js +10 -0
- package/dist/components/ui/badge.cjs +5 -1
- package/dist/components/ui/badge.d.cts +1 -1
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/badge.js +5 -1
- package/dist/dashboard-catalog.cjs +8 -0
- package/dist/dashboard-catalog.d.cts +1 -0
- package/dist/dashboard-catalog.d.ts +1 -0
- package/dist/dashboard-catalog.js +8 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +1 -0
- package/dist/reference.cjs +12 -0
- package/dist/reference.d.cts +5 -0
- package/dist/reference.d.ts +5 -0
- package/dist/reference.js +12 -0
- package/dist/styles/glass-board.css +43 -0
- package/dist/styles/globals.css +497 -0
- package/dist/styles/plugin.css +4 -0
- package/package.json +71 -30
- package/src/components/context-editor/CapabilitySection.tsx +85 -0
- package/src/components/context-editor/ContextEditor.tsx +138 -0
- package/src/components/context-editor/PresetBar.tsx +94 -0
- package/src/components/context-editor/SkillsSection.tsx +37 -0
- package/src/components/context-editor/SystemPromptSection.tsx +88 -0
- package/src/components/context-editor/ToolsSection.tsx +37 -0
- package/src/components/context-editor/index.ts +7 -0
- package/src/components/context-editor/parts.tsx +217 -0
- package/src/components/context-editor/use-context-editor-model.ts +296 -0
- package/src/components/dashboard/__tests__/catalog.test.ts +57 -0
- package/src/components/dashboard/__tests__/glass.test.tsx +42 -0
- package/src/components/dashboard/__tests__/state.test.tsx +110 -0
- package/src/components/dashboard/__tests__/variants.test.tsx +95 -0
- package/src/components/dashboard/activity-list.tsx +99 -0
- package/src/components/dashboard/catalog.ts +351 -0
- package/src/components/dashboard/data-boundary.tsx +54 -0
- package/src/components/dashboard/empty-state.tsx +70 -0
- package/src/components/dashboard/icon-button.tsx +58 -0
- package/src/components/dashboard/index.ts +21 -0
- package/src/components/dashboard/item-list.tsx +106 -0
- package/src/components/dashboard/key-value.tsx +54 -0
- package/src/components/dashboard/layout.tsx +286 -0
- package/src/components/dashboard/metric.tsx +139 -0
- package/src/components/dashboard/progress-ring.tsx +100 -0
- package/src/components/dashboard/skeletons.tsx +73 -0
- package/src/components/dashboard/spacing.ts +14 -0
- package/src/components/dashboard/stale-indicator.tsx +55 -0
- package/src/components/dashboard/status.tsx +75 -0
- package/src/components/dashboard/tone.ts +35 -0
- package/src/components/dashboard/typography.tsx +151 -0
- package/src/components/reference/ActivityExample.tsx +68 -0
- package/src/components/reference/ResourceExample.tsx +78 -0
- package/src/components/reference/SchedulerExample.tsx +88 -0
- package/src/components/reference/StarterExample.tsx +48 -0
- package/src/components/reference/index.ts +10 -0
- package/src/components/ui/badge.tsx +6 -0
- package/src/dashboard-catalog.ts +13 -0
- package/src/index.ts +5 -0
- package/src/reference.ts +15 -0
- package/src/styles/glass-board.css +43 -0
- package/src/styles/globals.css +59 -0
- package/tsup.config.ts +11 -3
- package/vitest.config.ts +8 -0
- package/LICENSE +0 -201
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// DataBoundary — a presentation boundary for runtime data states.
|
|
2
|
+
//
|
|
3
|
+
// It selects which content to show for loading / ready / empty / stale / error.
|
|
4
|
+
// It is presentation only: it does not fetch data, subscribe to sources or own
|
|
5
|
+
// retry behaviour. The plugin passes the current state and the fallback nodes.
|
|
6
|
+
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
|
|
9
|
+
export type DataState = "loading" | "ready" | "empty" | "stale" | "error";
|
|
10
|
+
|
|
11
|
+
export interface DataBoundaryProps {
|
|
12
|
+
state: DataState;
|
|
13
|
+
/** Shown while `state === "loading"`. */
|
|
14
|
+
loading?: React.ReactNode;
|
|
15
|
+
/** Shown while `state === "empty"`. */
|
|
16
|
+
empty?: React.ReactNode;
|
|
17
|
+
/** Shown while `state === "error"`. */
|
|
18
|
+
error?: React.ReactNode;
|
|
19
|
+
/** Banner rendered above children while `state === "stale"`. */
|
|
20
|
+
stale?: React.ReactNode;
|
|
21
|
+
/** Ready content, also shown (below `stale`) while `state === "stale"`. */
|
|
22
|
+
children?: React.ReactNode;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Selects presentation for the current data state. */
|
|
26
|
+
function DataBoundary({
|
|
27
|
+
state,
|
|
28
|
+
loading,
|
|
29
|
+
empty,
|
|
30
|
+
error,
|
|
31
|
+
stale,
|
|
32
|
+
children,
|
|
33
|
+
}: DataBoundaryProps) {
|
|
34
|
+
switch (state) {
|
|
35
|
+
case "loading":
|
|
36
|
+
return <>{loading ?? null}</>;
|
|
37
|
+
case "empty":
|
|
38
|
+
return <>{empty ?? null}</>;
|
|
39
|
+
case "error":
|
|
40
|
+
return <>{error ?? null}</>;
|
|
41
|
+
case "stale":
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
{stale}
|
|
45
|
+
{children}
|
|
46
|
+
</>
|
|
47
|
+
);
|
|
48
|
+
case "ready":
|
|
49
|
+
default:
|
|
50
|
+
return <>{children}</>;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { DataBoundary };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// EmptyState — a compact empty state built on the Empty primitives.
|
|
2
|
+
//
|
|
3
|
+
// Tightened for widget use (the base Empty primitive pads for full pages).
|
|
4
|
+
// Optional icon, title, message and action.
|
|
5
|
+
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
|
|
8
|
+
import { cn } from "../../lib/utils";
|
|
9
|
+
import {
|
|
10
|
+
Empty,
|
|
11
|
+
EmptyContent,
|
|
12
|
+
EmptyDescription,
|
|
13
|
+
EmptyHeader,
|
|
14
|
+
EmptyMedia,
|
|
15
|
+
EmptyTitle,
|
|
16
|
+
} from "../ui/empty";
|
|
17
|
+
import { Icon } from "./typography";
|
|
18
|
+
|
|
19
|
+
export interface EmptyStateProps
|
|
20
|
+
extends Omit<React.ComponentProps<typeof Empty>, "title"> {
|
|
21
|
+
/** Leading icon component (e.g. a lucide-react icon). */
|
|
22
|
+
icon?: React.ComponentType<{ className?: string }>;
|
|
23
|
+
title: React.ReactNode;
|
|
24
|
+
message?: React.ReactNode;
|
|
25
|
+
/** Optional action (e.g. a Button). */
|
|
26
|
+
action?: React.ReactNode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Compact empty state for widgets and plugin views. */
|
|
30
|
+
function EmptyState({
|
|
31
|
+
className,
|
|
32
|
+
icon,
|
|
33
|
+
title,
|
|
34
|
+
message,
|
|
35
|
+
action,
|
|
36
|
+
...props
|
|
37
|
+
}: EmptyStateProps) {
|
|
38
|
+
return (
|
|
39
|
+
<Empty
|
|
40
|
+
data-slot="empty-state"
|
|
41
|
+
className={cn(
|
|
42
|
+
"flex-1 gap-3 border-0 p-4 md:p-4",
|
|
43
|
+
className,
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
<EmptyHeader className="gap-1.5">
|
|
48
|
+
{icon && (
|
|
49
|
+
<EmptyMedia
|
|
50
|
+
variant="icon"
|
|
51
|
+
className="size-9 border-[var(--surface-line)] bg-[var(--surface-flat)] text-[var(--text-muted)]"
|
|
52
|
+
>
|
|
53
|
+
<Icon icon={icon} size="md" className="text-current" />
|
|
54
|
+
</EmptyMedia>
|
|
55
|
+
)}
|
|
56
|
+
<EmptyTitle className="text-sm font-medium text-[var(--text-primary)]">
|
|
57
|
+
{title}
|
|
58
|
+
</EmptyTitle>
|
|
59
|
+
{message && (
|
|
60
|
+
<EmptyDescription className="text-xs text-[var(--text-muted)]">
|
|
61
|
+
{message}
|
|
62
|
+
</EmptyDescription>
|
|
63
|
+
)}
|
|
64
|
+
</EmptyHeader>
|
|
65
|
+
{action && <EmptyContent className="gap-2">{action}</EmptyContent>}
|
|
66
|
+
</Empty>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { EmptyState };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// IconButton — a small icon-only button built on the Button primitive.
|
|
2
|
+
//
|
|
3
|
+
// Widget-appropriate sizing with a required accessible label, since an
|
|
4
|
+
// icon-only control carries no text for assistive tech.
|
|
5
|
+
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
|
|
8
|
+
import { cn } from "../../lib/utils";
|
|
9
|
+
import { Button } from "../ui/button";
|
|
10
|
+
import { Icon } from "./typography";
|
|
11
|
+
|
|
12
|
+
const sizeMap = {
|
|
13
|
+
xs: "icon-xs",
|
|
14
|
+
sm: "icon-sm",
|
|
15
|
+
md: "icon",
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
const iconSizeMap = {
|
|
19
|
+
xs: "xs",
|
|
20
|
+
sm: "sm",
|
|
21
|
+
md: "md",
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
export interface IconButtonProps
|
|
25
|
+
extends Omit<React.ComponentProps<typeof Button>, "size" | "children"> {
|
|
26
|
+
/** The icon component to render (e.g. a lucide-react icon). */
|
|
27
|
+
icon: React.ComponentType<{ className?: string }>;
|
|
28
|
+
/** Required accessible label — the control has no visible text. */
|
|
29
|
+
label: string;
|
|
30
|
+
size?: keyof typeof sizeMap;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** A compact icon-only button with a required accessible label. */
|
|
34
|
+
function IconButton({
|
|
35
|
+
className,
|
|
36
|
+
icon,
|
|
37
|
+
label,
|
|
38
|
+
size = "sm",
|
|
39
|
+
variant = "ghost",
|
|
40
|
+
...props
|
|
41
|
+
}: IconButtonProps) {
|
|
42
|
+
return (
|
|
43
|
+
<Button
|
|
44
|
+
data-slot="icon-button"
|
|
45
|
+
type="button"
|
|
46
|
+
variant={variant}
|
|
47
|
+
size={sizeMap[size]}
|
|
48
|
+
aria-label={label}
|
|
49
|
+
title={label}
|
|
50
|
+
className={cn("text-[var(--text-muted)]", className)}
|
|
51
|
+
{...props}
|
|
52
|
+
>
|
|
53
|
+
<Icon icon={icon} size={iconSizeMap[size]} className="text-current" />
|
|
54
|
+
</Button>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { IconButton };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// @sero-ai/ui dashboard components — shared compact presentation for widgets
|
|
2
|
+
// and full plugin views. Import from the package root: `@sero-ai/ui`.
|
|
3
|
+
//
|
|
4
|
+
// The component catalogue (metadata for discovery tooling) is exported
|
|
5
|
+
// separately from `@sero-ai/ui/dashboard-catalog`, not from here.
|
|
6
|
+
|
|
7
|
+
export * from "./tone";
|
|
8
|
+
export * from "./spacing";
|
|
9
|
+
export * from "./layout";
|
|
10
|
+
export * from "./typography";
|
|
11
|
+
export * from "./metric";
|
|
12
|
+
export * from "./status";
|
|
13
|
+
export * from "./key-value";
|
|
14
|
+
export * from "./item-list";
|
|
15
|
+
export * from "./activity-list";
|
|
16
|
+
export * from "./progress-ring";
|
|
17
|
+
export * from "./stale-indicator";
|
|
18
|
+
export * from "./data-boundary";
|
|
19
|
+
export * from "./empty-state";
|
|
20
|
+
export * from "./skeletons";
|
|
21
|
+
export * from "./icon-button";
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// ItemList and ItemListItem — compact rows for dashboard lists.
|
|
2
|
+
//
|
|
3
|
+
// Built on the existing Item primitive, tightened for dense widget use. Each
|
|
4
|
+
// row takes optional leading media, primary/secondary text, trailing metadata
|
|
5
|
+
// and actions. ItemList adds a shared overflow ("+N more") treatment so widgets
|
|
6
|
+
// stop hand-rolling it.
|
|
7
|
+
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
|
|
10
|
+
import { cn } from "../../lib/utils";
|
|
11
|
+
import {
|
|
12
|
+
Item,
|
|
13
|
+
ItemActions,
|
|
14
|
+
ItemContent,
|
|
15
|
+
ItemDescription,
|
|
16
|
+
ItemMedia,
|
|
17
|
+
ItemTitle,
|
|
18
|
+
} from "../ui/item";
|
|
19
|
+
|
|
20
|
+
export interface ItemListItemProps
|
|
21
|
+
extends Omit<React.ComponentProps<typeof Item>, "children"> {
|
|
22
|
+
/** Leading media — an icon, avatar or status marker. */
|
|
23
|
+
leading?: React.ReactNode;
|
|
24
|
+
primary: React.ReactNode;
|
|
25
|
+
secondary?: React.ReactNode;
|
|
26
|
+
/** Trailing metadata (e.g. a timestamp or count). */
|
|
27
|
+
trailing?: React.ReactNode;
|
|
28
|
+
/** Trailing interactive actions (e.g. an IconButton). */
|
|
29
|
+
actions?: React.ReactNode;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A single compact list row with optional media, text, metadata and actions. */
|
|
33
|
+
function ItemListItem({
|
|
34
|
+
className,
|
|
35
|
+
leading,
|
|
36
|
+
primary,
|
|
37
|
+
secondary,
|
|
38
|
+
trailing,
|
|
39
|
+
actions,
|
|
40
|
+
variant = "default",
|
|
41
|
+
...props
|
|
42
|
+
}: ItemListItemProps) {
|
|
43
|
+
return (
|
|
44
|
+
<Item
|
|
45
|
+
data-slot="item-list-item"
|
|
46
|
+
variant={variant}
|
|
47
|
+
size="sm"
|
|
48
|
+
className={cn(
|
|
49
|
+
"gap-2 rounded-md bg-[var(--surface-flat)] p-2 transition-colors hover:bg-[var(--glass-hover)]",
|
|
50
|
+
className,
|
|
51
|
+
)}
|
|
52
|
+
{...props}
|
|
53
|
+
>
|
|
54
|
+
{leading && (
|
|
55
|
+
<ItemMedia className="self-center">{leading}</ItemMedia>
|
|
56
|
+
)}
|
|
57
|
+
<ItemContent className="gap-0.5">
|
|
58
|
+
<ItemTitle className="truncate text-xs font-medium text-[var(--text-primary)]">
|
|
59
|
+
{primary}
|
|
60
|
+
</ItemTitle>
|
|
61
|
+
{secondary && (
|
|
62
|
+
<ItemDescription className="line-clamp-1 text-xs text-[var(--text-muted)]">
|
|
63
|
+
{secondary}
|
|
64
|
+
</ItemDescription>
|
|
65
|
+
)}
|
|
66
|
+
</ItemContent>
|
|
67
|
+
{trailing && (
|
|
68
|
+
<span className="shrink-0 text-[11px] tabular-nums text-[var(--text-muted)]">
|
|
69
|
+
{trailing}
|
|
70
|
+
</span>
|
|
71
|
+
)}
|
|
72
|
+
{actions && <ItemActions>{actions}</ItemActions>}
|
|
73
|
+
</Item>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ItemListProps extends React.ComponentProps<"div"> {
|
|
78
|
+
/** Count of items not shown; renders a "+N more" footer when > 0. */
|
|
79
|
+
overflowCount?: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** A vertical stack of ItemListItem rows with a shared overflow footer. */
|
|
83
|
+
function ItemList({
|
|
84
|
+
className,
|
|
85
|
+
overflowCount = 0,
|
|
86
|
+
children,
|
|
87
|
+
...props
|
|
88
|
+
}: ItemListProps) {
|
|
89
|
+
return (
|
|
90
|
+
<div
|
|
91
|
+
data-slot="item-list"
|
|
92
|
+
role="list"
|
|
93
|
+
className={cn("flex min-w-0 flex-col gap-1", className)}
|
|
94
|
+
{...props}
|
|
95
|
+
>
|
|
96
|
+
{children}
|
|
97
|
+
{overflowCount > 0 && (
|
|
98
|
+
<span className="px-2 text-[11px] text-[var(--text-muted)]">
|
|
99
|
+
+{overflowCount} more
|
|
100
|
+
</span>
|
|
101
|
+
)}
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export { ItemList, ItemListItem };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// KeyValue and KeyValueList — compact label/value metadata.
|
|
2
|
+
//
|
|
3
|
+
// For configuration summaries, totals and metadata where a full metric is too
|
|
4
|
+
// heavy. Uses a semantic description list with predictable alignment and
|
|
5
|
+
// value truncation.
|
|
6
|
+
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
|
|
9
|
+
import { cn } from "../../lib/utils";
|
|
10
|
+
|
|
11
|
+
export interface KeyValueProps extends React.ComponentProps<"div"> {
|
|
12
|
+
label: React.ReactNode;
|
|
13
|
+
value: React.ReactNode;
|
|
14
|
+
/** Use mono + tabular numerals for the value (ids, counts, sizes). */
|
|
15
|
+
mono?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** A single label/value row rendered as a `dt`/`dd` pair. */
|
|
19
|
+
function KeyValue({ className, label, value, mono, ...props }: KeyValueProps) {
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
data-slot="key-value"
|
|
23
|
+
className={cn("flex items-baseline justify-between gap-3", className)}
|
|
24
|
+
{...props}
|
|
25
|
+
>
|
|
26
|
+
<dt className="shrink-0 truncate text-xs text-[var(--text-muted)]">
|
|
27
|
+
{label}
|
|
28
|
+
</dt>
|
|
29
|
+
<dd
|
|
30
|
+
className={cn(
|
|
31
|
+
"min-w-0 truncate text-right text-xs text-[var(--text-primary)]",
|
|
32
|
+
mono && "font-mono tabular-nums",
|
|
33
|
+
)}
|
|
34
|
+
>
|
|
35
|
+
{value}
|
|
36
|
+
</dd>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface KeyValueListProps extends React.ComponentProps<"dl"> {}
|
|
42
|
+
|
|
43
|
+
/** A vertical list of KeyValue rows. */
|
|
44
|
+
function KeyValueList({ className, ...props }: KeyValueListProps) {
|
|
45
|
+
return (
|
|
46
|
+
<dl
|
|
47
|
+
data-slot="key-value-list"
|
|
48
|
+
className={cn("flex flex-col gap-1.5", className)}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { KeyValue, KeyValueList };
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
// Layout and structure primitives for dashboard widgets.
|
|
2
|
+
//
|
|
3
|
+
// WidgetContent, Stack, Inline, Grid, Section and Divider give widgets a
|
|
4
|
+
// consistent frame, spacing and container-query boundary without re-declaring
|
|
5
|
+
// `flex h-full flex-col gap-2 p-3` in every widget. They compose normal React
|
|
6
|
+
// children and accept `className` for escape hatches.
|
|
7
|
+
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
10
|
+
|
|
11
|
+
import { cn } from "../../lib/utils";
|
|
12
|
+
import { Separator } from "../ui/separator";
|
|
13
|
+
import { gapClass, type Gap } from "./spacing";
|
|
14
|
+
|
|
15
|
+
// ── WidgetContent ────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
const widgetContentVariants = cva(
|
|
18
|
+
// Full-height frame with a container-query boundary so child components can
|
|
19
|
+
// respond to the widget's own size rather than the viewport.
|
|
20
|
+
"@container/widget flex h-full min-h-0 flex-col",
|
|
21
|
+
{
|
|
22
|
+
variants: {
|
|
23
|
+
padding: {
|
|
24
|
+
none: "p-0",
|
|
25
|
+
sm: "p-2",
|
|
26
|
+
md: "p-3",
|
|
27
|
+
lg: "p-4",
|
|
28
|
+
},
|
|
29
|
+
scroll: {
|
|
30
|
+
true: "overflow-auto",
|
|
31
|
+
false: "overflow-hidden",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
defaultVariants: { padding: "md", scroll: false },
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
export interface WidgetContentProps
|
|
39
|
+
extends React.ComponentProps<"div">,
|
|
40
|
+
VariantProps<typeof widgetContentVariants> {
|
|
41
|
+
/**
|
|
42
|
+
* Apply the glass token scope so surfaces read as translucent on the
|
|
43
|
+
* frosted dashboard tile. On by default; set `false` for solid full
|
|
44
|
+
* plugin views (outside a dashboard tile).
|
|
45
|
+
*/
|
|
46
|
+
glass?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Full-height widget content frame with standard padding + a container query. */
|
|
50
|
+
function WidgetContent({
|
|
51
|
+
className,
|
|
52
|
+
padding,
|
|
53
|
+
scroll,
|
|
54
|
+
glass = true,
|
|
55
|
+
...props
|
|
56
|
+
}: WidgetContentProps) {
|
|
57
|
+
return (
|
|
58
|
+
<div
|
|
59
|
+
data-slot="widget-content"
|
|
60
|
+
className={cn(widgetContentVariants({ padding, scroll }), glass && "glass", className)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ── Stack ────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
const alignClass = {
|
|
69
|
+
start: "items-start",
|
|
70
|
+
center: "items-center",
|
|
71
|
+
end: "items-end",
|
|
72
|
+
stretch: "items-stretch",
|
|
73
|
+
} as const;
|
|
74
|
+
|
|
75
|
+
const justifyClass = {
|
|
76
|
+
start: "justify-start",
|
|
77
|
+
center: "justify-center",
|
|
78
|
+
end: "justify-end",
|
|
79
|
+
between: "justify-between",
|
|
80
|
+
around: "justify-around",
|
|
81
|
+
} as const;
|
|
82
|
+
|
|
83
|
+
export interface StackProps extends React.ComponentProps<"div"> {
|
|
84
|
+
gap?: Gap;
|
|
85
|
+
align?: keyof typeof alignClass;
|
|
86
|
+
justify?: keyof typeof justifyClass;
|
|
87
|
+
/**
|
|
88
|
+
* Grow to fill the remaining height of a flex parent (e.g. the top-level
|
|
89
|
+
* Stack under `WidgetContent`, so a nested `scroll` region can bound).
|
|
90
|
+
*/
|
|
91
|
+
fill?: boolean;
|
|
92
|
+
/** Fill remaining height and scroll overflow. Use for the main content flow. */
|
|
93
|
+
scroll?: boolean;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Vertical layout with semantic spacing, alignment and optional scrolling. */
|
|
97
|
+
function Stack({
|
|
98
|
+
className,
|
|
99
|
+
gap = "sm",
|
|
100
|
+
align,
|
|
101
|
+
justify,
|
|
102
|
+
fill = false,
|
|
103
|
+
scroll = false,
|
|
104
|
+
...props
|
|
105
|
+
}: StackProps) {
|
|
106
|
+
return (
|
|
107
|
+
<div
|
|
108
|
+
data-slot="stack"
|
|
109
|
+
className={cn(
|
|
110
|
+
"flex flex-col",
|
|
111
|
+
gapClass[gap],
|
|
112
|
+
align && alignClass[align],
|
|
113
|
+
justify && justifyClass[justify],
|
|
114
|
+
(fill || scroll) && "min-h-0 flex-1",
|
|
115
|
+
scroll && "overflow-auto",
|
|
116
|
+
className,
|
|
117
|
+
)}
|
|
118
|
+
{...props}
|
|
119
|
+
/>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ── Inline ───────────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
export interface InlineProps extends React.ComponentProps<"div"> {
|
|
126
|
+
gap?: Gap;
|
|
127
|
+
align?: keyof typeof alignClass;
|
|
128
|
+
justify?: keyof typeof justifyClass;
|
|
129
|
+
wrap?: boolean;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Horizontal layout with semantic spacing, alignment, wrap and justify. */
|
|
133
|
+
function Inline({
|
|
134
|
+
className,
|
|
135
|
+
gap = "sm",
|
|
136
|
+
align = "center",
|
|
137
|
+
justify,
|
|
138
|
+
wrap = false,
|
|
139
|
+
...props
|
|
140
|
+
}: InlineProps) {
|
|
141
|
+
return (
|
|
142
|
+
<div
|
|
143
|
+
data-slot="inline"
|
|
144
|
+
className={cn(
|
|
145
|
+
"flex flex-row",
|
|
146
|
+
gapClass[gap],
|
|
147
|
+
alignClass[align],
|
|
148
|
+
justify && justifyClass[justify],
|
|
149
|
+
wrap ? "flex-wrap" : "min-w-0",
|
|
150
|
+
className,
|
|
151
|
+
)}
|
|
152
|
+
{...props}
|
|
153
|
+
/>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ── Grid ─────────────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
const columnsClass = {
|
|
160
|
+
1: "grid-cols-1",
|
|
161
|
+
2: "grid-cols-2",
|
|
162
|
+
3: "grid-cols-3",
|
|
163
|
+
4: "grid-cols-4",
|
|
164
|
+
} as const;
|
|
165
|
+
|
|
166
|
+
export interface GridProps extends React.ComponentProps<"div"> {
|
|
167
|
+
/** Fixed column count, or `auto` for a responsive min-width fill. */
|
|
168
|
+
columns?: keyof typeof columnsClass | "auto";
|
|
169
|
+
gap?: Gap;
|
|
170
|
+
/** Minimum column width for `columns="auto"`. */
|
|
171
|
+
minColumnWidth?: number;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Responsive grid with bounded columns for metric and summary layouts. */
|
|
175
|
+
function Grid({
|
|
176
|
+
className,
|
|
177
|
+
columns = "auto",
|
|
178
|
+
gap = "sm",
|
|
179
|
+
minColumnWidth = 120,
|
|
180
|
+
style,
|
|
181
|
+
...props
|
|
182
|
+
}: GridProps) {
|
|
183
|
+
const auto = columns === "auto";
|
|
184
|
+
return (
|
|
185
|
+
<div
|
|
186
|
+
data-slot="grid"
|
|
187
|
+
className={cn(
|
|
188
|
+
"grid",
|
|
189
|
+
gapClass[gap],
|
|
190
|
+
!auto && columnsClass[columns],
|
|
191
|
+
className,
|
|
192
|
+
)}
|
|
193
|
+
style={
|
|
194
|
+
auto
|
|
195
|
+
? {
|
|
196
|
+
gridTemplateColumns: `repeat(auto-fill, minmax(${minColumnWidth}px, 1fr))`,
|
|
197
|
+
...style,
|
|
198
|
+
}
|
|
199
|
+
: style
|
|
200
|
+
}
|
|
201
|
+
{...props}
|
|
202
|
+
/>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ── Section ──────────────────────────────────────────────────────
|
|
207
|
+
|
|
208
|
+
export interface SectionProps extends React.ComponentProps<"div"> {
|
|
209
|
+
/** Section heading text or node. Omit for an unlabelled group. */
|
|
210
|
+
heading?: React.ReactNode;
|
|
211
|
+
/** Supporting text shown under the heading. */
|
|
212
|
+
description?: React.ReactNode;
|
|
213
|
+
/** Trailing action aligned to the heading row (e.g. an IconButton). */
|
|
214
|
+
action?: React.ReactNode;
|
|
215
|
+
gap?: Gap;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Compact section with optional heading, description and trailing action. */
|
|
219
|
+
function Section({
|
|
220
|
+
className,
|
|
221
|
+
heading,
|
|
222
|
+
description,
|
|
223
|
+
action,
|
|
224
|
+
gap = "sm",
|
|
225
|
+
children,
|
|
226
|
+
...props
|
|
227
|
+
}: SectionProps) {
|
|
228
|
+
return (
|
|
229
|
+
<section
|
|
230
|
+
data-slot="section"
|
|
231
|
+
className={cn("flex flex-col", gapClass[gap], className)}
|
|
232
|
+
{...props}
|
|
233
|
+
>
|
|
234
|
+
{(heading || action) && (
|
|
235
|
+
<div className="flex items-center justify-between gap-2">
|
|
236
|
+
<div className="flex min-w-0 flex-col gap-0.5">
|
|
237
|
+
{heading && (
|
|
238
|
+
<h3 className="truncate text-[11px] font-semibold uppercase tracking-wider text-[var(--text-muted)]">
|
|
239
|
+
{heading}
|
|
240
|
+
</h3>
|
|
241
|
+
)}
|
|
242
|
+
{description && (
|
|
243
|
+
<p className="truncate text-xs text-[var(--text-muted)]">
|
|
244
|
+
{description}
|
|
245
|
+
</p>
|
|
246
|
+
)}
|
|
247
|
+
</div>
|
|
248
|
+
{action && <div className="shrink-0">{action}</div>}
|
|
249
|
+
</div>
|
|
250
|
+
)}
|
|
251
|
+
{children}
|
|
252
|
+
</section>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ── Divider ──────────────────────────────────────────────────────
|
|
257
|
+
|
|
258
|
+
export interface DividerProps
|
|
259
|
+
extends React.ComponentProps<typeof Separator> {
|
|
260
|
+
/** Vertical spacing around a horizontal divider. */
|
|
261
|
+
spacing?: "none" | "sm" | "md";
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const dividerSpacing = { none: "my-0", sm: "my-1", md: "my-2" } as const;
|
|
265
|
+
|
|
266
|
+
/** Compact divider built on the Separator primitive. */
|
|
267
|
+
function Divider({
|
|
268
|
+
className,
|
|
269
|
+
orientation = "horizontal",
|
|
270
|
+
spacing = "sm",
|
|
271
|
+
...props
|
|
272
|
+
}: DividerProps) {
|
|
273
|
+
return (
|
|
274
|
+
<Separator
|
|
275
|
+
data-slot="divider"
|
|
276
|
+
orientation={orientation}
|
|
277
|
+
className={cn(
|
|
278
|
+
orientation === "horizontal" ? dividerSpacing[spacing] : "mx-1",
|
|
279
|
+
className,
|
|
280
|
+
)}
|
|
281
|
+
{...props}
|
|
282
|
+
/>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export { WidgetContent, Stack, Inline, Grid, Section, Divider };
|