@tangle-network/ui 11.12.0 → 11.13.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/CHANGELOG.md +17 -0
- package/dist/_virtual/_rolldown/runtime.js +13 -0
- package/dist/auth/auth.d.ts +53 -0
- package/dist/{auth-DOETfnd9.js → auth/auth.js} +7 -37
- package/dist/auth/login-layout.d.ts +20 -0
- package/dist/auth/login-layout.js +36 -0
- package/dist/auth.d.ts +2 -1
- package/dist/auth.js +2 -1
- package/dist/chat/agent-timeline.d.ts +83 -0
- package/dist/chat/agent-timeline.js +236 -0
- package/dist/chat/chat-container.d.ts +51 -0
- package/dist/chat/chat-container.js +309 -0
- package/dist/chat/chat-message.d.ts +22 -0
- package/dist/chat/chat-message.js +38 -0
- package/dist/chat/message-list.d.ts +32 -0
- package/dist/chat/message-list.js +37 -0
- package/dist/chat/thinking-indicator.d.ts +6 -0
- package/dist/chat/thinking-indicator.js +38 -0
- package/dist/chat/user-message.d.ts +21 -0
- package/dist/chat/user-message.js +42 -0
- package/dist/chat.d.ts +6 -1
- package/dist/chat.js +6 -1
- package/dist/editor/document-editor-pane.d.ts +37 -0
- package/dist/editor/document-editor-pane.js +199 -0
- package/dist/editor/editor-lazy.js +37 -0
- package/dist/editor/editor-loading.js +21 -0
- package/dist/editor/editor-peers.d.ts +6 -0
- package/dist/editor/editor-peers.js +155 -0
- package/dist/editor/editor-provider.d.ts +97 -0
- package/dist/editor/editor-provider.js +253 -0
- package/dist/editor/editor-toolbar.d.ts +11 -0
- package/dist/editor/editor-toolbar.js +106 -0
- package/dist/editor/markdown-conversion.js +23 -0
- package/dist/editor/markdown-document-editor.js +103 -0
- package/dist/editor/tiptap-editor.d.ts +45 -0
- package/dist/editor/tiptap-editor.js +271 -0
- package/dist/editor/use-editor.d.ts +67 -0
- package/dist/editor/use-editor.js +158 -0
- package/dist/editor.d.ts +6 -119
- package/dist/editor.js +5 -1
- package/dist/files/file-artifact-pane.d.ts +47 -0
- package/dist/files/file-artifact-pane.js +114 -0
- package/dist/files/file-format.d.ts +51 -0
- package/dist/{file-format-BgXbr-Ib.js → files/file-format.js} +1 -1
- package/dist/files/file-preview.d.ts +27 -0
- package/dist/files/file-preview.js +359 -0
- package/dist/files/file-tabs.d.ts +19 -0
- package/dist/files/file-tabs.js +48 -0
- package/dist/files/file-tree.d.ts +31 -0
- package/dist/files/file-tree.js +135 -0
- package/dist/files/rich-file-tree.d.ts +62 -0
- package/dist/files/rich-file-tree.js +122 -0
- package/dist/files.d.ts +6 -1
- package/dist/files.js +6 -2
- package/dist/hooks/use-auth.d.ts +40 -0
- package/dist/hooks/use-auth.js +91 -0
- package/dist/hooks/use-auto-scroll.d.ts +11 -0
- package/dist/hooks/use-auto-scroll.js +48 -0
- package/dist/hooks/use-dropdown-menu.d.ts +12 -0
- package/dist/hooks/use-dropdown-menu.js +31 -0
- package/dist/hooks/use-live-time.d.ts +7 -0
- package/dist/hooks/use-live-time.js +22 -0
- package/dist/hooks/use-realtime-session.d.ts +32 -0
- package/dist/hooks/use-realtime-session.js +235 -0
- package/dist/hooks/use-run-collapse-state.d.ts +13 -0
- package/dist/hooks/use-run-collapse-state.js +24 -0
- package/dist/hooks/use-run-groups.d.ts +11 -0
- package/dist/hooks/use-run-groups.js +85 -0
- package/dist/hooks/use-sdk-session.d.ts +59 -0
- package/dist/hooks/use-sdk-session.js +322 -0
- package/dist/hooks/use-sse-stream.d.ts +124 -0
- package/dist/hooks/use-sse-stream.js +229 -0
- package/dist/hooks/use-tool-call-stream.d.ts +23 -0
- package/dist/hooks/use-tool-call-stream.js +75 -0
- package/dist/hooks.d.ts +11 -49
- package/dist/hooks.js +10 -110
- package/dist/index.d.ts +99 -25
- package/dist/index.js +86 -36
- package/dist/lib/focus.d.ts +55 -0
- package/dist/{focus-BcE_SHqz.js → lib/focus.js} +1 -1
- package/dist/lib/utils.d.ts +4 -0
- package/dist/{utils-C_uf36nf.js → lib/utils.js} +1 -1
- package/dist/{code-block-B9miUPht.d.ts → markdown/code-block.d.ts} +5 -6
- package/dist/{code-block-B36KVUSN.js → markdown/code-block.js} +22 -47
- package/dist/{markdown-C0atSabr.d.ts → markdown/markdown.d.ts} +4 -5
- package/dist/{markdown-Db08KAYF.js → markdown/markdown.js} +3 -3
- package/dist/markdown.d.ts +2 -2
- package/dist/markdown.js +2 -2
- package/dist/{openui-artifact-renderer-fkBQaoLO.d.ts → openui/openui-artifact-renderer.d.ts} +20 -21
- package/dist/{openui-artifact-renderer-DG_i2lOR.js → openui/openui-artifact-renderer.js} +9 -7
- package/dist/{schema-BpW9rSSG.js → openui/schema-compiled.js} +2 -354
- package/dist/{schema-DAstk-J4.d.ts → openui/schema-data.d.ts} +3 -24
- package/dist/openui/schema-data.js +206 -0
- package/dist/openui-schema.d.ts +22 -3
- package/dist/openui-schema.js +138 -1
- package/dist/openui.d.ts +3 -2
- package/dist/openui.js +3 -2
- package/dist/{artifact-pane-BL_DFWNl.d.ts → primitives/artifact-pane.d.ts} +3 -4
- package/dist/{artifact-pane-DMXT9a8U.js → primitives/artifact-pane.js} +2 -2
- package/dist/primitives/avatar.d.ts +8 -0
- package/dist/primitives/avatar.js +25 -0
- package/dist/primitives/badge.d.ts +22 -0
- package/dist/primitives/badge.js +47 -0
- package/dist/{button-yoIv77e2.d.ts → primitives/button.d.ts} +2 -2
- package/dist/primitives/button.js +113 -0
- package/dist/primitives/card.d.ts +18 -0
- package/dist/primitives/card.js +52 -0
- package/dist/primitives/code-block.d.ts +6 -0
- package/dist/primitives/code-block.js +13 -0
- package/dist/primitives/dialog.d.ts +23 -0
- package/dist/primitives/dialog.js +59 -0
- package/dist/primitives/drop-zone.d.ts +23 -0
- package/dist/primitives/drop-zone.js +104 -0
- package/dist/primitives/dropdown-menu.d.ts +29 -0
- package/dist/{dropdown-menu-BpcLa5vF.js → primitives/dropdown-menu.js} +2 -23
- package/dist/primitives/empty-state.d.ts +11 -0
- package/dist/primitives/empty-state.js +32 -0
- package/dist/primitives/heading.d.ts +23 -0
- package/dist/primitives/heading.js +38 -0
- package/dist/primitives/input.d.ts +23 -0
- package/dist/primitives/input.js +110 -0
- package/dist/primitives/label.d.ts +6 -0
- package/dist/primitives/label.js +16 -0
- package/dist/primitives/metric-strip.d.ts +48 -0
- package/dist/primitives/metric-strip.js +52 -0
- package/dist/primitives/page-header.d.ts +25 -0
- package/dist/primitives/page-header.js +47 -0
- package/dist/primitives/page-shell.d.ts +14 -0
- package/dist/primitives/page-shell.js +17 -0
- package/dist/primitives/progress.d.ts +9 -0
- package/dist/primitives/progress.js +29 -0
- package/dist/primitives/segmented-control.d.ts +40 -0
- package/dist/primitives/segmented-control.js +57 -0
- package/dist/primitives/select.d.ts +15 -0
- package/dist/primitives/select.js +74 -0
- package/dist/primitives/sidebar-drop-zone.d.ts +21 -0
- package/dist/primitives/sidebar-drop-zone.js +88 -0
- package/dist/primitives/skeleton.d.ts +10 -0
- package/dist/primitives/skeleton.js +50 -0
- package/dist/primitives/stat-card.d.ts +16 -0
- package/dist/primitives/stat-card.js +60 -0
- package/dist/primitives/status-pill.d.ts +44 -0
- package/dist/primitives/status-pill.js +119 -0
- package/dist/primitives/switch.d.ts +6 -0
- package/dist/primitives/switch.js +16 -0
- package/dist/primitives/table.d.ts +22 -0
- package/dist/primitives/table.js +63 -0
- package/dist/primitives/tabs.d.ts +13 -0
- package/dist/{tabs-BVHR6NkO.js → primitives/tabs.js} +3 -3
- package/dist/primitives/terminal-display.d.ts +25 -0
- package/dist/primitives/terminal-display.js +109 -0
- package/dist/primitives/text-shimmer.d.ts +16 -0
- package/dist/{text-shimmer-BUUo6I-g.js → primitives/text-shimmer.js} +2 -2
- package/dist/primitives/theme-toggle.d.ts +11 -0
- package/dist/primitives/theme-toggle.js +154 -0
- package/dist/primitives/toast.d.ts +29 -0
- package/dist/primitives/toast.js +142 -0
- package/dist/primitives/toolbar.d.ts +55 -0
- package/dist/primitives/toolbar.js +37 -0
- package/dist/primitives/upload-progress.d.ts +23 -0
- package/dist/primitives/upload-progress.js +72 -0
- package/dist/primitives.d.ts +35 -5
- package/dist/primitives.js +35 -9
- package/dist/{index-COgqsFAQ.d.ts → redaction/redacted-document.d.ts} +6 -7
- package/dist/{redaction-CGGB2ENG.js → redaction/redacted-document.js} +2 -2
- package/dist/redaction.d.ts +1 -1
- package/dist/redaction.js +1 -1
- package/dist/run/expandable-content.js +41 -0
- package/dist/run/expanded-tool-detail.d.ts +12 -0
- package/dist/run/expanded-tool-detail.js +163 -0
- package/dist/run/inline-thinking-item.d.ts +11 -0
- package/dist/run/inline-thinking-item.js +60 -0
- package/dist/run/inline-tool-item.d.ts +25 -0
- package/dist/run/inline-tool-item.js +63 -0
- package/dist/run/run-group.d.ts +26 -0
- package/dist/{run-group-BWGeesgs.js → run/run-group.js} +9 -60
- package/dist/run/run-item-primitives.d.ts +5 -0
- package/dist/{run-ZsIKPA6J.js → run/run-item-primitives.js} +2 -2
- package/dist/run/run-row-shell.d.ts +53 -0
- package/dist/run/run-row-shell.js +114 -0
- package/dist/{tool-call-feed-BeB1_4pX.d.ts → run/tool-call-feed.d.ts} +7 -12
- package/dist/{tool-call-feed-BpmMLpPX.js → run/tool-call-feed.js} +4 -4
- package/dist/run/tool-call-step.d.ts +5 -0
- package/dist/run/tool-call-step.js +82 -0
- package/dist/run.d.ts +8 -2
- package/dist/run.js +7 -4
- package/dist/sdk-hooks.d.ts +8 -1
- package/dist/sdk-hooks.js +8 -2
- package/dist/stores/active-sessions-store.d.ts +83 -0
- package/dist/{active-sessions-store-Gt1R0rFG.js → stores/active-sessions-store.js} +1 -1
- package/dist/stores/chat-store.d.ts +14 -0
- package/dist/stores/chat-store.js +33 -0
- package/dist/stores.d.ts +3 -16
- package/dist/stores.js +2 -33
- package/dist/tool-previews/command-preview.d.ts +11 -0
- package/dist/tool-previews/command-preview.js +91 -0
- package/dist/tool-previews/diff-preview.d.ts +7 -0
- package/dist/tool-previews/diff-preview.js +76 -0
- package/dist/tool-previews/glob-results-preview.d.ts +7 -0
- package/dist/tool-previews/glob-results-preview.js +53 -0
- package/dist/tool-previews/grep-results-preview.d.ts +7 -0
- package/dist/tool-previews/grep-results-preview.js +99 -0
- package/dist/tool-previews/preview-primitives.js +58 -0
- package/dist/tool-previews/question-preview.d.ts +7 -0
- package/dist/tool-previews/question-preview.js +63 -0
- package/dist/tool-previews/web-search-preview.d.ts +7 -0
- package/dist/tool-previews/web-search-preview.js +78 -0
- package/dist/tool-previews/write-file-preview.d.ts +11 -0
- package/dist/tool-previews/write-file-preview.js +52 -0
- package/dist/tool-previews.d.ts +7 -1
- package/dist/tool-previews.js +7 -1
- package/dist/{branding-26d5ARIr.d.ts → types/branding.d.ts} +2 -3
- package/dist/{message-vhUI0c5G.d.ts → types/message.d.ts} +2 -3
- package/dist/{parts-5mhy9yJ3.d.ts → types/parts.d.ts} +8 -9
- package/dist/{run-OyWmIDqu.d.ts → types/run.d.ts} +9 -10
- package/dist/{tool-display-DgmL5rIU.d.ts → types/tool-display.d.ts} +5 -6
- package/dist/types.d.ts +5 -5
- package/dist/utils/copy-text.d.ts +7 -0
- package/dist/{utils-Bl_CZ0Qp.js → utils/copy-text.js} +1 -11
- package/dist/utils/format.d.ts +22 -0
- package/dist/{format-DfcLWqMa.js → utils/format.js} +1 -1
- package/dist/utils/time-ago.d.ts +3 -0
- package/dist/utils/time-ago.js +11 -0
- package/dist/utils/tool-display.d.ts +16 -0
- package/dist/{tool-display-BAuAVa43.js → utils/tool-display.js} +1 -1
- package/dist/utils.d.ts +6 -1
- package/dist/utils.js +6 -5
- package/package.json +2 -2
- package/src/files/rich-file-tree.stories.tsx +1 -1
- package/src/files/rich-file-tree.tsx +12 -9
- package/src/markdown/code-block.test.tsx +46 -2
- package/src/markdown/code-block.tsx +23 -60
- package/src/primitives/button.stories.tsx +37 -1
- package/src/primitives/button.test.tsx +230 -0
- package/src/primitives/button.tsx +62 -14
- package/src/primitives/card-anatomy.stories.tsx +51 -0
- package/src/primitives/card.test.tsx +71 -0
- package/src/primitives/card.tsx +25 -13
- package/src/primitives/heading.stories.tsx +35 -0
- package/src/primitives/heading.test.tsx +56 -0
- package/src/primitives/heading.tsx +76 -0
- package/src/primitives/index.ts +8 -0
- package/src/primitives/input.stories.tsx +28 -0
- package/src/primitives/input.test.tsx +161 -0
- package/src/primitives/input.tsx +34 -28
- package/src/primitives/page-header.stories.tsx +51 -0
- package/src/primitives/page-header.test.tsx +48 -0
- package/src/primitives/page-header.tsx +35 -35
- package/src/primitives/page-shell.stories.tsx +89 -0
- package/src/primitives/page-shell.test.tsx +24 -0
- package/src/primitives/page-shell.tsx +26 -0
- package/src/primitives/table-wrapper.stories.tsx +54 -0
- package/src/primitives/table.test.tsx +72 -0
- package/src/primitives/table.tsx +33 -12
- package/src/primitives/theme-toggle.test.tsx +167 -0
- package/src/primitives/theme-toggle.tsx +111 -39
- package/dist/active-sessions-store-nl70ZnMC.d.ts +0 -84
- package/dist/button-C87nEe2R.js +0 -78
- package/dist/chat-DDvF2oGQ.js +0 -672
- package/dist/diff-preview-BcK3nRXb.js +0 -533
- package/dist/document-editor-pane-BilJfiCo.d.ts +0 -136
- package/dist/document-editor-pane-POsq5Q0o.js +0 -1279
- package/dist/files-hywocVZz.js +0 -753
- package/dist/index-2UntyjBb.d.ts +0 -52
- package/dist/index-BgUHilZm.d.ts +0 -108
- package/dist/index-CJ8wRfiC.d.ts +0 -236
- package/dist/index-CPDET_s1.d.ts +0 -549
- package/dist/index-Cg6gYAqh.d.ts +0 -73
- package/dist/index-Cs7j8V7l.d.ts +0 -203
- package/dist/index-DjKxO357.d.ts +0 -128
- package/dist/primitives-Bx59fT-U.js +0 -1239
- package/dist/table-Bss73DhB.js +0 -146
- package/dist/tool-call-step-DNebqhmX.js +0 -434
- package/dist/use-auto-scroll-C5Pjeqon.js +0 -153
- package/dist/use-sse-stream-DjqRuDft.js +0 -884
- package/dist/use-sse-stream-Dm6R5YWz.d.ts +0 -282
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
|
3
|
+
//#region src/primitives/progress.d.ts
|
|
4
|
+
declare const Progress: React.ForwardRefExoticComponent<Omit<ProgressPrimitive.ProgressProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
5
|
+
variant?: "default" | "sandbox";
|
|
6
|
+
showValue?: boolean;
|
|
7
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { Progress };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import * as ProgressPrimitive from "@radix-ui/react-progress";
|
|
5
|
+
//#region src/primitives/progress.tsx
|
|
6
|
+
const Progress = React.forwardRef(({ className, value, variant = "default", showValue = false, ...props }, ref) => {
|
|
7
|
+
const indicatorVariants = {
|
|
8
|
+
default: "bg-primary",
|
|
9
|
+
sandbox: "bg-[image:var(--accent-gradient-strong)]"
|
|
10
|
+
};
|
|
11
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
12
|
+
className: "relative",
|
|
13
|
+
children: [/* @__PURE__ */ jsx(ProgressPrimitive.Root, {
|
|
14
|
+
ref,
|
|
15
|
+
className: cn("relative h-2 w-full overflow-hidden rounded-full bg-muted", className),
|
|
16
|
+
...props,
|
|
17
|
+
children: /* @__PURE__ */ jsx(ProgressPrimitive.Indicator, {
|
|
18
|
+
className: cn("h-full w-full flex-1 transition-all duration-300 ease-out", indicatorVariants[variant]),
|
|
19
|
+
style: { transform: `translateX(-${100 - (value || 0)}%)` }
|
|
20
|
+
})
|
|
21
|
+
}), showValue && /* @__PURE__ */ jsxs("span", {
|
|
22
|
+
className: "absolute -top-6 right-0 text-muted-foreground text-xs",
|
|
23
|
+
children: [value, "%"]
|
|
24
|
+
})]
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
Progress.displayName = ProgressPrimitive.Root.displayName;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { Progress };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
//#region src/primitives/segmented-control.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Visually lightweight segmented control for single-value selection.
|
|
5
|
+
*
|
|
6
|
+
* Uses role="radiogroup" / role="radio" because this is a value-selector
|
|
7
|
+
* with no associated panels — not a tab interface. Arrow keys navigate
|
|
8
|
+
* between options (with wrapping), Home/End jump to first/last.
|
|
9
|
+
*
|
|
10
|
+
* **Accessibility:** Provide either `aria-label` or `aria-labelledby` — the
|
|
11
|
+
* ARIA spec requires every radiogroup to have an accessible name. A dev-mode
|
|
12
|
+
* console warning fires when both are omitted.
|
|
13
|
+
*
|
|
14
|
+
* Design rules baked into the default variant:
|
|
15
|
+
* - Only the SELECTED segment shows a surface colour + accent text.
|
|
16
|
+
* - Unselected segments have NO background — they're plain-text labels
|
|
17
|
+
* that darken on hover. This keeps the selected segment as the
|
|
18
|
+
* single visual anchor instead of the whole group competing with
|
|
19
|
+
* itself.
|
|
20
|
+
*/
|
|
21
|
+
export interface SegmentedControlOption<T extends string = string> {
|
|
22
|
+
value: T;
|
|
23
|
+
label: React.ReactNode;
|
|
24
|
+
/** Rendered right of the label, typically a count or status pill. */
|
|
25
|
+
adornment?: React.ReactNode;
|
|
26
|
+
}
|
|
27
|
+
export interface SegmentedControlProps<T extends string = string> extends Pick<React.HTMLAttributes<HTMLDivElement>, "id" | "className" | "aria-label" | "aria-labelledby"> {
|
|
28
|
+
value: T;
|
|
29
|
+
onValueChange: (value: T) => void;
|
|
30
|
+
options: SegmentedControlOption<T>[];
|
|
31
|
+
/**
|
|
32
|
+
* Layout:
|
|
33
|
+
* - "row" — horizontal pill bar (default, fits in a header region)
|
|
34
|
+
* - "tabs" — horizontal with a bottom border so the selected pill
|
|
35
|
+
* reads as a classic tab (used on the Team page)
|
|
36
|
+
*/
|
|
37
|
+
variant?: "row" | "tabs";
|
|
38
|
+
}
|
|
39
|
+
export declare function SegmentedControl<T extends string = string>({ value, onValueChange, options, variant, className, ...rest }: SegmentedControlProps<T>): React.JSX.Element;
|
|
40
|
+
//#endregion
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { focusRing, focusRingInset } from "../lib/focus.js";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/primitives/segmented-control.tsx
|
|
7
|
+
function SegmentedControl({ value, onValueChange, options, variant = "row", className, ...rest }) {
|
|
8
|
+
if (typeof process !== "undefined" && process?.env?.NODE_ENV !== "production" && !rest["aria-label"] && !rest["aria-labelledby"]) console.warn("[SegmentedControl] role=\"radiogroup\" requires either aria-label or aria-labelledby for accessibility.");
|
|
9
|
+
const optionRefs = React.useRef(/* @__PURE__ */ new Map());
|
|
10
|
+
const hasMatch = options.some((o) => o.value === value);
|
|
11
|
+
const handleKeyDown = (e) => {
|
|
12
|
+
if (options.length === 0) return;
|
|
13
|
+
let idx = options.findIndex((o) => o.value === value);
|
|
14
|
+
if (idx === -1) idx = 0;
|
|
15
|
+
let next;
|
|
16
|
+
if (e.key === "ArrowRight" || e.key === "ArrowDown") next = (idx + 1) % options.length;
|
|
17
|
+
else if (e.key === "ArrowLeft" || e.key === "ArrowUp") next = (idx - 1 + options.length) % options.length;
|
|
18
|
+
else if (e.key === "Home") next = 0;
|
|
19
|
+
else if (e.key === "End") next = options.length - 1;
|
|
20
|
+
if (next !== void 0) {
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
if (options[next].value !== value) onValueChange(options[next].value);
|
|
23
|
+
optionRefs.current.get(options[next].value)?.focus();
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
return /* @__PURE__ */ jsx("div", {
|
|
27
|
+
role: "radiogroup",
|
|
28
|
+
id: rest.id,
|
|
29
|
+
"aria-label": rest["aria-label"],
|
|
30
|
+
"aria-labelledby": rest["aria-labelledby"],
|
|
31
|
+
onKeyDown: handleKeyDown,
|
|
32
|
+
className: cn("flex gap-1", variant === "row" && "flex-wrap items-center rounded-lg border border-border bg-card p-1", variant === "tabs" && "flex-nowrap items-end border-b border-border pb-0 overflow-x-auto", className),
|
|
33
|
+
children: options.map((option, i) => {
|
|
34
|
+
const active = option.value === value;
|
|
35
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
36
|
+
ref: (el) => {
|
|
37
|
+
if (el) optionRefs.current.set(option.value, el);
|
|
38
|
+
else optionRefs.current.delete(option.value);
|
|
39
|
+
},
|
|
40
|
+
type: "button",
|
|
41
|
+
role: "radio",
|
|
42
|
+
"aria-checked": active,
|
|
43
|
+
tabIndex: active || !hasMatch && i === 0 ? 0 : -1,
|
|
44
|
+
onClick: () => {
|
|
45
|
+
if (!active) onValueChange(option.value);
|
|
46
|
+
},
|
|
47
|
+
className: cn("relative inline-flex items-center gap-2 whitespace-nowrap text-sm transition-[color,background-color,border-color,box-shadow]", variant === "row" && ["rounded-md px-3 py-1.5", focusRing], variant === "tabs" && ["rounded-none border-b-2 -mb-px px-4 py-2", focusRingInset], active ? variant === "row" ? "border border-transparent bg-[var(--accent-surface)] text-[var(--accent-text)] font-semibold" : "border-[var(--accent-text)] text-[var(--accent-text)] font-semibold" : variant === "row" ? "border border-transparent text-muted-foreground hover:text-foreground" : "border-transparent text-muted-foreground hover:text-foreground"),
|
|
48
|
+
children: [/* @__PURE__ */ jsx("span", { children: option.label }), option.adornment && /* @__PURE__ */ jsx("span", {
|
|
49
|
+
className: cn("text-xs", active ? "text-[var(--accent-text)]/80" : "text-muted-foreground"),
|
|
50
|
+
children: option.adornment
|
|
51
|
+
})]
|
|
52
|
+
}, option.value);
|
|
53
|
+
})
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { SegmentedControl };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
3
|
+
//#region src/primitives/select.d.ts
|
|
4
|
+
declare const Select: React.FC<SelectPrimitive.SelectProps>;
|
|
5
|
+
declare const SelectGroup: React.ForwardRefExoticComponent<SelectPrimitive.SelectGroupProps & React.RefAttributes<HTMLDivElement>>;
|
|
6
|
+
declare const SelectValue: React.ForwardRefExoticComponent<SelectPrimitive.SelectValueProps & React.RefAttributes<HTMLSpanElement>>;
|
|
7
|
+
declare const SelectTrigger: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
8
|
+
declare const SelectScrollUpButton: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectScrollUpButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
9
|
+
declare const SelectScrollDownButton: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectScrollDownButtonProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
10
|
+
declare const SelectContent: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
11
|
+
declare const SelectLabel: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectLabelProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
12
|
+
declare const SelectItem: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
13
|
+
declare const SelectSeparator: React.ForwardRefExoticComponent<Omit<SelectPrimitive.SelectSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { focusField } from "../lib/focus.js";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
|
7
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
8
|
+
//#region src/primitives/select.tsx
|
|
9
|
+
const Select = SelectPrimitive.Root;
|
|
10
|
+
const SelectGroup = SelectPrimitive.Group;
|
|
11
|
+
const SelectValue = SelectPrimitive.Value;
|
|
12
|
+
const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(SelectPrimitive.Trigger, {
|
|
13
|
+
ref,
|
|
14
|
+
className: cn("flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border bg-card px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", focusField, className),
|
|
15
|
+
...props,
|
|
16
|
+
children: [children, /* @__PURE__ */ jsx(SelectPrimitive.Icon, {
|
|
17
|
+
asChild: true,
|
|
18
|
+
children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 opacity-50" })
|
|
19
|
+
})]
|
|
20
|
+
}));
|
|
21
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
22
|
+
const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.ScrollUpButton, {
|
|
23
|
+
ref,
|
|
24
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
25
|
+
...props,
|
|
26
|
+
children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" })
|
|
27
|
+
}));
|
|
28
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
29
|
+
const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.ScrollDownButton, {
|
|
30
|
+
ref,
|
|
31
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
32
|
+
...props,
|
|
33
|
+
children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
|
|
34
|
+
}));
|
|
35
|
+
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
36
|
+
const SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(SelectPrimitive.Content, {
|
|
37
|
+
ref,
|
|
38
|
+
className: cn("data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border border-border bg-card text-foreground shadow-[var(--shadow-card)] data-[state=closed]:animate-out data-[state=open]:animate-in", position === "popper" && "data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1", className),
|
|
39
|
+
position,
|
|
40
|
+
...props,
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ jsx(SelectScrollUpButton, {}),
|
|
43
|
+
/* @__PURE__ */ jsx(SelectPrimitive.Viewport, {
|
|
44
|
+
className: cn("p-1", position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"),
|
|
45
|
+
children
|
|
46
|
+
}),
|
|
47
|
+
/* @__PURE__ */ jsx(SelectScrollDownButton, {})
|
|
48
|
+
]
|
|
49
|
+
}) }));
|
|
50
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
51
|
+
const SelectLabel = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Label, {
|
|
52
|
+
ref,
|
|
53
|
+
className: cn("px-2 py-1.5 font-semibold text-sm", className),
|
|
54
|
+
...props
|
|
55
|
+
}));
|
|
56
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
57
|
+
const SelectItem = React.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(SelectPrimitive.Item, {
|
|
58
|
+
ref,
|
|
59
|
+
className: cn("relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pr-8 pl-2 text-sm outline-none focus:bg-muted/50 focus:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className),
|
|
60
|
+
...props,
|
|
61
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
62
|
+
className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center",
|
|
63
|
+
children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) })
|
|
64
|
+
}), /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })]
|
|
65
|
+
}));
|
|
66
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
67
|
+
const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Separator, {
|
|
68
|
+
ref,
|
|
69
|
+
className: cn("-mx-1 my-1 h-px bg-border", className),
|
|
70
|
+
...props
|
|
71
|
+
}));
|
|
72
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
73
|
+
//#endregion
|
|
74
|
+
export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
//#region src/primitives/sidebar-drop-zone.d.ts
|
|
3
|
+
export interface SidebarDropZoneProps {
|
|
4
|
+
/** Called with dropped files */
|
|
5
|
+
onDrop: (files: File[]) => void;
|
|
6
|
+
/** Accepted file types (e.g. ".pdf,.csv") */
|
|
7
|
+
accept?: string;
|
|
8
|
+
/** Whether drop zone is active */
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
/** Title shown in the drop zone */
|
|
11
|
+
title?: string;
|
|
12
|
+
/** Description shown below the title */
|
|
13
|
+
description?: string;
|
|
14
|
+
/** Custom icon (replaces default upload icon) */
|
|
15
|
+
icon?: ReactNode;
|
|
16
|
+
/** Always visible (not just on drag). Useful as a persistent upload area. */
|
|
17
|
+
persistent?: boolean;
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function SidebarDropZone({ onDrop, accept, disabled, title, description, icon, persistent, className }: SidebarDropZoneProps): import("react").JSX.Element;
|
|
21
|
+
//#endregion
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { useCallback, useRef, useState } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Upload } from "lucide-react";
|
|
5
|
+
//#region src/primitives/sidebar-drop-zone.tsx
|
|
6
|
+
/**
|
|
7
|
+
* SidebarDropZone — compact scoped drop zone for sidebar panels and narrow containers.
|
|
8
|
+
*
|
|
9
|
+
* Unlike the full-window DropZone, this renders as an inline element
|
|
10
|
+
* that can be placed inside sidebar panels, file trees, or any container.
|
|
11
|
+
* Shows a subtle drop target when files are dragged over it.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* <SidebarPanelContent>
|
|
15
|
+
* <FileTree ... />
|
|
16
|
+
* <SidebarDropZone onDrop={uploadToWorkspace} title="Upload to workspace" />
|
|
17
|
+
* </SidebarPanelContent>
|
|
18
|
+
*/
|
|
19
|
+
function SidebarDropZone({ onDrop, accept, disabled, title = "Drop files here", description, icon, persistent = false, className }) {
|
|
20
|
+
const [dragOver, setDragOver] = useState(false);
|
|
21
|
+
const counter = useRef(0);
|
|
22
|
+
const isAccepted = useCallback((file) => {
|
|
23
|
+
if (!accept) return true;
|
|
24
|
+
const extensions = accept.split(",").map((ext) => ext.trim().toLowerCase());
|
|
25
|
+
const fileName = file.name.toLowerCase();
|
|
26
|
+
return extensions.some((ext) => fileName.endsWith(ext));
|
|
27
|
+
}, [accept]);
|
|
28
|
+
const handleDragEnter = useCallback((e) => {
|
|
29
|
+
e.preventDefault();
|
|
30
|
+
e.stopPropagation();
|
|
31
|
+
if (disabled) return;
|
|
32
|
+
counter.current++;
|
|
33
|
+
if (e.dataTransfer?.types.includes("Files")) setDragOver(true);
|
|
34
|
+
}, [disabled]);
|
|
35
|
+
const handleDragLeave = useCallback((e) => {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
e.stopPropagation();
|
|
38
|
+
counter.current--;
|
|
39
|
+
if (counter.current === 0) setDragOver(false);
|
|
40
|
+
}, []);
|
|
41
|
+
const handleDragOver = useCallback((e) => {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
e.stopPropagation();
|
|
44
|
+
if (!disabled) e.dataTransfer.dropEffect = "copy";
|
|
45
|
+
}, [disabled]);
|
|
46
|
+
const handleDrop = useCallback((e) => {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
e.stopPropagation();
|
|
49
|
+
counter.current = 0;
|
|
50
|
+
setDragOver(false);
|
|
51
|
+
if (disabled) return;
|
|
52
|
+
const allFiles = Array.from(e.dataTransfer?.files || []);
|
|
53
|
+
const accepted = accept ? allFiles.filter(isAccepted) : allFiles;
|
|
54
|
+
if (accepted.length > 0) onDrop(accepted);
|
|
55
|
+
}, [
|
|
56
|
+
disabled,
|
|
57
|
+
accept,
|
|
58
|
+
isAccepted,
|
|
59
|
+
onDrop
|
|
60
|
+
]);
|
|
61
|
+
const isVisible = persistent || dragOver;
|
|
62
|
+
return /* @__PURE__ */ jsx("div", {
|
|
63
|
+
onDragEnter: handleDragEnter,
|
|
64
|
+
onDragLeave: handleDragLeave,
|
|
65
|
+
onDragOver: handleDragOver,
|
|
66
|
+
onDrop: handleDrop,
|
|
67
|
+
className: cn("rounded-lg border-2 border-dashed transition-all duration-150", isVisible ? "p-4" : "p-0 border-transparent", dragOver ? "border-[var(--brand-cool,hsl(var(--ring)))] bg-[var(--accent-surface-soft)]" : persistent ? "border-[var(--border-subtle,hsl(var(--border)))] bg-transparent hover:border-[var(--border-default,hsl(var(--border)))] hover:bg-[var(--bg-hover,hsl(var(--accent)))]" : "", disabled && "opacity-50 pointer-events-none", className),
|
|
68
|
+
children: isVisible && /* @__PURE__ */ jsxs("div", {
|
|
69
|
+
className: "flex flex-col items-center gap-2 text-center",
|
|
70
|
+
children: [
|
|
71
|
+
/* @__PURE__ */ jsx("div", {
|
|
72
|
+
className: cn("flex h-8 w-8 items-center justify-center rounded-lg transition-colors", dragOver ? "bg-[var(--brand-cool,hsl(var(--primary)))]/15 text-[var(--brand-cool,hsl(var(--primary)))]" : "text-[var(--text-muted,hsl(var(--muted-foreground)))]"),
|
|
73
|
+
children: icon ?? /* @__PURE__ */ jsx(Upload, { className: "h-4 w-4" })
|
|
74
|
+
}),
|
|
75
|
+
/* @__PURE__ */ jsx("p", {
|
|
76
|
+
className: cn("text-xs font-medium", dragOver ? "text-[var(--text-primary,hsl(var(--foreground)))]" : "text-[var(--text-muted,hsl(var(--muted-foreground)))]"),
|
|
77
|
+
children: title
|
|
78
|
+
}),
|
|
79
|
+
description && /* @__PURE__ */ jsx("p", {
|
|
80
|
+
className: "text-[10px] text-[var(--text-muted,hsl(var(--muted-foreground)))]",
|
|
81
|
+
children: description
|
|
82
|
+
})
|
|
83
|
+
]
|
|
84
|
+
})
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
export { SidebarDropZone };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/primitives/skeleton.d.ts
|
|
2
|
+
declare function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): import("react").JSX.Element;
|
|
3
|
+
declare function SkeletonCard({ className }: {
|
|
4
|
+
className?: string;
|
|
5
|
+
}): import("react").JSX.Element;
|
|
6
|
+
declare function SkeletonTable({ rows }: {
|
|
7
|
+
rows?: number;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { Skeleton, SkeletonCard, SkeletonTable };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/primitives/skeleton.tsx
|
|
4
|
+
function Skeleton({ className, ...props }) {
|
|
5
|
+
return /* @__PURE__ */ jsx("div", {
|
|
6
|
+
className: cn("animate-pulse rounded-lg bg-muted/50", className),
|
|
7
|
+
...props
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
function SkeletonCard({ className }) {
|
|
11
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
12
|
+
className: cn("space-y-4 rounded-xl border border-border bg-card p-6", className),
|
|
13
|
+
children: [
|
|
14
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-3/4" }),
|
|
15
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-1/2" }),
|
|
16
|
+
/* @__PURE__ */ jsxs("div", {
|
|
17
|
+
className: "space-y-2 pt-4",
|
|
18
|
+
children: [
|
|
19
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-full" }),
|
|
20
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-5/6" }),
|
|
21
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-4/6" })
|
|
22
|
+
]
|
|
23
|
+
})
|
|
24
|
+
]
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function SkeletonTable({ rows = 5 }) {
|
|
28
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
29
|
+
className: "space-y-3",
|
|
30
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
31
|
+
className: "flex gap-4 border-border border-b pb-2",
|
|
32
|
+
children: [
|
|
33
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-1/4" }),
|
|
34
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-1/4" }),
|
|
35
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-1/4" }),
|
|
36
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-1/4" })
|
|
37
|
+
]
|
|
38
|
+
}), Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsxs("div", {
|
|
39
|
+
className: "flex gap-4",
|
|
40
|
+
children: [
|
|
41
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-1/4" }),
|
|
42
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-1/4" }),
|
|
43
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-1/4" }),
|
|
44
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-1/4" })
|
|
45
|
+
]
|
|
46
|
+
}, i))]
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { Skeleton, SkeletonCard, SkeletonTable };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
//#region src/primitives/stat-card.d.ts
|
|
3
|
+
export interface StatCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
variant?: "default" | "sandbox";
|
|
5
|
+
title: string;
|
|
6
|
+
value: string | number;
|
|
7
|
+
subtitle?: string;
|
|
8
|
+
icon?: React.ReactNode;
|
|
9
|
+
trend?: {
|
|
10
|
+
value: number;
|
|
11
|
+
label?: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
declare const StatCard: React.ForwardRefExoticComponent<StatCardProps & React.RefAttributes<HTMLDivElement>>;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { StatCard };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { Card } from "./card.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/primitives/stat-card.tsx
|
|
6
|
+
const StatCard = React.forwardRef(({ className, variant = "default", title, value, subtitle, icon, trend, ...props }, ref) => {
|
|
7
|
+
const iconColors = {
|
|
8
|
+
default: "text-muted-foreground",
|
|
9
|
+
sandbox: "text-[var(--accent-text)]"
|
|
10
|
+
};
|
|
11
|
+
const trendColors = {
|
|
12
|
+
positive: "text-[var(--surface-success-text)]",
|
|
13
|
+
negative: "text-[var(--surface-danger-text)]",
|
|
14
|
+
neutral: "text-muted-foreground"
|
|
15
|
+
};
|
|
16
|
+
const trendStatus = trend ? trend.value > 0 ? "positive" : trend.value < 0 ? "negative" : "neutral" : null;
|
|
17
|
+
return /* @__PURE__ */ jsx(Card, {
|
|
18
|
+
ref,
|
|
19
|
+
variant,
|
|
20
|
+
className: cn("p-6", className),
|
|
21
|
+
...props,
|
|
22
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
23
|
+
className: "flex items-start justify-between",
|
|
24
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
25
|
+
className: "space-y-1",
|
|
26
|
+
children: [
|
|
27
|
+
/* @__PURE__ */ jsx("p", {
|
|
28
|
+
className: "text-muted-foreground text-sm",
|
|
29
|
+
children: title
|
|
30
|
+
}),
|
|
31
|
+
/* @__PURE__ */ jsx("p", {
|
|
32
|
+
className: "font-bold text-3xl tracking-tight",
|
|
33
|
+
children: value
|
|
34
|
+
}),
|
|
35
|
+
subtitle && /* @__PURE__ */ jsx("p", {
|
|
36
|
+
className: "text-[var(--text-dim)] text-xs",
|
|
37
|
+
children: subtitle
|
|
38
|
+
}),
|
|
39
|
+
trend && trendStatus && /* @__PURE__ */ jsxs("div", {
|
|
40
|
+
className: cn("flex items-center gap-1 text-sm", trendColors[trendStatus]),
|
|
41
|
+
children: [
|
|
42
|
+
trend.value > 0 ? "↑" : trend.value < 0 ? "↓" : "→",
|
|
43
|
+
/* @__PURE__ */ jsxs("span", { children: [Math.abs(trend.value), "%"] }),
|
|
44
|
+
trend.label && /* @__PURE__ */ jsx("span", {
|
|
45
|
+
className: "text-muted-foreground",
|
|
46
|
+
children: trend.label
|
|
47
|
+
})
|
|
48
|
+
]
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
}), icon && /* @__PURE__ */ jsx("div", {
|
|
52
|
+
className: cn("rounded-lg bg-muted/50 p-2", variant === "sandbox" && "bg-[var(--accent-surface-soft)]", iconColors[variant]),
|
|
53
|
+
children: icon
|
|
54
|
+
})]
|
|
55
|
+
})
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
StatCard.displayName = "StatCard";
|
|
59
|
+
//#endregion
|
|
60
|
+
export { StatCard };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
//#region src/primitives/status-pill.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* A run/resource state, told three ways at once.
|
|
5
|
+
*
|
|
6
|
+
* Status is the one place a console cannot afford to speak in colour alone: a
|
|
7
|
+
* red dot and a green dot are the same dot to roughly one man in twelve, and
|
|
8
|
+
* identical in a greyscale print or a screenshot pasted into a ticket. So every
|
|
9
|
+
* pill carries all three channels — a GLYPH whose silhouette differs per tone,
|
|
10
|
+
* the tone's COLOUR, and the state's own LABEL as text.
|
|
11
|
+
*
|
|
12
|
+
* The glyphs are chosen to survive at 8px and to differ in outline rather than
|
|
13
|
+
* in fill: a ring reads as "still open", a solid disc as "settled", a slashed
|
|
14
|
+
* disc as "stopped". Two states never share one silhouette.
|
|
15
|
+
*
|
|
16
|
+
* Each tone draws its fill, border and text from ONE matched token triple.
|
|
17
|
+
* That pairing is the whole point: a status colour is solved against its own
|
|
18
|
+
* background, and nothing guarantees it against an arbitrary one. A component
|
|
19
|
+
* that always brings its own background cannot be placed onto a plane that
|
|
20
|
+
* breaks it.
|
|
21
|
+
*/
|
|
22
|
+
export type StatusTone = "success" | "warning" | "danger" | "info" | "neutral" | "running";
|
|
23
|
+
export interface StatusPillProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
|
|
24
|
+
tone: StatusTone;
|
|
25
|
+
children: React.ReactNode;
|
|
26
|
+
/**
|
|
27
|
+
* Drops the fill and border, leaving a toned GLYPH beside a label in the
|
|
28
|
+
* inherited body colour.
|
|
29
|
+
*
|
|
30
|
+
* The label deliberately does not keep the tone. Every status text token
|
|
31
|
+
* clears the 4.5:1 body floor on the page canvas — a gate holds them there —
|
|
32
|
+
* but in light the margin is thin (4.51:1 for warning, 4.54:1 for success),
|
|
33
|
+
* and the canvas is only one of the planes a caller can put a pill on. The
|
|
34
|
+
* glyph keeps the tone instead because a glyph is non-text content against a
|
|
35
|
+
* 3:1 floor, so the tone still reads with room to spare on any plane.
|
|
36
|
+
*
|
|
37
|
+
* For a control that supplies its own surface (a chip, a selected row).
|
|
38
|
+
*/
|
|
39
|
+
bare?: boolean;
|
|
40
|
+
size?: "sm" | "md";
|
|
41
|
+
}
|
|
42
|
+
declare const StatusPill: React.ForwardRefExoticComponent<StatusPillProps & React.RefAttributes<HTMLSpanElement>>;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { StatusPill };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/primitives/status-pill.tsx
|
|
5
|
+
const TONE_SURFACE = {
|
|
6
|
+
success: "bg-[var(--surface-success-bg)] text-[var(--surface-success-text)] border-[var(--surface-success-border)]",
|
|
7
|
+
warning: "bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)] border-[var(--surface-warning-border)]",
|
|
8
|
+
danger: "bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)] border-[var(--surface-danger-border)]",
|
|
9
|
+
info: "bg-[var(--surface-info-bg)] text-[var(--surface-info-text)] border-[var(--surface-info-border)]",
|
|
10
|
+
running: "bg-[var(--surface-info-bg)] text-[var(--surface-info-text)] border-[var(--surface-info-border)]",
|
|
11
|
+
neutral: "bg-[var(--surface-neutral-bg)] text-[var(--surface-neutral-text)] border-[var(--surface-neutral-border)]"
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The text tier alone, for `bare`. Declared rather than recovered from
|
|
15
|
+
* `TONE_SURFACE` by string search: picking the `text-` class out of that string
|
|
16
|
+
* silently yields `undefined` the moment the triple gains a second `text-`
|
|
17
|
+
* utility, and an undefined class removes the tone from the one channel `bare`
|
|
18
|
+
* has left — the glyph — with nothing to signal that it happened.
|
|
19
|
+
*/
|
|
20
|
+
const TONE_TEXT = {
|
|
21
|
+
success: "text-[var(--surface-success-text)]",
|
|
22
|
+
warning: "text-[var(--surface-warning-text)]",
|
|
23
|
+
danger: "text-[var(--surface-danger-text)]",
|
|
24
|
+
info: "text-[var(--surface-info-text)]",
|
|
25
|
+
running: "text-[var(--surface-info-text)]",
|
|
26
|
+
neutral: "text-[var(--surface-neutral-text)]"
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* One glyph per silhouette. `currentColor` throughout so the mark inherits the
|
|
30
|
+
* tone's text colour and can never drift from the label beside it.
|
|
31
|
+
*/
|
|
32
|
+
function ToneGlyph({ tone }) {
|
|
33
|
+
const common = {
|
|
34
|
+
viewBox: "0 0 8 8",
|
|
35
|
+
className: "size-2 shrink-0"
|
|
36
|
+
};
|
|
37
|
+
switch (tone) {
|
|
38
|
+
case "success": return /* @__PURE__ */ jsx("svg", {
|
|
39
|
+
...common,
|
|
40
|
+
"aria-hidden": "true",
|
|
41
|
+
fill: "currentColor",
|
|
42
|
+
children: /* @__PURE__ */ jsx("circle", {
|
|
43
|
+
cx: "4",
|
|
44
|
+
cy: "4",
|
|
45
|
+
r: "3.25"
|
|
46
|
+
})
|
|
47
|
+
});
|
|
48
|
+
case "danger": return /* @__PURE__ */ jsxs("svg", {
|
|
49
|
+
...common,
|
|
50
|
+
"aria-hidden": "true",
|
|
51
|
+
fill: "none",
|
|
52
|
+
stroke: "currentColor",
|
|
53
|
+
strokeWidth: "1.5",
|
|
54
|
+
children: [/* @__PURE__ */ jsx("circle", {
|
|
55
|
+
cx: "4",
|
|
56
|
+
cy: "4",
|
|
57
|
+
r: "3.1",
|
|
58
|
+
fill: "currentColor",
|
|
59
|
+
opacity: "0.35"
|
|
60
|
+
}), /* @__PURE__ */ jsx("path", {
|
|
61
|
+
d: "M1.9 6.1 6.1 1.9",
|
|
62
|
+
strokeLinecap: "round"
|
|
63
|
+
})]
|
|
64
|
+
});
|
|
65
|
+
case "warning": return /* @__PURE__ */ jsx("svg", {
|
|
66
|
+
...common,
|
|
67
|
+
"aria-hidden": "true",
|
|
68
|
+
fill: "currentColor",
|
|
69
|
+
children: /* @__PURE__ */ jsx("path", { d: "M4 0.6 7.7 7.1H0.3z" })
|
|
70
|
+
});
|
|
71
|
+
case "running": return /* @__PURE__ */ jsx("svg", {
|
|
72
|
+
...common,
|
|
73
|
+
"aria-hidden": "true",
|
|
74
|
+
fill: "none",
|
|
75
|
+
stroke: "currentColor",
|
|
76
|
+
strokeWidth: "1.75",
|
|
77
|
+
children: /* @__PURE__ */ jsx("circle", {
|
|
78
|
+
cx: "4",
|
|
79
|
+
cy: "4",
|
|
80
|
+
r: "2.9"
|
|
81
|
+
})
|
|
82
|
+
});
|
|
83
|
+
case "info": return /* @__PURE__ */ jsx("svg", {
|
|
84
|
+
...common,
|
|
85
|
+
"aria-hidden": "true",
|
|
86
|
+
fill: "currentColor",
|
|
87
|
+
children: /* @__PURE__ */ jsx("rect", {
|
|
88
|
+
x: "0.9",
|
|
89
|
+
y: "0.9",
|
|
90
|
+
width: "6.2",
|
|
91
|
+
height: "6.2",
|
|
92
|
+
rx: "1.2"
|
|
93
|
+
})
|
|
94
|
+
});
|
|
95
|
+
default: return /* @__PURE__ */ jsx("svg", {
|
|
96
|
+
...common,
|
|
97
|
+
"aria-hidden": "true",
|
|
98
|
+
fill: "none",
|
|
99
|
+
stroke: "currentColor",
|
|
100
|
+
strokeWidth: "1.75",
|
|
101
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
102
|
+
d: "M1 4h6",
|
|
103
|
+
strokeLinecap: "round"
|
|
104
|
+
})
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const StatusPill = React.forwardRef(({ className, tone, children, bare = false, size = "sm", ...props }, ref) => /* @__PURE__ */ jsxs("span", {
|
|
109
|
+
ref,
|
|
110
|
+
className: cn("inline-flex items-center gap-1.5 whitespace-nowrap rounded-full font-medium", size === "sm" ? "px-2 py-0.5 text-xs" : "px-2.5 py-1 text-sm", bare ? "border border-transparent bg-transparent px-0" : cn("border", TONE_SURFACE[tone]), className),
|
|
111
|
+
...props,
|
|
112
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
113
|
+
className: cn("inline-flex", bare && TONE_TEXT[tone]),
|
|
114
|
+
children: /* @__PURE__ */ jsx(ToneGlyph, { tone })
|
|
115
|
+
}), children]
|
|
116
|
+
}));
|
|
117
|
+
StatusPill.displayName = "StatusPill";
|
|
118
|
+
//#endregion
|
|
119
|
+
export { StatusPill };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
|
3
|
+
//#region src/primitives/switch.d.ts
|
|
4
|
+
declare const Switch: React.ForwardRefExoticComponent<Omit<SwitchPrimitives.SwitchProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { Switch };
|