@corbits/react-ui 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/LICENSE +501 -0
- package/README.md +125 -0
- package/dist/blocks/login/auth-layout.d.ts +38 -0
- package/dist/blocks/login/auth-layout.js +52 -0
- package/dist/blocks/login/login-form.d.ts +60 -0
- package/dist/blocks/login/login-form.js +151 -0
- package/dist/hooks/use-command-palette-navigation.d.ts +25 -0
- package/dist/hooks/use-command-palette-navigation.js +52 -0
- package/dist/hooks/use-controllable-state.d.ts +28 -0
- package/dist/hooks/use-controllable-state.js +44 -0
- package/dist/hooks/use-delayed-autofocus.d.ts +9 -0
- package/dist/hooks/use-delayed-autofocus.js +20 -0
- package/dist/hooks/use-dismissable-popover.d.ts +37 -0
- package/dist/hooks/use-dismissable-popover.js +64 -0
- package/dist/hooks/use-list-selection.d.ts +66 -0
- package/dist/hooks/use-list-selection.js +147 -0
- package/dist/hooks/use-prefers-reduced-motion.d.ts +13 -0
- package/dist/hooks/use-prefers-reduced-motion.js +28 -0
- package/dist/hooks/use-scroll-current-into-view.d.ts +19 -0
- package/dist/hooks/use-scroll-current-into-view.js +33 -0
- package/dist/index.d.ts +122 -0
- package/dist/index.js +131 -0
- package/dist/lib/artifact.d.ts +72 -0
- package/dist/lib/artifact.js +67 -0
- package/dist/lib/chart-geometry.d.ts +62 -0
- package/dist/lib/chart-geometry.js +86 -0
- package/dist/lib/chart-palette.d.ts +76 -0
- package/dist/lib/chart-palette.js +92 -0
- package/dist/lib/chat-message.d.ts +115 -0
- package/dist/lib/chat-message.js +41 -0
- package/dist/lib/chat-parts.d.ts +64 -0
- package/dist/lib/chat-parts.js +16 -0
- package/dist/lib/csv.d.ts +60 -0
- package/dist/lib/csv.js +108 -0
- package/dist/lib/metrics.d.ts +70 -0
- package/dist/lib/metrics.js +101 -0
- package/dist/lib/now-item.d.ts +65 -0
- package/dist/lib/now-item.js +59 -0
- package/dist/lib/relative-time.d.ts +9 -0
- package/dist/lib/relative-time.js +47 -0
- package/dist/lib/theme.d.ts +30 -0
- package/dist/lib/theme.js +77 -0
- package/dist/lib/url.d.ts +26 -0
- package/dist/lib/url.js +37 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/lib/workflow-display-flow.d.ts +83 -0
- package/dist/lib/workflow-display-flow.js +104 -0
- package/dist/lib/workflow-registry.d.ts +90 -0
- package/dist/lib/workflow-registry.js +56 -0
- package/dist/lib/workflow-run-error.d.ts +31 -0
- package/dist/lib/workflow-run-error.js +105 -0
- package/dist/lib/workflow-run-progress.d.ts +97 -0
- package/dist/lib/workflow-run-progress.js +135 -0
- package/dist/lib/workflow-run.d.ts +43 -0
- package/dist/lib/workflow-run.js +56 -0
- package/dist/styles.css +3080 -0
- package/dist/test/render-hook.d.ts +14 -0
- package/dist/test/render-hook.js +38 -0
- package/dist/theme.css +628 -0
- package/dist/ui/agent-turn.d.ts +26 -0
- package/dist/ui/agent-turn.js +60 -0
- package/dist/ui/animated-number.d.ts +25 -0
- package/dist/ui/animated-number.js +63 -0
- package/dist/ui/approval-card.d.ts +46 -0
- package/dist/ui/approval-card.js +116 -0
- package/dist/ui/artifact-body.d.ts +26 -0
- package/dist/ui/artifact-body.js +170 -0
- package/dist/ui/artifact-notice.d.ts +21 -0
- package/dist/ui/artifact-notice.js +36 -0
- package/dist/ui/avatar.d.ts +37 -0
- package/dist/ui/avatar.js +73 -0
- package/dist/ui/badge.d.ts +10 -0
- package/dist/ui/badge.js +35 -0
- package/dist/ui/bar-chart.d.ts +38 -0
- package/dist/ui/bar-chart.js +118 -0
- package/dist/ui/block-card.d.ts +29 -0
- package/dist/ui/block-card.js +66 -0
- package/dist/ui/boot-screen.d.ts +23 -0
- package/dist/ui/boot-screen.js +49 -0
- package/dist/ui/bulk-action-bar.d.ts +37 -0
- package/dist/ui/bulk-action-bar.js +64 -0
- package/dist/ui/button.d.ts +12 -0
- package/dist/ui/button.js +73 -0
- package/dist/ui/card.d.ts +6 -0
- package/dist/ui/card.js +37 -0
- package/dist/ui/category-bars.d.ts +20 -0
- package/dist/ui/category-bars.js +69 -0
- package/dist/ui/chart-frame.d.ts +54 -0
- package/dist/ui/chart-frame.js +108 -0
- package/dist/ui/chat-composer.d.ts +24 -0
- package/dist/ui/chat-composer.js +32 -0
- package/dist/ui/chat-dock-timing.d.ts +15 -0
- package/dist/ui/chat-dock-timing.js +11 -0
- package/dist/ui/chat-input.d.ts +38 -0
- package/dist/ui/chat-input.js +138 -0
- package/dist/ui/chat-panel.d.ts +20 -0
- package/dist/ui/chat-panel.js +56 -0
- package/dist/ui/chat-thread.d.ts +28 -0
- package/dist/ui/chat-thread.js +61 -0
- package/dist/ui/checkbox.d.ts +25 -0
- package/dist/ui/checkbox.js +63 -0
- package/dist/ui/command-palette.d.ts +100 -0
- package/dist/ui/command-palette.js +341 -0
- package/dist/ui/compare-body.d.ts +52 -0
- package/dist/ui/compare-body.js +140 -0
- package/dist/ui/confirm-button.d.ts +29 -0
- package/dist/ui/confirm-button.js +41 -0
- package/dist/ui/corbits-mark.d.ts +29 -0
- package/dist/ui/corbits-mark.js +33 -0
- package/dist/ui/csv-table.d.ts +22 -0
- package/dist/ui/csv-table.js +85 -0
- package/dist/ui/dialog.d.ts +37 -0
- package/dist/ui/dialog.js +105 -0
- package/dist/ui/dictation-status-line.d.ts +20 -0
- package/dist/ui/dictation-status-line.js +38 -0
- package/dist/ui/dither-canvas.d.ts +31 -0
- package/dist/ui/dither-canvas.js +169 -0
- package/dist/ui/embed-body.d.ts +30 -0
- package/dist/ui/embed-body.js +92 -0
- package/dist/ui/empty-state.d.ts +26 -0
- package/dist/ui/empty-state.js +42 -0
- package/dist/ui/file-input.d.ts +25 -0
- package/dist/ui/file-input.js +66 -0
- package/dist/ui/filter-bar.d.ts +42 -0
- package/dist/ui/filter-bar.js +95 -0
- package/dist/ui/filter-chip.d.ts +14 -0
- package/dist/ui/filter-chip.js +23 -0
- package/dist/ui/gate-block.d.ts +16 -0
- package/dist/ui/gate-block.js +37 -0
- package/dist/ui/horizontal-stepper.d.ts +13 -0
- package/dist/ui/horizontal-stepper.js +65 -0
- package/dist/ui/input.d.ts +2 -0
- package/dist/ui/input.js +14 -0
- package/dist/ui/inspector-shell.d.ts +47 -0
- package/dist/ui/inspector-shell.js +98 -0
- package/dist/ui/intake-form.d.ts +39 -0
- package/dist/ui/intake-form.js +119 -0
- package/dist/ui/kind-card-grid.d.ts +23 -0
- package/dist/ui/kind-card-grid.js +43 -0
- package/dist/ui/library-page-header.d.ts +20 -0
- package/dist/ui/library-page-header.js +43 -0
- package/dist/ui/list-detail.d.ts +28 -0
- package/dist/ui/list-detail.js +55 -0
- package/dist/ui/live-run-banner.d.ts +13 -0
- package/dist/ui/live-run-banner.js +23 -0
- package/dist/ui/live-status-line.d.ts +22 -0
- package/dist/ui/live-status-line.js +37 -0
- package/dist/ui/menu.d.ts +18 -0
- package/dist/ui/menu.js +45 -0
- package/dist/ui/message-bubble.d.ts +24 -0
- package/dist/ui/message-bubble.js +64 -0
- package/dist/ui/mic-button.d.ts +35 -0
- package/dist/ui/mic-button.js +61 -0
- package/dist/ui/mic-permission-dialog.d.ts +20 -0
- package/dist/ui/mic-permission-dialog.js +50 -0
- package/dist/ui/notifications-bell.d.ts +28 -0
- package/dist/ui/notifications-bell.js +69 -0
- package/dist/ui/now-cards.d.ts +37 -0
- package/dist/ui/now-cards.js +116 -0
- package/dist/ui/page-panel.d.ts +37 -0
- package/dist/ui/page-panel.js +23 -0
- package/dist/ui/page-shell.d.ts +21 -0
- package/dist/ui/page-shell.js +26 -0
- package/dist/ui/parts-renderer.d.ts +22 -0
- package/dist/ui/parts-renderer.js +234 -0
- package/dist/ui/profile-card.d.ts +39 -0
- package/dist/ui/profile-card.js +124 -0
- package/dist/ui/progress-checklist.d.ts +25 -0
- package/dist/ui/progress-checklist.js +70 -0
- package/dist/ui/provider-mark.d.ts +32 -0
- package/dist/ui/provider-mark.js +31 -0
- package/dist/ui/quick-reply-chips.d.ts +14 -0
- package/dist/ui/quick-reply-chips.js +24 -0
- package/dist/ui/quote-card.d.ts +39 -0
- package/dist/ui/quote-card.js +66 -0
- package/dist/ui/reasoning-block.d.ts +35 -0
- package/dist/ui/reasoning-block.js +60 -0
- package/dist/ui/research-body.d.ts +70 -0
- package/dist/ui/research-body.js +270 -0
- package/dist/ui/rich-empty-state.d.ts +35 -0
- package/dist/ui/rich-empty-state.js +65 -0
- package/dist/ui/run-now-button.d.ts +23 -0
- package/dist/ui/run-now-button.js +42 -0
- package/dist/ui/section.d.ts +20 -0
- package/dist/ui/section.js +48 -0
- package/dist/ui/select.d.ts +2 -0
- package/dist/ui/select.js +22 -0
- package/dist/ui/selection-checkbox.d.ts +42 -0
- package/dist/ui/selection-checkbox.js +54 -0
- package/dist/ui/settings-panel.d.ts +35 -0
- package/dist/ui/settings-panel.js +68 -0
- package/dist/ui/shimmer-text.d.ts +19 -0
- package/dist/ui/shimmer-text.js +20 -0
- package/dist/ui/sidebar-item-row.d.ts +40 -0
- package/dist/ui/sidebar-item-row.js +64 -0
- package/dist/ui/sidebar-panel.d.ts +25 -0
- package/dist/ui/sidebar-panel.js +56 -0
- package/dist/ui/sidebar.d.ts +46 -0
- package/dist/ui/sidebar.js +111 -0
- package/dist/ui/skeleton.d.ts +13 -0
- package/dist/ui/skeleton.js +20 -0
- package/dist/ui/sortable-table.d.ts +43 -0
- package/dist/ui/sortable-table.js +103 -0
- package/dist/ui/sparkline.d.ts +27 -0
- package/dist/ui/sparkline.js +68 -0
- package/dist/ui/stat-grid.d.ts +55 -0
- package/dist/ui/stat-grid.js +87 -0
- package/dist/ui/status-dot.d.ts +13 -0
- package/dist/ui/status-dot.js +51 -0
- package/dist/ui/step-list.d.ts +21 -0
- package/dist/ui/step-list.js +77 -0
- package/dist/ui/switch.d.ts +23 -0
- package/dist/ui/switch.js +29 -0
- package/dist/ui/table.d.ts +8 -0
- package/dist/ui/table.js +48 -0
- package/dist/ui/tabs.d.ts +44 -0
- package/dist/ui/tabs.js +101 -0
- package/dist/ui/textarea.d.ts +8 -0
- package/dist/ui/textarea.js +32 -0
- package/dist/ui/theme-provider.d.ts +36 -0
- package/dist/ui/theme-provider.js +126 -0
- package/dist/ui/theme-toggle.d.ts +11 -0
- package/dist/ui/theme-toggle.js +32 -0
- package/dist/ui/thinking-indicator.d.ts +7 -0
- package/dist/ui/thinking-indicator.js +19 -0
- package/dist/ui/thinking-label.d.ts +19 -0
- package/dist/ui/thinking-label.js +54 -0
- package/dist/ui/thinking-mark.d.ts +10 -0
- package/dist/ui/thinking-mark.js +153 -0
- package/dist/ui/time-series-chart.d.ts +55 -0
- package/dist/ui/time-series-chart.js +249 -0
- package/dist/ui/toast.d.ts +16 -0
- package/dist/ui/toast.js +41 -0
- package/dist/ui/token-mosaic.d.ts +22 -0
- package/dist/ui/token-mosaic.js +65 -0
- package/dist/ui/tool-block.d.ts +54 -0
- package/dist/ui/tool-block.js +170 -0
- package/dist/ui/tool-narrative.d.ts +22 -0
- package/dist/ui/tool-narrative.js +77 -0
- package/dist/ui/tool-picker.d.ts +44 -0
- package/dist/ui/tool-picker.js +200 -0
- package/dist/ui/tooltip.d.ts +21 -0
- package/dist/ui/tooltip.js +48 -0
- package/dist/ui/top-bar.d.ts +53 -0
- package/dist/ui/top-bar.js +98 -0
- package/dist/ui/trace-waterfall.d.ts +38 -0
- package/dist/ui/trace-waterfall.js +121 -0
- package/dist/ui/typing-indicator.d.ts +9 -0
- package/dist/ui/typing-indicator.js +25 -0
- package/dist/ui/view-toggle.d.ts +17 -0
- package/dist/ui/view-toggle.js +46 -0
- package/dist/ui/voice-waveform.d.ts +16 -0
- package/dist/ui/voice-waveform.js +23 -0
- package/package.json +583 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/utils.js";
|
|
3
|
+
const SIZE_CLASS = {
|
|
4
|
+
sm: "size-6 text-[10px]",
|
|
5
|
+
md: "size-8 text-xs",
|
|
6
|
+
lg: "size-10 text-sm"
|
|
7
|
+
};
|
|
8
|
+
const TONE_CLASS = {
|
|
9
|
+
neutral: "bg-muted text-muted-foreground",
|
|
10
|
+
agent: "bg-primary text-primary-foreground",
|
|
11
|
+
agent2: "bg-accent text-accent-foreground",
|
|
12
|
+
agent3: "bg-success text-success-foreground"
|
|
13
|
+
};
|
|
14
|
+
const BADGE_SIZE = {
|
|
15
|
+
sm: "size-3 text-[7px]",
|
|
16
|
+
md: "size-3.5 text-[8px]",
|
|
17
|
+
lg: "size-4 text-[9px]"
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Initials avatar with optional tenant monogram badge. Images are
|
|
21
|
+
* intentionally unsupported here: identity headers should not flash or
|
|
22
|
+
* depend on third-party avatar hosts.
|
|
23
|
+
*/ export function Avatar({ initials, label, tone = "neutral", size = "md", tenantMonogram, className }) {
|
|
24
|
+
return /*#__PURE__*/ _jsxs("span", {
|
|
25
|
+
role: "img",
|
|
26
|
+
"aria-label": label,
|
|
27
|
+
className: cn("relative inline-flex shrink-0 items-center justify-center font-bold uppercase", SIZE_CLASS[size], TONE_CLASS[tone], className),
|
|
28
|
+
children: [
|
|
29
|
+
/*#__PURE__*/ _jsx("span", {
|
|
30
|
+
"aria-hidden": true,
|
|
31
|
+
children: initials.slice(0, 2)
|
|
32
|
+
}),
|
|
33
|
+
tenantMonogram === undefined ? null : /*#__PURE__*/ _jsx("span", {
|
|
34
|
+
"aria-hidden": true,
|
|
35
|
+
className: cn("absolute -right-0.5 -bottom-0.5 inline-flex items-center justify-center border border-background bg-muted font-bold uppercase text-muted-foreground", BADGE_SIZE[size]),
|
|
36
|
+
children: tenantMonogram.slice(0, 1)
|
|
37
|
+
})
|
|
38
|
+
]
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Overlapping avatar stack for thread participants and channel members.
|
|
43
|
+
*/ export function AvatarStack({ items, max = 4, size = "sm", className }) {
|
|
44
|
+
const visible = items.slice(0, max);
|
|
45
|
+
const overflow = items.length - visible.length;
|
|
46
|
+
return /*#__PURE__*/ _jsxs("span", {
|
|
47
|
+
className: cn("inline-flex items-center", className),
|
|
48
|
+
role: "group",
|
|
49
|
+
"aria-label": "Participants",
|
|
50
|
+
children: [
|
|
51
|
+
visible.map((item, index)=>/*#__PURE__*/ _jsx(Avatar, {
|
|
52
|
+
initials: item.initials,
|
|
53
|
+
label: item.label,
|
|
54
|
+
...item.tone === undefined ? {} : {
|
|
55
|
+
tone: item.tone
|
|
56
|
+
},
|
|
57
|
+
...item.tenantMonogram === undefined ? {} : {
|
|
58
|
+
tenantMonogram: item.tenantMonogram
|
|
59
|
+
},
|
|
60
|
+
size: size,
|
|
61
|
+
className: cn("ring-1 ring-background", index > 0 && "-ml-1.5")
|
|
62
|
+
}, item.id)),
|
|
63
|
+
overflow <= 0 ? null : /*#__PURE__*/ _jsxs("span", {
|
|
64
|
+
className: cn("inline-flex items-center justify-center bg-muted font-mono font-semibold text-muted-foreground ring-1 ring-background", SIZE_CLASS[size], visible.length > 0 && "-ml-1.5"),
|
|
65
|
+
"aria-label": `${overflow} more`,
|
|
66
|
+
children: [
|
|
67
|
+
"+",
|
|
68
|
+
overflow
|
|
69
|
+
]
|
|
70
|
+
})
|
|
71
|
+
]
|
|
72
|
+
});
|
|
73
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type VariantProps } from "class-variance-authority";
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
declare const badgeVariants: (props?: ({
|
|
4
|
+
tone?: "neutral" | "accent" | "info" | "success" | "warning" | "danger" | "shared" | "access" | null | undefined;
|
|
5
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
|
+
/** The tone names, exported so callers can type a state → tone lookup table. */
|
|
7
|
+
export type BadgeTone = NonNullable<VariantProps<typeof badgeVariants>["tone"]>;
|
|
8
|
+
export type BadgeProps = React.ComponentProps<"span"> & VariantProps<typeof badgeVariants>;
|
|
9
|
+
export declare function Badge({ className, tone, ...props }: BadgeProps): React.JSX.Element;
|
|
10
|
+
export { badgeVariants };
|
package/dist/ui/badge.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
// A compact label chip. Tones are semantic, not decorative: `accent` is the one
|
|
5
|
+
// orange tone and — like the primary button — should stay rare on a screen.
|
|
6
|
+
// Orange here is `primary-emphasis`, the orange that clears text contrast on the
|
|
7
|
+
// page and card grounds; the fill orange does not.
|
|
8
|
+
const badgeVariants = cva("inline-flex items-center gap-1 rounded-sm border px-1.5 py-0.5 text-[10px] font-semibold tracking-[0.08em] uppercase", {
|
|
9
|
+
variants: {
|
|
10
|
+
tone: {
|
|
11
|
+
neutral: "border-border bg-muted text-muted-foreground",
|
|
12
|
+
accent: "border-primary-emphasis/40 bg-primary/10 text-primary-emphasis",
|
|
13
|
+
info: "border-accent bg-accent text-accent-foreground",
|
|
14
|
+
success: "border-success bg-success text-success-foreground",
|
|
15
|
+
// Warning reuses accent amber until a dedicated warning pair lands in theme.
|
|
16
|
+
warning: "border-primary-emphasis/40 bg-primary/10 text-primary-emphasis",
|
|
17
|
+
danger: "border-destructive bg-destructive text-destructive-foreground",
|
|
18
|
+
shared: "border-accent bg-accent text-accent-foreground",
|
|
19
|
+
access: "border-primary-emphasis/40 bg-primary/10 text-primary-emphasis"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
defaultVariants: {
|
|
23
|
+
tone: "neutral"
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
export function Badge({ className, tone, ...props }) {
|
|
27
|
+
return /*#__PURE__*/ _jsx("span", {
|
|
28
|
+
"data-slot": "badge",
|
|
29
|
+
className: cn(badgeVariants({
|
|
30
|
+
tone
|
|
31
|
+
}), className),
|
|
32
|
+
...props
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export { badgeVariants };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type BarDatum = {
|
|
2
|
+
readonly label: string;
|
|
3
|
+
readonly value: number;
|
|
4
|
+
};
|
|
5
|
+
export type BarChartProps = {
|
|
6
|
+
readonly title: string;
|
|
7
|
+
readonly description?: string;
|
|
8
|
+
readonly data: readonly BarDatum[];
|
|
9
|
+
/** Column header for the value in the table view. */
|
|
10
|
+
readonly valueLabel?: string;
|
|
11
|
+
/** Formats the value at each bar's tip and in the table. */
|
|
12
|
+
readonly format?: (value: number) => string;
|
|
13
|
+
readonly className?: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* One value per category, as horizontal bars.
|
|
17
|
+
*
|
|
18
|
+
* Horizontal, not vertical, because category labels are words. Words on a
|
|
19
|
+
* vertical chart's x-axis either rotate 45°, truncate, or force the chart to be
|
|
20
|
+
* as wide as the longest name; on a horizontal chart they sit flat in a fixed
|
|
21
|
+
* gutter and stay readable at any count.
|
|
22
|
+
*
|
|
23
|
+
* Every bar is the *same* colour — slot 1. Colouring nominal bars by their value
|
|
24
|
+
* spends the identity channel re-encoding what bar length already shows, and it
|
|
25
|
+
* makes a legend necessary for information the reader already has. The bars are
|
|
26
|
+
* one series, so there is no legend either: the caption names it.
|
|
27
|
+
*
|
|
28
|
+
* The rounded end is only on the value end. A bar rounded at the baseline no
|
|
29
|
+
* longer starts at zero — it starts at a curve, and small values read as smaller
|
|
30
|
+
* than they are. `RADIUS` is also clamped to half the bar's length so a short
|
|
31
|
+
* bar becomes a stub rather than a lozenge whose length is pure geometry.
|
|
32
|
+
*
|
|
33
|
+
* The whole plot is `aria-hidden` and the table below the frame carries the
|
|
34
|
+
* numbers. An SVG full of `<rect>` elements announced individually is noise, and
|
|
35
|
+
* the exact values — which is what anyone reading non-visually wants — are in
|
|
36
|
+
* the table, in order, with headers.
|
|
37
|
+
*/
|
|
38
|
+
export declare function BarChart({ title, description, data, valueLabel, format, className }: BarChartProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { formatCompact, niceTicks, scaleLinear } from "../lib/chart-geometry.js";
|
|
3
|
+
import { seriesColor } from "../lib/chart-palette.js";
|
|
4
|
+
import { cn } from "../lib/utils.js";
|
|
5
|
+
import { ChartFrame } from "./chart-frame.js";
|
|
6
|
+
const ROW_HEIGHT = 28;
|
|
7
|
+
const BAR_HEIGHT = 20; // ≤ 24px: the band's leftover is deliberate air.
|
|
8
|
+
const LABEL_WIDTH = 120;
|
|
9
|
+
const VALUE_WIDTH = 56;
|
|
10
|
+
const RADIUS = 4;
|
|
11
|
+
/**
|
|
12
|
+
* One value per category, as horizontal bars.
|
|
13
|
+
*
|
|
14
|
+
* Horizontal, not vertical, because category labels are words. Words on a
|
|
15
|
+
* vertical chart's x-axis either rotate 45°, truncate, or force the chart to be
|
|
16
|
+
* as wide as the longest name; on a horizontal chart they sit flat in a fixed
|
|
17
|
+
* gutter and stay readable at any count.
|
|
18
|
+
*
|
|
19
|
+
* Every bar is the *same* colour — slot 1. Colouring nominal bars by their value
|
|
20
|
+
* spends the identity channel re-encoding what bar length already shows, and it
|
|
21
|
+
* makes a legend necessary for information the reader already has. The bars are
|
|
22
|
+
* one series, so there is no legend either: the caption names it.
|
|
23
|
+
*
|
|
24
|
+
* The rounded end is only on the value end. A bar rounded at the baseline no
|
|
25
|
+
* longer starts at zero — it starts at a curve, and small values read as smaller
|
|
26
|
+
* than they are. `RADIUS` is also clamped to half the bar's length so a short
|
|
27
|
+
* bar becomes a stub rather than a lozenge whose length is pure geometry.
|
|
28
|
+
*
|
|
29
|
+
* The whole plot is `aria-hidden` and the table below the frame carries the
|
|
30
|
+
* numbers. An SVG full of `<rect>` elements announced individually is noise, and
|
|
31
|
+
* the exact values — which is what anyone reading non-visually wants — are in
|
|
32
|
+
* the table, in order, with headers.
|
|
33
|
+
*/ export function BarChart({ title, description, data, valueLabel = "Value", format = formatCompact, className }) {
|
|
34
|
+
const max = Math.max(0, ...data.map((datum)=>datum.value));
|
|
35
|
+
const ticks = niceTicks(max);
|
|
36
|
+
const axisMax = ticks[ticks.length - 1] ?? 1;
|
|
37
|
+
const plotWidth = 320;
|
|
38
|
+
const height = data.length * ROW_HEIGHT;
|
|
39
|
+
const width = LABEL_WIDTH + plotWidth + VALUE_WIDTH;
|
|
40
|
+
return /*#__PURE__*/ _jsxs(ChartFrame, {
|
|
41
|
+
title: title,
|
|
42
|
+
...description === undefined ? {} : {
|
|
43
|
+
description
|
|
44
|
+
},
|
|
45
|
+
table: {
|
|
46
|
+
columns: [
|
|
47
|
+
"Category",
|
|
48
|
+
valueLabel
|
|
49
|
+
],
|
|
50
|
+
rows: data.map((datum)=>[
|
|
51
|
+
datum.label,
|
|
52
|
+
format(datum.value)
|
|
53
|
+
])
|
|
54
|
+
},
|
|
55
|
+
className: className,
|
|
56
|
+
children: [
|
|
57
|
+
/*#__PURE__*/ _jsxs("svg", {
|
|
58
|
+
viewBox: `0 0 ${width} ${height}`,
|
|
59
|
+
// Scales to the container's width; the row rhythm is what must stay
|
|
60
|
+
// fixed, and it is expressed in the viewBox.
|
|
61
|
+
className: "h-auto w-full",
|
|
62
|
+
"aria-hidden": true,
|
|
63
|
+
focusable: "false",
|
|
64
|
+
children: [
|
|
65
|
+
ticks.map((tick)=>{
|
|
66
|
+
const x = LABEL_WIDTH + scaleLinear(tick, axisMax, plotWidth);
|
|
67
|
+
return(// Hairline, solid, one step off the surface: present enough to
|
|
68
|
+
// measure against, quiet enough that the bars stay the loud thing.
|
|
69
|
+
/*#__PURE__*/ _jsx("line", {
|
|
70
|
+
x1: x,
|
|
71
|
+
y1: 0,
|
|
72
|
+
x2: x,
|
|
73
|
+
y2: height,
|
|
74
|
+
stroke: "var(--border)",
|
|
75
|
+
strokeWidth: 1
|
|
76
|
+
}, tick));
|
|
77
|
+
}),
|
|
78
|
+
data.map((datum, index)=>{
|
|
79
|
+
const y = index * ROW_HEIGHT + (ROW_HEIGHT - BAR_HEIGHT) / 2;
|
|
80
|
+
const length = scaleLinear(datum.value, axisMax, plotWidth);
|
|
81
|
+
const radius = Math.min(RADIUS, length / 2);
|
|
82
|
+
return /*#__PURE__*/ _jsxs("g", {
|
|
83
|
+
children: [
|
|
84
|
+
/*#__PURE__*/ _jsx("text", {
|
|
85
|
+
x: LABEL_WIDTH - 8,
|
|
86
|
+
y: index * ROW_HEIGHT + ROW_HEIGHT / 2,
|
|
87
|
+
textAnchor: "end",
|
|
88
|
+
dominantBaseline: "middle",
|
|
89
|
+
className: "fill-muted-foreground text-[11px]",
|
|
90
|
+
children: datum.label
|
|
91
|
+
}),
|
|
92
|
+
/*#__PURE__*/ _jsx("path", {
|
|
93
|
+
d: length <= 0 ? "" : `M${LABEL_WIDTH} ${y}` + `H${LABEL_WIDTH + length - radius}` + `a${radius} ${radius} 0 0 1 ${radius} ${radius}` + `V${y + BAR_HEIGHT - radius}` + `a${radius} ${radius} 0 0 1 ${-radius} ${radius}` + `H${LABEL_WIDTH}Z`,
|
|
94
|
+
fill: seriesColor(0)
|
|
95
|
+
}),
|
|
96
|
+
/*#__PURE__*/ _jsx("text", {
|
|
97
|
+
x: LABEL_WIDTH + length + 8,
|
|
98
|
+
y: index * ROW_HEIGHT + ROW_HEIGHT / 2,
|
|
99
|
+
dominantBaseline: "middle",
|
|
100
|
+
className: "fill-foreground text-[11px] tabular-nums",
|
|
101
|
+
children: format(datum.value)
|
|
102
|
+
})
|
|
103
|
+
]
|
|
104
|
+
}, datum.label);
|
|
105
|
+
})
|
|
106
|
+
]
|
|
107
|
+
}),
|
|
108
|
+
/*#__PURE__*/ _jsxs("p", {
|
|
109
|
+
className: cn("text-xs text-muted-foreground"),
|
|
110
|
+
children: [
|
|
111
|
+
"Axis to ",
|
|
112
|
+
format(axisMax),
|
|
113
|
+
", from zero."
|
|
114
|
+
]
|
|
115
|
+
})
|
|
116
|
+
]
|
|
117
|
+
});
|
|
118
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type BlockCardProps = {
|
|
3
|
+
readonly title: string;
|
|
4
|
+
readonly children: ReactNode;
|
|
5
|
+
readonly className?: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* The generative-UI block frame: a bordered, square-cornered card with a
|
|
9
|
+
* pulse-dot header. Zero radius is deliberate — it is what separates a
|
|
10
|
+
* generated block from the theme's otherwise-rounded surfaces, so a reader
|
|
11
|
+
* can tell "the agent made this" at a glance. Lifted out of a consumer app
|
|
12
|
+
* unchanged; see `RiskBadge` below for the tone it pairs with inside
|
|
13
|
+
* `children`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function BlockCard({ title, children, className }: BlockCardProps): import("react").JSX.Element;
|
|
16
|
+
export type RiskLevel = "low" | "medium" | "high";
|
|
17
|
+
export type RiskBadgeProps = {
|
|
18
|
+
readonly level: RiskLevel;
|
|
19
|
+
readonly label: string;
|
|
20
|
+
readonly note?: string;
|
|
21
|
+
readonly className?: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* A risk callout for a block whose action carries consequence — approve this
|
|
25
|
+
* order cancellation, run this migration. `level` drives colour only; the
|
|
26
|
+
* `label` is what actually says what the risk is, so the badge is never a
|
|
27
|
+
* bare colour dot.
|
|
28
|
+
*/
|
|
29
|
+
export declare function RiskBadge({ level, label, note, className }: RiskBadgeProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { TriangleAlert } from "lucide-react";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
/**
|
|
5
|
+
* The generative-UI block frame: a bordered, square-cornered card with a
|
|
6
|
+
* pulse-dot header. Zero radius is deliberate — it is what separates a
|
|
7
|
+
* generated block from the theme's otherwise-rounded surfaces, so a reader
|
|
8
|
+
* can tell "the agent made this" at a glance. Lifted out of a consumer app
|
|
9
|
+
* unchanged; see `RiskBadge` below for the tone it pairs with inside
|
|
10
|
+
* `children`.
|
|
11
|
+
*/ export function BlockCard({ title, children, className }) {
|
|
12
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
13
|
+
"data-slot": "block-card",
|
|
14
|
+
className: cn("overflow-hidden rounded-none border border-border bg-card", className),
|
|
15
|
+
children: [
|
|
16
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
17
|
+
"data-slot": "block-card-header",
|
|
18
|
+
className: "flex items-center gap-2 border-b border-border px-3.5 py-2.5 text-xs font-bold tracking-[0.02em] text-primary-emphasis",
|
|
19
|
+
children: [
|
|
20
|
+
/*#__PURE__*/ _jsx("span", {
|
|
21
|
+
"aria-hidden": true,
|
|
22
|
+
className: "size-1.5 shrink-0 rounded-none bg-current [animation:corbits-block-pulse_1.6s_ease_infinite]"
|
|
23
|
+
}),
|
|
24
|
+
/*#__PURE__*/ _jsx("span", {
|
|
25
|
+
"data-slot": "block-card-title",
|
|
26
|
+
children: title
|
|
27
|
+
})
|
|
28
|
+
]
|
|
29
|
+
}),
|
|
30
|
+
/*#__PURE__*/ _jsx("div", {
|
|
31
|
+
"data-slot": "block-card-body",
|
|
32
|
+
className: "p-4",
|
|
33
|
+
children: children
|
|
34
|
+
})
|
|
35
|
+
]
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const RISK_TONE_CLASS = {
|
|
39
|
+
low: "text-muted-foreground",
|
|
40
|
+
medium: "text-warn",
|
|
41
|
+
high: "text-destructive"
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* A risk callout for a block whose action carries consequence — approve this
|
|
45
|
+
* order cancellation, run this migration. `level` drives colour only; the
|
|
46
|
+
* `label` is what actually says what the risk is, so the badge is never a
|
|
47
|
+
* bare colour dot.
|
|
48
|
+
*/ export function RiskBadge({ level, label, note, className }) {
|
|
49
|
+
return /*#__PURE__*/ _jsxs("span", {
|
|
50
|
+
"data-slot": "risk-badge",
|
|
51
|
+
"data-risk": level,
|
|
52
|
+
className: cn("inline-flex items-center gap-1.5 text-xs font-bold [&_svg]:size-3.5", RISK_TONE_CLASS[level], className),
|
|
53
|
+
children: [
|
|
54
|
+
/*#__PURE__*/ _jsx(TriangleAlert, {
|
|
55
|
+
"aria-hidden": true
|
|
56
|
+
}),
|
|
57
|
+
/*#__PURE__*/ _jsx("span", {
|
|
58
|
+
children: label
|
|
59
|
+
}),
|
|
60
|
+
note === undefined ? null : /*#__PURE__*/ _jsx("span", {
|
|
61
|
+
className: "font-medium text-muted-foreground",
|
|
62
|
+
children: note
|
|
63
|
+
})
|
|
64
|
+
]
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type BootScreenProps = {
|
|
3
|
+
/** What the app is doing, in words: "Loading workbench", "Reconnecting…". */
|
|
4
|
+
readonly message: string;
|
|
5
|
+
/** Brand lockup, top left. */
|
|
6
|
+
readonly brand?: ReactNode;
|
|
7
|
+
/** Quiet corner detail — a version string, a build id. */
|
|
8
|
+
readonly footer?: ReactNode;
|
|
9
|
+
readonly className?: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* The screen before the app: brand top-left, one centered status line, a quiet
|
|
13
|
+
* corner detail. Fills its positioned parent — `BootScreen` is the surface, and
|
|
14
|
+
* whoever mounts it decides whether that is the whole viewport (initial load)
|
|
15
|
+
* or a cover over a live app (`ReconnectingOverlay`).
|
|
16
|
+
*
|
|
17
|
+
* `role="status"` with the message as the accessible name, so a screen reader
|
|
18
|
+
* announces the state change rather than the user waiting on a blank screen.
|
|
19
|
+
* No spinner: the pulsing bar is decorative and `aria-hidden`, and it collapses
|
|
20
|
+
* to a still frame under the theme's reduced-motion rule with nothing to do
|
|
21
|
+
* here.
|
|
22
|
+
*/
|
|
23
|
+
export declare function BootScreen({ message, brand, footer, className }: BootScreenProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useId } from "react";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
/**
|
|
5
|
+
* The screen before the app: brand top-left, one centered status line, a quiet
|
|
6
|
+
* corner detail. Fills its positioned parent — `BootScreen` is the surface, and
|
|
7
|
+
* whoever mounts it decides whether that is the whole viewport (initial load)
|
|
8
|
+
* or a cover over a live app (`ReconnectingOverlay`).
|
|
9
|
+
*
|
|
10
|
+
* `role="status"` with the message as the accessible name, so a screen reader
|
|
11
|
+
* announces the state change rather than the user waiting on a blank screen.
|
|
12
|
+
* No spinner: the pulsing bar is decorative and `aria-hidden`, and it collapses
|
|
13
|
+
* to a still frame under the theme's reduced-motion rule with nothing to do
|
|
14
|
+
* here.
|
|
15
|
+
*/ export function BootScreen({ message, brand, footer, className }) {
|
|
16
|
+
const messageId = useId();
|
|
17
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
18
|
+
role: "status",
|
|
19
|
+
"aria-labelledby": messageId,
|
|
20
|
+
className: cn("absolute inset-0 overflow-hidden bg-background text-foreground", className),
|
|
21
|
+
children: [
|
|
22
|
+
brand === undefined ? null : /*#__PURE__*/ _jsx("div", {
|
|
23
|
+
className: "absolute top-6 left-7",
|
|
24
|
+
children: brand
|
|
25
|
+
}),
|
|
26
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
27
|
+
className: "absolute inset-0 flex flex-col items-center justify-center gap-4 p-6",
|
|
28
|
+
children: [
|
|
29
|
+
/*#__PURE__*/ _jsx("span", {
|
|
30
|
+
"aria-hidden": true,
|
|
31
|
+
className: "h-0.5 w-24 overflow-hidden rounded-full bg-muted",
|
|
32
|
+
children: /*#__PURE__*/ _jsx("span", {
|
|
33
|
+
className: "block h-full w-1/3 rounded-full bg-primary [animation:corbits-boot-sweep_1.4s_ease-in-out_infinite]"
|
|
34
|
+
})
|
|
35
|
+
}),
|
|
36
|
+
/*#__PURE__*/ _jsx("p", {
|
|
37
|
+
id: messageId,
|
|
38
|
+
className: "max-w-[32ch] text-center text-xs leading-relaxed text-muted-foreground",
|
|
39
|
+
children: message
|
|
40
|
+
})
|
|
41
|
+
]
|
|
42
|
+
}),
|
|
43
|
+
footer === undefined ? null : /*#__PURE__*/ _jsx("div", {
|
|
44
|
+
className: "absolute bottom-6 left-7 font-mono text-[11px] text-muted-foreground",
|
|
45
|
+
children: footer
|
|
46
|
+
})
|
|
47
|
+
]
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type BulkActionBarProps = {
|
|
3
|
+
readonly count: number;
|
|
4
|
+
readonly onClear: () => void;
|
|
5
|
+
/** Action buttons, rendered after the "N selected" label — typically `Button`s. */
|
|
6
|
+
readonly children: ReactNode;
|
|
7
|
+
/** Overrides the default `"Bulk actions"` accessible name outright — for a consumer localizing this published library's one hardcoded English string. */
|
|
8
|
+
readonly label?: string;
|
|
9
|
+
readonly className?: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* The floating pill that appears once a list has a selection: fixed to the
|
|
13
|
+
* bottom of the viewport (or the nearest ancestor that establishes its own
|
|
14
|
+
* containing block — a `transform`, `filter`, or similar CSS property on an
|
|
15
|
+
* ancestor changes what `fixed` positions against), riding in with a small
|
|
16
|
+
* rise so it reads as newly arrived rather than always having been there.
|
|
17
|
+
* Renders nothing while `count` is 0 — the caller does not need to guard
|
|
18
|
+
* the render site itself.
|
|
19
|
+
*
|
|
20
|
+
* The horizontal centering (`left-1/2` plus a permanent `-translate-x-1/2`)
|
|
21
|
+
* is not part of the entrance animation and is never gated by
|
|
22
|
+
* `motion-safe:` — a `prefers-reduced-motion` viewer gets no animation at
|
|
23
|
+
* all, and if the centering correction lived only in the animation's own
|
|
24
|
+
* `transform`, that viewer would see the bar pinned at the horizontal
|
|
25
|
+
* midpoint with its left edge running off-viewport. The animation itself
|
|
26
|
+
* only touches `opacity`/`translateY`, on an inner element, so it never
|
|
27
|
+
* overwrites the outer element's static centering transform.
|
|
28
|
+
*
|
|
29
|
+
* Escape clears the selection from anywhere on the page while the bar is
|
|
30
|
+
* showing, not only while it has focus — the bar is a global consequence of
|
|
31
|
+
* list state, not a dialog the user has stepped into, so it listens on
|
|
32
|
+
* `window` rather than requiring focus to land on it first. It defers to
|
|
33
|
+
* `event.defaultPrevented`, so a dialog or input stacked above the bar that
|
|
34
|
+
* already handled its own Escape wins instead of also clearing the
|
|
35
|
+
* selection underneath it.
|
|
36
|
+
*/
|
|
37
|
+
export declare function BulkActionBar({ count, onClear, children, label, className }: BulkActionBarProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
/**
|
|
5
|
+
* The floating pill that appears once a list has a selection: fixed to the
|
|
6
|
+
* bottom of the viewport (or the nearest ancestor that establishes its own
|
|
7
|
+
* containing block — a `transform`, `filter`, or similar CSS property on an
|
|
8
|
+
* ancestor changes what `fixed` positions against), riding in with a small
|
|
9
|
+
* rise so it reads as newly arrived rather than always having been there.
|
|
10
|
+
* Renders nothing while `count` is 0 — the caller does not need to guard
|
|
11
|
+
* the render site itself.
|
|
12
|
+
*
|
|
13
|
+
* The horizontal centering (`left-1/2` plus a permanent `-translate-x-1/2`)
|
|
14
|
+
* is not part of the entrance animation and is never gated by
|
|
15
|
+
* `motion-safe:` — a `prefers-reduced-motion` viewer gets no animation at
|
|
16
|
+
* all, and if the centering correction lived only in the animation's own
|
|
17
|
+
* `transform`, that viewer would see the bar pinned at the horizontal
|
|
18
|
+
* midpoint with its left edge running off-viewport. The animation itself
|
|
19
|
+
* only touches `opacity`/`translateY`, on an inner element, so it never
|
|
20
|
+
* overwrites the outer element's static centering transform.
|
|
21
|
+
*
|
|
22
|
+
* Escape clears the selection from anywhere on the page while the bar is
|
|
23
|
+
* showing, not only while it has focus — the bar is a global consequence of
|
|
24
|
+
* list state, not a dialog the user has stepped into, so it listens on
|
|
25
|
+
* `window` rather than requiring focus to land on it first. It defers to
|
|
26
|
+
* `event.defaultPrevented`, so a dialog or input stacked above the bar that
|
|
27
|
+
* already handled its own Escape wins instead of also clearing the
|
|
28
|
+
* selection underneath it.
|
|
29
|
+
*/ export function BulkActionBar({ count, onClear, children, label = "Bulk actions", className }) {
|
|
30
|
+
useEffect(()=>{
|
|
31
|
+
if (count === 0) return;
|
|
32
|
+
function onKeyDown(event) {
|
|
33
|
+
if (event.key === "Escape" && !event.defaultPrevented) onClear();
|
|
34
|
+
}
|
|
35
|
+
window.addEventListener("keydown", onKeyDown);
|
|
36
|
+
return ()=>window.removeEventListener("keydown", onKeyDown);
|
|
37
|
+
}, [
|
|
38
|
+
count,
|
|
39
|
+
onClear
|
|
40
|
+
]);
|
|
41
|
+
if (count === 0) return null;
|
|
42
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
43
|
+
className: cn("fixed bottom-6 left-1/2 z-40 -translate-x-1/2", className),
|
|
44
|
+
children: /*#__PURE__*/ _jsxs("div", {
|
|
45
|
+
role: "group",
|
|
46
|
+
"aria-label": label,
|
|
47
|
+
className: cn("flex items-center gap-3 rounded-full border border-border bg-card px-4 py-2 shadow-lg", "motion-safe:[animation:corbits-bulk-action-bar-in_220ms_var(--ease-out)_both]"),
|
|
48
|
+
children: [
|
|
49
|
+
/*#__PURE__*/ _jsxs("span", {
|
|
50
|
+
"aria-live": "polite",
|
|
51
|
+
className: "text-sm font-medium whitespace-nowrap",
|
|
52
|
+
children: [
|
|
53
|
+
count,
|
|
54
|
+
" selected"
|
|
55
|
+
]
|
|
56
|
+
}),
|
|
57
|
+
/*#__PURE__*/ _jsx("div", {
|
|
58
|
+
className: "flex items-center gap-2",
|
|
59
|
+
children: children
|
|
60
|
+
})
|
|
61
|
+
]
|
|
62
|
+
})
|
|
63
|
+
});
|
|
64
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type VariantProps } from "class-variance-authority";
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
declare const buttonVariantsBase: (props?: ({
|
|
4
|
+
variant?: "link" | "primary" | "secondary" | "outline" | "ghost" | "destructive" | null | undefined;
|
|
5
|
+
size?: "sm" | "md" | "lg" | "icon" | null | undefined;
|
|
6
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
|
+
declare function buttonVariants(props?: VariantProps<typeof buttonVariantsBase>): string;
|
|
8
|
+
export type ButtonProps = React.ComponentProps<"button"> & VariantProps<typeof buttonVariantsBase> & {
|
|
9
|
+
asChild?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function Button({ className, variant, size, asChild, ...props }: ButtonProps): React.JSX.Element;
|
|
12
|
+
export { buttonVariants };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { twMerge } from "tailwind-merge";
|
|
5
|
+
import { cn } from "../lib/utils.js";
|
|
6
|
+
// The focus ring lives in the theme's base layer, not here — one ring for the
|
|
7
|
+
// whole app, declared once.
|
|
8
|
+
//
|
|
9
|
+
// NOTE FOR THE NEXT IMPLEMENTER: DESIGN.md:377 specifies a white label on the
|
|
10
|
+
// orange primary. That measures 2.69:1, a hard AA failure. `text-primary-
|
|
11
|
+
// foreground` resolves to ink in light (5.53:1) and black in dark (5.34:1),
|
|
12
|
+
// which is correct. DESIGN.md is wrong; do not "fix" this back.
|
|
13
|
+
const buttonVariantsBase = cva(// active:brightness-95 is the press state for every variant: it darkens the
|
|
14
|
+
// rendered fill whatever that fill is, so it needs no per-variant pressed
|
|
15
|
+
// token. Deliberately outside `transition-colors` — a press should land on
|
|
16
|
+
// the frame the pointer goes down, not ease in. The scale feedback gets its
|
|
17
|
+
// own `transition-transform` for the same reason: sharing `transition-colors`
|
|
18
|
+
// would delay the press-down scale behind the color easing.
|
|
19
|
+
"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-colors active:brightness-95 motion-safe:transition-transform motion-safe:duration-100 motion-safe:active:scale-[0.97] disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0", {
|
|
20
|
+
variants: {
|
|
21
|
+
variant: {
|
|
22
|
+
primary: "bg-primary text-primary-foreground hover:bg-primary-active",
|
|
23
|
+
secondary: "bg-secondary text-secondary-foreground hover:opacity-90",
|
|
24
|
+
outline: "border border-input bg-transparent hover:bg-muted",
|
|
25
|
+
ghost: "hover:bg-muted",
|
|
26
|
+
link: "text-primary-emphasis underline-offset-4 hover:underline",
|
|
27
|
+
destructive: "bg-destructive text-destructive-foreground hover:opacity-90"
|
|
28
|
+
},
|
|
29
|
+
size: {
|
|
30
|
+
sm: "h-8 px-3",
|
|
31
|
+
md: "h-9 px-4",
|
|
32
|
+
lg: "h-10 px-6",
|
|
33
|
+
icon: "size-9"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
// link reads as inline text, not a control — it opts out of every size's
|
|
37
|
+
// fixed height/padding rather than getting a box of its own.
|
|
38
|
+
compoundVariants: [
|
|
39
|
+
{
|
|
40
|
+
variant: "link",
|
|
41
|
+
size: [
|
|
42
|
+
"sm",
|
|
43
|
+
"md",
|
|
44
|
+
"lg",
|
|
45
|
+
"icon"
|
|
46
|
+
],
|
|
47
|
+
class: "h-auto px-0"
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
defaultVariants: {
|
|
51
|
+
variant: "primary",
|
|
52
|
+
size: "md"
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
// cva concatenates variant, size, and compound-variant classes as plain
|
|
56
|
+
// strings with no dedup, so the compound override above only wins once
|
|
57
|
+
// tailwind-merge resolves the h-*/px-* conflicts — hence the wrapper instead
|
|
58
|
+
// of exporting buttonVariantsBase directly.
|
|
59
|
+
function buttonVariants(props) {
|
|
60
|
+
return twMerge(buttonVariantsBase(props));
|
|
61
|
+
}
|
|
62
|
+
export function Button({ className, variant, size, asChild = false, ...props }) {
|
|
63
|
+
const Comp = asChild ? Slot : "button";
|
|
64
|
+
return /*#__PURE__*/ _jsx(Comp, {
|
|
65
|
+
"data-slot": "button",
|
|
66
|
+
className: cn(buttonVariants({
|
|
67
|
+
variant,
|
|
68
|
+
size
|
|
69
|
+
}), className),
|
|
70
|
+
...props
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export { buttonVariants };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
export declare function Card({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
3
|
+
export declare function CardHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
4
|
+
export declare function CardTitle({ className, ...props }: React.ComponentProps<"h3">): React.JSX.Element;
|
|
5
|
+
export declare function CardDescription({ className, ...props }: React.ComponentProps<"p">): React.JSX.Element;
|
|
6
|
+
export declare function CardFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|