@burtson-labs/ui 0.14.0 → 0.15.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/LICENSES/DEPENDENCIES.txt +1 -1
- package/README.md +15 -11
- package/dist/components/accordion.d.ts +3 -3
- package/dist/components/accordion.js +13 -10
- package/dist/components/accordion.js.map +1 -1
- package/dist/components/agent-run.d.ts +22 -0
- package/dist/components/agent-run.js +131 -0
- package/dist/components/agent-run.js.map +1 -0
- package/dist/components/alert-dialog.d.ts +12 -10
- package/dist/components/alert-dialog.js +29 -20
- package/dist/components/alert-dialog.js.map +1 -1
- package/dist/components/alert.d.ts +5 -3
- package/dist/components/alert.js +11 -7
- package/dist/components/alert.js.map +1 -1
- package/dist/components/app-shell.d.ts +7 -6
- package/dist/components/app-shell.js +21 -14
- package/dist/components/app-shell.js.map +1 -1
- package/dist/components/attachment.d.ts +3 -3
- package/dist/components/attachment.js +10 -7
- package/dist/components/attachment.js.map +1 -1
- package/dist/components/audio-player.d.ts +2 -2
- package/dist/components/audio-player.js +8 -6
- package/dist/components/audio-player.js.map +1 -1
- package/dist/components/avatar-upload.d.ts +19 -0
- package/dist/components/avatar-upload.js +359 -0
- package/dist/components/avatar-upload.js.map +1 -0
- package/dist/components/avatar.d.ts +3 -3
- package/dist/components/avatar.js +10 -7
- package/dist/components/avatar.js.map +1 -1
- package/dist/components/badge.d.ts +1 -1
- package/dist/components/badge.js +12 -10
- package/dist/components/badge.js.map +1 -1
- package/dist/components/breadcrumb.d.ts +8 -8
- package/dist/components/breadcrumb.js +24 -17
- package/dist/components/breadcrumb.js.map +1 -1
- package/dist/components/button.d.ts +8 -1
- package/dist/components/button.js +12 -3
- package/dist/components/button.js.map +1 -1
- package/dist/components/card.d.ts +8 -8
- package/dist/components/card.js +32 -23
- package/dist/components/card.js.map +1 -1
- package/dist/components/chat-history.d.ts +1 -1
- package/dist/components/chat-history.js +10 -9
- package/dist/components/chat-history.js.map +1 -1
- package/dist/components/chat-layout.d.ts +1 -1
- package/dist/components/chat-layout.js +7 -3
- package/dist/components/chat-layout.js.map +1 -1
- package/dist/components/checkbox-card.d.ts +2 -2
- package/dist/components/checkbox-card.js +9 -7
- package/dist/components/checkbox-card.js.map +1 -1
- package/dist/components/checkbox.d.ts +23 -2
- package/dist/components/checkbox.js +48 -6
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/collapsible.d.ts +2 -2
- package/dist/components/collapsible.js +7 -5
- package/dist/components/collapsible.js.map +1 -1
- package/dist/components/combobox.d.ts +14 -4
- package/dist/components/combobox.js +18 -8
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/command.d.ts +68 -12
- package/dist/components/command.js +38 -25
- package/dist/components/command.js.map +1 -1
- package/dist/components/composer.d.ts +2 -2
- package/dist/components/composer.js +9 -7
- package/dist/components/composer.js.map +1 -1
- package/dist/components/connection-status.d.ts +3 -3
- package/dist/components/connection-status.js +18 -14
- package/dist/components/connection-status.js.map +1 -1
- package/dist/components/context-menu.d.ts +14 -13
- package/dist/components/context-menu.js +33 -23
- package/dist/components/context-menu.js.map +1 -1
- package/dist/components/conversation.d.ts +1 -1
- package/dist/components/conversation.js +6 -5
- package/dist/components/conversation.js.map +1 -1
- package/dist/components/copy-button.d.ts +1 -1
- package/dist/components/copy-button.js +3 -2
- package/dist/components/copy-button.js.map +1 -1
- package/dist/components/data-table.d.ts +7 -2
- package/dist/components/data-table.js +16 -42
- package/dist/components/data-table.js.map +1 -1
- package/dist/components/dialog.d.ts +18 -9
- package/dist/components/dialog.js +38 -22
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/dropdown-menu.d.ts +14 -13
- package/dist/components/dropdown-menu.js +33 -23
- package/dist/components/dropdown-menu.js.map +1 -1
- package/dist/components/editor-tabs.d.ts +1 -1
- package/dist/components/editor-tabs.js +6 -5
- package/dist/components/editor-tabs.js.map +1 -1
- package/dist/components/empty-state.d.ts +5 -5
- package/dist/components/empty-state.js +16 -11
- package/dist/components/empty-state.js.map +1 -1
- package/dist/components/error-summary.d.ts +21 -0
- package/dist/components/error-summary.js +62 -0
- package/dist/components/error-summary.js.map +1 -0
- package/dist/components/field.d.ts +66 -7
- package/dist/components/field.js +151 -20
- package/dist/components/field.js.map +1 -1
- package/dist/components/form-actions.d.ts +18 -0
- package/dist/components/form-actions.js +30 -0
- package/dist/components/form-actions.js.map +1 -0
- package/dist/components/icon-button.d.ts +3 -1
- package/dist/components/icon-button.js +5 -3
- package/dist/components/icon-button.js.map +1 -1
- package/dist/components/input.d.ts +42 -7
- package/dist/components/input.js +53 -10
- package/dist/components/input.js.map +1 -1
- package/dist/components/kbd.d.ts +2 -2
- package/dist/components/kbd.js +7 -5
- package/dist/components/kbd.js.map +1 -1
- package/dist/components/label.d.ts +6 -1
- package/dist/components/label.js +10 -4
- package/dist/components/label.js.map +1 -1
- package/dist/components/markdown.d.ts +4 -3
- package/dist/components/markdown.js +7 -6
- package/dist/components/markdown.js.map +1 -1
- package/dist/components/menubar.d.ts +16 -14
- package/dist/components/menubar.js +39 -27
- package/dist/components/menubar.js.map +1 -1
- package/dist/components/message-actions.d.ts +3 -3
- package/dist/components/message-actions.js +15 -12
- package/dist/components/message-actions.js.map +1 -1
- package/dist/components/message.d.ts +1 -1
- package/dist/components/message.js +5 -3
- package/dist/components/message.js.map +1 -1
- package/dist/components/mobile-nav.d.ts +1 -1
- package/dist/components/mobile-nav.js +6 -5
- package/dist/components/mobile-nav.js.map +1 -1
- package/dist/components/native-select.d.ts +16 -0
- package/dist/components/native-select.js +33 -0
- package/dist/components/native-select.js.map +1 -0
- package/dist/components/navigation-menu.d.ts +12 -11
- package/dist/components/navigation-menu.js +32 -23
- package/dist/components/navigation-menu.js.map +1 -1
- package/dist/components/number-input.d.ts +23 -0
- package/dist/components/number-input.js +45 -0
- package/dist/components/number-input.js.map +1 -0
- package/dist/components/onboarding-checklist.d.ts +1 -1
- package/dist/components/onboarding-checklist.js +4 -3
- package/dist/components/onboarding-checklist.js.map +1 -1
- package/dist/components/page-header.d.ts +7 -6
- package/dist/components/page-header.js +22 -15
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/pagination.d.ts +1 -1
- package/dist/components/pagination.js +3 -2
- package/dist/components/pagination.js.map +1 -1
- package/dist/components/popover.d.ts +13 -10
- package/dist/components/popover.js +23 -17
- package/dist/components/popover.js.map +1 -1
- package/dist/components/progress.d.ts +1 -1
- package/dist/components/progress.js +6 -5
- package/dist/components/progress.js.map +1 -1
- package/dist/components/radio-group.d.ts +22 -3
- package/dist/components/radio-group.js +51 -10
- package/dist/components/radio-group.js.map +1 -1
- package/dist/components/reasoning.d.ts +3 -3
- package/dist/components/reasoning.js +9 -7
- package/dist/components/reasoning.js.map +1 -1
- package/dist/components/scroll-area.d.ts +3 -2
- package/dist/components/scroll-area.js +10 -7
- package/dist/components/scroll-area.js.map +1 -1
- package/dist/components/secret-input.d.ts +3 -2
- package/dist/components/secret-input.js +6 -4
- package/dist/components/secret-input.js.map +1 -1
- package/dist/components/select.d.ts +11 -6
- package/dist/components/select.js +24 -18
- package/dist/components/select.js.map +1 -1
- package/dist/components/separator.d.ts +1 -1
- package/dist/components/separator.js +4 -3
- package/dist/components/separator.js.map +1 -1
- package/dist/components/sheet.d.ts +10 -8
- package/dist/components/sheet.js +31 -20
- package/dist/components/sheet.js.map +1 -1
- package/dist/components/skeleton.d.ts +2 -1
- package/dist/components/skeleton.js +6 -4
- package/dist/components/skeleton.js.map +1 -1
- package/dist/components/slider.d.ts +10 -6
- package/dist/components/slider.js +15 -7
- package/dist/components/slider.js.map +1 -1
- package/dist/components/source.d.ts +2 -2
- package/dist/components/source.js +9 -7
- package/dist/components/source.js.map +1 -1
- package/dist/components/spinner.d.ts +4 -2
- package/dist/components/spinner.js +4 -3
- package/dist/components/spinner.js.map +1 -1
- package/dist/components/stat-card.d.ts +27 -2
- package/dist/components/stat-card.js +69 -9
- package/dist/components/stat-card.js.map +1 -1
- package/dist/components/status.d.ts +7 -6
- package/dist/components/status.js +11 -5
- package/dist/components/status.js.map +1 -1
- package/dist/components/steps.d.ts +1 -1
- package/dist/components/steps.js +4 -3
- package/dist/components/steps.js.map +1 -1
- package/dist/components/switch.d.ts +46 -2
- package/dist/components/switch.js +85 -8
- package/dist/components/switch.js.map +1 -1
- package/dist/components/table.d.ts +29 -12
- package/dist/components/table.js +79 -34
- package/dist/components/table.js.map +1 -1
- package/dist/components/tabs.d.ts +9 -4
- package/dist/components/tabs.js +22 -13
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/textarea.d.ts +6 -1
- package/dist/components/textarea.js +3 -3
- package/dist/components/textarea.js.map +1 -1
- package/dist/components/toast.d.ts +10 -6
- package/dist/components/toast.js +27 -18
- package/dist/components/toast.js.map +1 -1
- package/dist/components/toaster.d.ts +4 -2
- package/dist/components/toaster.js +6 -2
- package/dist/components/toaster.js.map +1 -1
- package/dist/components/tool-call.d.ts +3 -3
- package/dist/components/tool-call.js +16 -12
- package/dist/components/tool-call.js.map +1 -1
- package/dist/components/toolbar.d.ts +9 -4
- package/dist/components/toolbar.js +14 -9
- package/dist/components/toolbar.js.map +1 -1
- package/dist/components/tooltip.d.ts +3 -2
- package/dist/components/tooltip.js +7 -4
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/tour.js +2 -2
- package/dist/components/tour.js.map +1 -1
- package/dist/components/tree-view.d.ts +1 -1
- package/dist/components/tree-view.js +9 -5
- package/dist/components/tree-view.js.map +1 -1
- package/dist/components/voice-recorder.d.ts +1 -1
- package/dist/components/voice-recorder.js +4 -2
- package/dist/components/voice-recorder.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +19 -12
- package/dist/lib/media.d.ts +18 -0
- package/dist/lib/media.js +34 -0
- package/dist/lib/media.js.map +1 -0
- package/dist/lib/utils.d.ts +32 -0
- package/dist/lib/utils.js +33 -1
- package/dist/lib/utils.js.map +1 -1
- package/dist/mui/index.js +20 -8
- package/dist/mui/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/theme.css +36 -17
- package/dist/tokens.d.ts +78 -36
- package/dist/tokens.js +63 -32
- package/dist/tokens.js.map +1 -1
- package/package.json +5 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
export type ToolCallStatus = 'pending' | 'running' | 'success' | 'error';
|
|
2
|
+
export type ToolCallStatus = 'pending' | 'running' | 'success' | 'error' | 'canceled';
|
|
3
3
|
export interface ToolCallProps extends Omit<React.ComponentProps<'div'>, 'title'> {
|
|
4
4
|
/** The tool's name, e.g. `get_load`. */
|
|
5
5
|
name: string;
|
|
@@ -14,7 +14,7 @@ export interface ToolCallProps extends Omit<React.ComponentProps<'div'>, 'title'
|
|
|
14
14
|
defaultOpen?: boolean;
|
|
15
15
|
}
|
|
16
16
|
/** A tool call an agent made: name, live status, and collapsible arguments and result. */
|
|
17
|
-
declare
|
|
17
|
+
declare const ToolCall: React.ForwardRefExoticComponent<Omit<ToolCallProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
18
18
|
export interface ToolApprovalProps extends Omit<React.ComponentProps<'div'>, 'title'> {
|
|
19
19
|
name: string;
|
|
20
20
|
/** What will happen, in plain words, e.g. "Approve L-1042 and send it to Gravitate". */
|
|
@@ -30,5 +30,5 @@ export interface ToolApprovalProps extends Omit<React.ComponentProps<'div'>, 'ti
|
|
|
30
30
|
denyLabel?: string;
|
|
31
31
|
}
|
|
32
32
|
/** A gate before an agent runs a tool with side effects: the person approves or denies it. */
|
|
33
|
-
declare
|
|
33
|
+
declare const ToolApproval: React.ForwardRefExoticComponent<Omit<ToolApprovalProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
34
34
|
export { ToolApproval, ToolCall };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { cn, focusRingClasses } from "../lib/utils.js";
|
|
3
3
|
import { Content, Root, Trigger } from "../primitives/vendor/radix/react-collapsible/collapsible.js";
|
|
4
4
|
import { Button } from "./button.js";
|
|
5
5
|
import { StatusDot } from "./status.js";
|
|
6
|
-
import "react";
|
|
6
|
+
import * as React from "react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import ChevronRight from "@burtson-labs/icons/react/chevron-right";
|
|
9
9
|
import Wrench from "@burtson-labs/icons/react/wrench";
|
|
@@ -12,13 +12,15 @@ var statusText = {
|
|
|
12
12
|
pending: "Queued",
|
|
13
13
|
running: "Running",
|
|
14
14
|
success: "Done",
|
|
15
|
-
error: "Failed"
|
|
15
|
+
error: "Failed",
|
|
16
|
+
canceled: "Canceled"
|
|
16
17
|
};
|
|
17
18
|
var statusDot = {
|
|
18
19
|
pending: "neutral",
|
|
19
20
|
running: "brand",
|
|
20
21
|
success: "success",
|
|
21
|
-
error: "
|
|
22
|
+
error: "destructive",
|
|
23
|
+
canceled: "neutral"
|
|
22
24
|
};
|
|
23
25
|
var json = (value) => {
|
|
24
26
|
if (typeof value === "string") return value;
|
|
@@ -37,18 +39,19 @@ var json = (value) => {
|
|
|
37
39
|
}
|
|
38
40
|
};
|
|
39
41
|
/** A tool call an agent made: name, live status, and collapsible arguments and result. */
|
|
40
|
-
function ToolCall({ name, status, args, result, error, durationMs, defaultOpen = false, className, ...props }) {
|
|
42
|
+
var ToolCall = React.forwardRef(function ToolCall({ name, status, args, result, error, durationMs, defaultOpen = false, className, ...props }, ref) {
|
|
41
43
|
const hasDetail = args !== void 0 || result !== void 0 || Boolean(error);
|
|
42
44
|
return /* @__PURE__ */ jsx(Root, {
|
|
43
45
|
defaultOpen,
|
|
44
46
|
disabled: !hasDetail,
|
|
45
47
|
children: /* @__PURE__ */ jsxs("div", {
|
|
48
|
+
ref,
|
|
46
49
|
"data-slot": "tool-call",
|
|
47
50
|
"data-status": status,
|
|
48
51
|
className: cn("animate-in overflow-hidden rounded-md border bg-surface text-[13px]", status === "error" && "border-destructive/30", className),
|
|
49
52
|
...props,
|
|
50
53
|
children: [/* @__PURE__ */ jsxs(Trigger, {
|
|
51
|
-
className: "group/tool flex h-9 w-full items-center gap-2 px-3 text-left
|
|
54
|
+
className: cn("group/tool flex h-9 w-full items-center gap-2 px-3 text-left hover:bg-muted/60 disabled:cursor-default disabled:hover:bg-transparent pointer-coarse:h-11", focusRingClasses, "focus-visible:outline-offset-[-2px]"),
|
|
52
55
|
children: [
|
|
53
56
|
/* @__PURE__ */ jsx(Wrench, {
|
|
54
57
|
className: "size-3.5 shrink-0 text-muted-foreground",
|
|
@@ -112,25 +115,26 @@ function ToolCall({ name, status, args, result, error, durationMs, defaultOpen =
|
|
|
112
115
|
})]
|
|
113
116
|
})
|
|
114
117
|
});
|
|
115
|
-
}
|
|
118
|
+
});
|
|
116
119
|
/** A gate before an agent runs a tool with side effects: the person approves or denies it. */
|
|
117
|
-
function ToolApproval({ name, description, args, state = "pending", busy = false, onApprove, onDeny, approveLabel = "Approve", denyLabel = "Deny", className, ...props }) {
|
|
120
|
+
var ToolApproval = React.forwardRef(function ToolApproval({ name, description, args, state = "pending", busy = false, onApprove, onDeny, approveLabel = "Approve", denyLabel = "Deny", className, ...props }, ref) {
|
|
118
121
|
return /* @__PURE__ */ jsxs("div", {
|
|
122
|
+
ref,
|
|
119
123
|
"data-slot": "tool-approval",
|
|
120
124
|
"aria-busy": busy || void 0,
|
|
121
125
|
"data-state": state,
|
|
122
|
-
className: cn("grid animate-in gap-3 rounded-md border p-3 text-[13px]", state === "pending" ? "border-warning/40 bg-warning/5" : "bg-surface", className),
|
|
126
|
+
className: cn("grid min-w-0 animate-in gap-3 rounded-md border p-3 text-[13px]", state === "pending" ? "border-warning/40 bg-warning/5" : "bg-surface", className),
|
|
123
127
|
...props,
|
|
124
128
|
children: [
|
|
125
129
|
/* @__PURE__ */ jsxs("div", {
|
|
126
|
-
className: "flex items-center gap-2",
|
|
130
|
+
className: "flex flex-wrap items-center gap-2",
|
|
127
131
|
children: [
|
|
128
132
|
/* @__PURE__ */ jsx(Wrench, {
|
|
129
133
|
className: "size-3.5 text-muted-foreground",
|
|
130
134
|
"aria-hidden": true
|
|
131
135
|
}),
|
|
132
136
|
/* @__PURE__ */ jsx("span", {
|
|
133
|
-
className: "font-mono text-[12.5px]",
|
|
137
|
+
className: "min-w-0 break-all font-mono text-[12.5px]",
|
|
134
138
|
children: name
|
|
135
139
|
}),
|
|
136
140
|
/* @__PURE__ */ jsx("span", {
|
|
@@ -164,7 +168,7 @@ function ToolApproval({ name, description, args, state = "pending", busy = false
|
|
|
164
168
|
})
|
|
165
169
|
]
|
|
166
170
|
});
|
|
167
|
-
}
|
|
171
|
+
});
|
|
168
172
|
//#endregion
|
|
169
173
|
export { ToolApproval, ToolCall };
|
|
170
174
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-call.js","names":["CollapsiblePrimitive.Root","CollapsiblePrimitive.Trigger","CollapsiblePrimitive.Content"],"sources":["../../src/components/tool-call.tsx"],"sourcesContent":["import ChevronRight from '@burtson-labs/icons/react/chevron-right';\nimport Wrench from '@burtson-labs/icons/react/wrench';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport * as CollapsiblePrimitive from '../primitives/vendor/radix/react-collapsible';\n\nimport { Button } from './button';\nimport { StatusDot } from './status';\n\nexport type ToolCallStatus = 'pending' | 'running' | 'success' | 'error';\n\nconst statusText: Record<ToolCallStatus, string> = {\n pending: 'Queued',\n running: 'Running',\n success: 'Done',\n error: 'Failed',\n};\nconst statusDot = {\n pending: 'neutral',\n running: 'brand',\n success: 'success',\n error: 'danger',\n} as const;\n\nconst json = (value: unknown) => {\n if (typeof value === 'string') return value;\n const seen = new WeakSet<object>();\n try {\n return (\n JSON.stringify(\n value,\n (_key, item: unknown) => {\n if (typeof item === 'bigint') return item.toString();\n if (item && typeof item === 'object') {\n if (seen.has(item)) return '[Repeated reference]';\n seen.add(item);\n }\n return item;\n },\n 2,\n ) ?? String(value)\n );\n } catch {\n return '[Value could not be displayed]';\n }\n};\n\nexport interface ToolCallProps extends Omit<React.ComponentProps<'div'>, 'title'> {\n /** The tool's name, e.g. `get_load`. */\n name: string;\n status: ToolCallStatus;\n /** Arguments the agent passed; shown as formatted JSON. */\n args?: unknown;\n /** What the tool returned; shown as formatted JSON. */\n result?: unknown;\n /** Error message when the call failed. */\n error?: string;\n durationMs?: number;\n defaultOpen?: boolean;\n}\n\n/** A tool call an agent made: name, live status, and collapsible arguments and result. */\nfunction ToolCall({\n name,\n status,\n args,\n result,\n error,\n durationMs,\n defaultOpen = false,\n className,\n ...props\n}: ToolCallProps) {\n const hasDetail = args !== undefined || result !== undefined || Boolean(error);\n return (\n <CollapsiblePrimitive.Root defaultOpen={defaultOpen} disabled={!hasDetail}>\n <div\n data-slot=\"tool-call\"\n data-status={status}\n className={cn(\n 'animate-in overflow-hidden rounded-md border bg-surface text-[13px]',\n status === 'error' && 'border-destructive/30',\n className,\n )}\n {...props}\n >\n <CollapsiblePrimitive.Trigger className=\"group/tool flex h-9 w-full items-center gap-2 px-3 text-left outline-none hover:bg-muted/60 focus-visible:ring-[3px] focus-visible:ring-ring/20 disabled:cursor-default disabled:hover:bg-transparent\">\n <Wrench className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n <span className=\"min-w-0 flex-1 truncate font-mono text-[12.5px]\">{name}</span>\n <span className=\"inline-flex items-center gap-1.5 text-xs text-muted-foreground\">\n <StatusDot status={statusDot[status]} pulse={status === 'running'} />\n {statusText[status]}\n {durationMs !== undefined && status !== 'running' && (\n <span className=\"tabular-nums\">· {(durationMs / 1000).toFixed(1)}s</span>\n )}\n </span>\n {hasDetail && (\n <ChevronRight\n aria-hidden\n className=\"size-3.5 text-muted-foreground transition-transform group-data-[state=open]/tool:rotate-90\"\n />\n )}\n </CollapsiblePrimitive.Trigger>\n <CollapsiblePrimitive.Content className=\"overflow-hidden data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down\">\n <div className=\"grid gap-2 border-t p-3\">\n {args !== undefined && (\n <section className=\"grid gap-1\">\n <h4 className=\"text-[11px] font-semibold tracking-[0.06em] text-muted-foreground uppercase\">\n Arguments\n </h4>\n <pre className=\"overflow-x-auto rounded-sm bg-muted/60 p-2 font-mono text-[12px] leading-5\">\n {json(args)}\n </pre>\n </section>\n )}\n {error ? (\n <p role=\"alert\" className=\"text-xs font-medium text-destructive\">\n {error}\n </p>\n ) : (\n result !== undefined && (\n <section className=\"grid gap-1\">\n <h4 className=\"text-[11px] font-semibold tracking-[0.06em] text-muted-foreground uppercase\">\n Result\n </h4>\n <pre className=\"max-h-64 overflow-auto rounded-sm bg-muted/60 p-2 font-mono text-[12px] leading-5\">\n {json(result)}\n </pre>\n </section>\n )\n )}\n </div>\n </CollapsiblePrimitive.Content>\n </div>\n </CollapsiblePrimitive.Root>\n );\n}\n\nexport interface ToolApprovalProps extends Omit<React.ComponentProps<'div'>, 'title'> {\n name: string;\n /** What will happen, in plain words, e.g. \"Approve L-1042 and send it to Gravitate\". */\n description: React.ReactNode;\n args?: unknown;\n /** `pending` shows the buttons; the others record the decision. */\n state?: 'pending' | 'approved' | 'denied';\n /** Disable decision controls while the application records the choice. */\n busy?: boolean;\n onApprove?: () => void;\n onDeny?: () => void;\n approveLabel?: string;\n denyLabel?: string;\n}\n\n/** A gate before an agent runs a tool with side effects: the person approves or denies it. */\nfunction ToolApproval({\n name,\n description,\n args,\n state = 'pending',\n busy = false,\n onApprove,\n onDeny,\n approveLabel = 'Approve',\n denyLabel = 'Deny',\n className,\n ...props\n}: ToolApprovalProps) {\n return (\n <div\n data-slot=\"tool-approval\"\n aria-busy={busy || undefined}\n data-state={state}\n className={cn(\n 'grid animate-in gap-3 rounded-md border p-3 text-[13px]',\n state === 'pending' ? 'border-warning/40 bg-warning/5' : 'bg-surface',\n className,\n )}\n {...props}\n >\n <div className=\"flex items-center gap-2\">\n <Wrench className=\"size-3.5 text-muted-foreground\" aria-hidden />\n <span className=\"font-mono text-[12.5px]\">{name}</span>\n <span className=\"ml-auto text-xs text-muted-foreground\">\n {state === 'pending'\n ? 'Needs your approval'\n : state === 'approved'\n ? 'Approved'\n : 'Denied'}\n </span>\n </div>\n <p className=\"text-sm\">{description}</p>\n {args !== undefined && (\n <pre className=\"overflow-x-auto rounded-sm bg-muted/60 p-2 font-mono text-[12px] leading-5\">\n {json(args)}\n </pre>\n )}\n {state === 'pending' && (\n <div className=\"flex justify-end gap-2\">\n <Button size=\"sm\" variant=\"outline\" onClick={onDeny} disabled={busy || !onDeny}>\n {denyLabel}\n </Button>\n <Button size=\"sm\" onClick={onApprove} disabled={busy || !onApprove}>\n {approveLabel}\n </Button>\n </div>\n )}\n </div>\n );\n}\n\nexport { ToolApproval, ToolCall };\n"],"mappings":";;;;;;;;;;AAYA,IAAM,aAA6C;CACjD,SAAS;CACT,SAAS;CACT,SAAS;CACT,OAAO;AACT;AACA,IAAM,YAAY;CAChB,SAAS;CACT,SAAS;CACT,SAAS;CACT,OAAO;AACT;AAEA,IAAM,QAAQ,UAAmB;CAC/B,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,MAAM,uBAAO,IAAI,QAAgB;CACjC,IAAI;EACF,OACE,KAAK,UACH,QACC,MAAM,SAAkB;GACvB,IAAI,OAAO,SAAS,UAAU,OAAO,KAAK,SAAS;GACnD,IAAI,QAAQ,OAAO,SAAS,UAAU;IACpC,IAAI,KAAK,IAAI,IAAI,GAAG,OAAO;IAC3B,KAAK,IAAI,IAAI;GACf;GACA,OAAO;EACT,GACA,CACF,KAAK,OAAO,KAAK;CAErB,QAAQ;EACN,OAAO;CACT;AACF;;AAiBA,SAAS,SAAS,EAChB,MACA,QACA,MACA,QACA,OACA,YACA,cAAc,OACd,WACA,GAAG,SACa;CAChB,MAAM,YAAY,SAAS,KAAA,KAAa,WAAW,KAAA,KAAa,QAAQ,KAAK;CAC7E,OACE,oBAACA,MAAD;EAAwC;EAAa,UAAU,CAAC;EAC9D,UAAA,qBAAC,OAAD;GACE,aAAU;GACV,eAAa;GACb,WAAW,GACT,uEACA,WAAW,WAAW,yBACtB,SACF;GACA,GAAI;GARN,UAAA,CAUE,qBAACC,SAAD;IAA8B,WAAU;IAAxC,UAAA;KACE,oBAAC,QAAD;MAAQ,WAAU;MAA0C,eAAA;KAAa,CAAA;KACzE,oBAAC,QAAD;MAAM,WAAU;MAAmD,UAAA;KAAW,CAAA;KAC9E,qBAAC,QAAD;MAAM,WAAU;MAAhB,UAAA;OACE,oBAAC,WAAD;QAAW,QAAQ,UAAU;QAAS,OAAO,WAAW;OAAY,CAAA;OACnE,WAAW;OACX,eAAe,KAAA,KAAa,WAAW,aACtC,qBAAC,QAAD;QAAM,WAAU;QAAhB,UAAA;SAA+B;UAAI,aAAa,IAAA,CAAM,QAAQ,CAAC;SAAE;QAAO;;MAEtE;;KACL,aACC,oBAAC,cAAD;MACE,eAAA;MACA,WAAU;KACX,CAAA;IAEyB;GAC9B,CAAA,GAAA,oBAACC,SAAD;IAA8B,WAAU;IACtC,UAAA,qBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACG,SAAS,KAAA,KACR,qBAAC,WAAD;MAAS,WAAU;MAAnB,UAAA,CACE,oBAAC,MAAD;OAAI,WAAU;OAA8E,UAAA;MAExF,CAAA,GACJ,oBAAC,OAAD;OAAK,WAAU;OACZ,UAAA,KAAK,IAAI;MACP,CAAA,CACE;KAEV,CAAA,GAAA,QACC,oBAAC,KAAD;MAAG,MAAK;MAAQ,WAAU;MACvB,UAAA;KACA,CAAA,IAEH,WAAW,KAAA,KACT,qBAAC,WAAD;MAAS,WAAU;MAAnB,UAAA,CACE,oBAAC,MAAD;OAAI,WAAU;OAA8E,UAAA;MAExF,CAAA,GACJ,oBAAC,OAAD;OAAK,WAAU;OACZ,UAAA,KAAK,MAAM;MACT,CAAA,CACE;KAGV,CAAA,CAAA;;GACuB,CAAA,CAC3B;;CACoB,CAAA;AAE/B;;AAkBA,SAAS,aAAa,EACpB,MACA,aACA,MACA,QAAQ,WACR,OAAO,OACP,WACA,QACA,eAAe,WACf,YAAY,QACZ,WACA,GAAG,SACiB;CACpB,OACE,qBAAC,OAAD;EACE,aAAU;EACV,aAAW,QAAQ,KAAA;EACnB,cAAY;EACZ,WAAW,GACT,2DACA,UAAU,YAAY,mCAAmC,cACzD,SACF;EACA,GAAI;EATN,UAAA;GAWE,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,oBAAC,QAAD;MAAQ,WAAU;MAAiC,eAAA;KAAa,CAAA;KAChE,oBAAC,QAAD;MAAM,WAAU;MAA2B,UAAA;KAAW,CAAA;KACtD,oBAAC,QAAD;MAAM,WAAU;MACb,UAAA,UAAU,YACP,wBACA,UAAU,aACR,aACA;KACF,CAAA;IACH;;GACL,oBAAC,KAAD;IAAG,WAAU;IAAW,UAAA;GAAe,CAAA;GACtC,SAAS,KAAA,KACR,oBAAC,OAAD;IAAK,WAAU;IACZ,UAAA,KAAK,IAAI;GACP,CAAA;GAEN,UAAU,aACT,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,oBAAC,QAAD;KAAQ,MAAK;KAAK,SAAQ;KAAU,SAAS;KAAQ,UAAU,QAAQ,CAAC;KACrE,UAAA;IACK,CAAA,GACR,oBAAC,QAAD;KAAQ,MAAK;KAAK,SAAS;KAAW,UAAU,QAAQ,CAAC;KACtD,UAAA;IACK,CAAA,CACL;;EAEJ;;AAET"}
|
|
1
|
+
{"version":3,"file":"tool-call.js","names":["CollapsiblePrimitive.Root","CollapsiblePrimitive.Trigger","CollapsiblePrimitive.Content"],"sources":["../../src/components/tool-call.tsx"],"sourcesContent":["import ChevronRight from '@burtson-labs/icons/react/chevron-right';\nimport Wrench from '@burtson-labs/icons/react/wrench';\nimport * as React from 'react';\n\nimport { cn, focusRingClasses } from '../lib/utils';\nimport * as CollapsiblePrimitive from '../primitives/vendor/radix/react-collapsible';\n\nimport { Button } from './button';\nimport { StatusDot } from './status';\n\nexport type ToolCallStatus = 'pending' | 'running' | 'success' | 'error' | 'canceled';\n\nconst statusText: Record<ToolCallStatus, string> = {\n pending: 'Queued',\n running: 'Running',\n success: 'Done',\n error: 'Failed',\n canceled: 'Canceled',\n};\nconst statusDot = {\n pending: 'neutral',\n running: 'brand',\n success: 'success',\n error: 'destructive',\n canceled: 'neutral',\n} as const;\n\nconst json = (value: unknown) => {\n if (typeof value === 'string') return value;\n const seen = new WeakSet<object>();\n try {\n return (\n JSON.stringify(\n value,\n (_key, item: unknown) => {\n if (typeof item === 'bigint') return item.toString();\n if (item && typeof item === 'object') {\n if (seen.has(item)) return '[Repeated reference]';\n seen.add(item);\n }\n return item;\n },\n 2,\n ) ?? String(value)\n );\n } catch {\n return '[Value could not be displayed]';\n }\n};\n\nexport interface ToolCallProps extends Omit<React.ComponentProps<'div'>, 'title'> {\n /** The tool's name, e.g. `get_load`. */\n name: string;\n status: ToolCallStatus;\n /** Arguments the agent passed; shown as formatted JSON. */\n args?: unknown;\n /** What the tool returned; shown as formatted JSON. */\n result?: unknown;\n /** Error message when the call failed. */\n error?: string;\n durationMs?: number;\n defaultOpen?: boolean;\n}\n\n/** A tool call an agent made: name, live status, and collapsible arguments and result. */\nconst ToolCall = React.forwardRef<HTMLDivElement, ToolCallProps>(function ToolCall(\n { name, status, args, result, error, durationMs, defaultOpen = false, className, ...props },\n ref,\n) {\n const hasDetail = args !== undefined || result !== undefined || Boolean(error);\n return (\n <CollapsiblePrimitive.Root defaultOpen={defaultOpen} disabled={!hasDetail}>\n <div\n ref={ref}\n data-slot=\"tool-call\"\n data-status={status}\n className={cn(\n 'animate-in overflow-hidden rounded-md border bg-surface text-[13px]',\n status === 'error' && 'border-destructive/30',\n className,\n )}\n {...props}\n >\n <CollapsiblePrimitive.Trigger\n className={cn(\n 'group/tool flex h-9 w-full items-center gap-2 px-3 text-left hover:bg-muted/60 disabled:cursor-default disabled:hover:bg-transparent pointer-coarse:h-11',\n focusRingClasses,\n 'focus-visible:outline-offset-[-2px]',\n )}\n >\n <Wrench className=\"size-3.5 shrink-0 text-muted-foreground\" aria-hidden />\n <span className=\"min-w-0 flex-1 truncate font-mono text-[12.5px]\">{name}</span>\n <span className=\"inline-flex items-center gap-1.5 text-xs text-muted-foreground\">\n <StatusDot status={statusDot[status]} pulse={status === 'running'} />\n {statusText[status]}\n {durationMs !== undefined && status !== 'running' && (\n <span className=\"tabular-nums\">· {(durationMs / 1000).toFixed(1)}s</span>\n )}\n </span>\n {hasDetail && (\n <ChevronRight\n aria-hidden\n className=\"size-3.5 text-muted-foreground transition-transform group-data-[state=open]/tool:rotate-90\"\n />\n )}\n </CollapsiblePrimitive.Trigger>\n <CollapsiblePrimitive.Content className=\"overflow-hidden data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down\">\n <div className=\"grid gap-2 border-t p-3\">\n {args !== undefined && (\n <section className=\"grid gap-1\">\n <h4 className=\"text-[11px] font-semibold tracking-[0.06em] text-muted-foreground uppercase\">\n Arguments\n </h4>\n <pre className=\"overflow-x-auto rounded-sm bg-muted/60 p-2 font-mono text-[12px] leading-5\">\n {json(args)}\n </pre>\n </section>\n )}\n {error ? (\n <p role=\"alert\" className=\"text-xs font-medium text-destructive\">\n {error}\n </p>\n ) : (\n result !== undefined && (\n <section className=\"grid gap-1\">\n <h4 className=\"text-[11px] font-semibold tracking-[0.06em] text-muted-foreground uppercase\">\n Result\n </h4>\n <pre className=\"max-h-64 overflow-auto rounded-sm bg-muted/60 p-2 font-mono text-[12px] leading-5\">\n {json(result)}\n </pre>\n </section>\n )\n )}\n </div>\n </CollapsiblePrimitive.Content>\n </div>\n </CollapsiblePrimitive.Root>\n );\n});\n\nexport interface ToolApprovalProps extends Omit<React.ComponentProps<'div'>, 'title'> {\n name: string;\n /** What will happen, in plain words, e.g. \"Approve L-1042 and send it to Gravitate\". */\n description: React.ReactNode;\n args?: unknown;\n /** `pending` shows the buttons; the others record the decision. */\n state?: 'pending' | 'approved' | 'denied';\n /** Disable decision controls while the application records the choice. */\n busy?: boolean;\n onApprove?: () => void;\n onDeny?: () => void;\n approveLabel?: string;\n denyLabel?: string;\n}\n\n/** A gate before an agent runs a tool with side effects: the person approves or denies it. */\nconst ToolApproval = React.forwardRef<HTMLDivElement, ToolApprovalProps>(function ToolApproval(\n {\n name,\n description,\n args,\n state = 'pending',\n busy = false,\n onApprove,\n onDeny,\n approveLabel = 'Approve',\n denyLabel = 'Deny',\n className,\n ...props\n },\n ref,\n) {\n return (\n <div\n ref={ref}\n data-slot=\"tool-approval\"\n aria-busy={busy || undefined}\n data-state={state}\n className={cn(\n 'grid min-w-0 animate-in gap-3 rounded-md border p-3 text-[13px]',\n state === 'pending' ? 'border-warning/40 bg-warning/5' : 'bg-surface',\n className,\n )}\n {...props}\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <Wrench className=\"size-3.5 text-muted-foreground\" aria-hidden />\n <span className=\"min-w-0 break-all font-mono text-[12.5px]\">{name}</span>\n <span className=\"ml-auto text-xs text-muted-foreground\">\n {state === 'pending'\n ? 'Needs your approval'\n : state === 'approved'\n ? 'Approved'\n : 'Denied'}\n </span>\n </div>\n <p className=\"text-sm\">{description}</p>\n {args !== undefined && (\n <pre className=\"overflow-x-auto rounded-sm bg-muted/60 p-2 font-mono text-[12px] leading-5\">\n {json(args)}\n </pre>\n )}\n {state === 'pending' && (\n <div className=\"flex justify-end gap-2\">\n <Button size=\"sm\" variant=\"outline\" onClick={onDeny} disabled={busy || !onDeny}>\n {denyLabel}\n </Button>\n <Button size=\"sm\" onClick={onApprove} disabled={busy || !onApprove}>\n {approveLabel}\n </Button>\n </div>\n )}\n </div>\n );\n});\n\nexport { ToolApproval, ToolCall };\n"],"mappings":"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"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
export interface ToolbarProps extends React.ComponentProps<'div'> {
|
|
3
|
+
/** Names the group for screen readers ("List tools"). */
|
|
4
|
+
'aria-label'?: string;
|
|
5
|
+
}
|
|
6
|
+
/** A labelled group of controls; Tab moves between them as in any form. */
|
|
7
|
+
declare const Toolbar: React.ForwardRefExoticComponent<Omit<ToolbarProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
8
|
+
declare const ToolbarGroup: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
9
|
+
declare const ToolbarSeparator: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
10
|
+
declare const ToolbarSpacer: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
6
11
|
export { Toolbar, ToolbarGroup, ToolbarSeparator, ToolbarSpacer };
|
|
@@ -1,39 +1,44 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../lib/utils.js";
|
|
3
|
-
import "react";
|
|
3
|
+
import * as React from "react";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
//#region src/components/toolbar.tsx
|
|
6
|
-
|
|
6
|
+
/** A labelled group of controls; Tab moves between them as in any form. */
|
|
7
|
+
var Toolbar = React.forwardRef(function Toolbar({ className, ...props }, ref) {
|
|
7
8
|
return /* @__PURE__ */ jsx("div", {
|
|
9
|
+
ref,
|
|
8
10
|
"data-slot": "toolbar",
|
|
9
11
|
role: "group",
|
|
10
12
|
className: cn("flex min-h-10 flex-wrap items-center gap-1 rounded-lg border border-border bg-surface p-1.5 shadow-xs", className),
|
|
11
13
|
...props
|
|
12
14
|
});
|
|
13
|
-
}
|
|
14
|
-
function ToolbarGroup({ className, ...props }) {
|
|
15
|
+
});
|
|
16
|
+
var ToolbarGroup = React.forwardRef(function ToolbarGroup({ className, ...props }, ref) {
|
|
15
17
|
return /* @__PURE__ */ jsx("div", {
|
|
18
|
+
ref,
|
|
16
19
|
"data-slot": "toolbar-group",
|
|
17
20
|
className: cn("flex items-center gap-1", className),
|
|
18
21
|
...props
|
|
19
22
|
});
|
|
20
|
-
}
|
|
21
|
-
function ToolbarSeparator({ className, ...props }) {
|
|
23
|
+
});
|
|
24
|
+
var ToolbarSeparator = React.forwardRef(function ToolbarSeparator({ className, ...props }, ref) {
|
|
22
25
|
return /* @__PURE__ */ jsx("div", {
|
|
26
|
+
ref,
|
|
23
27
|
"data-slot": "toolbar-separator",
|
|
24
28
|
"aria-hidden": "true",
|
|
25
29
|
className: cn("mx-1 h-5 w-px bg-border", className),
|
|
26
30
|
...props
|
|
27
31
|
});
|
|
28
|
-
}
|
|
29
|
-
function ToolbarSpacer({ className, ...props }) {
|
|
32
|
+
});
|
|
33
|
+
var ToolbarSpacer = React.forwardRef(function ToolbarSpacer({ className, ...props }, ref) {
|
|
30
34
|
return /* @__PURE__ */ jsx("div", {
|
|
35
|
+
ref,
|
|
31
36
|
"data-slot": "toolbar-spacer",
|
|
32
37
|
"aria-hidden": "true",
|
|
33
38
|
className: cn("min-w-2 flex-1", className),
|
|
34
39
|
...props
|
|
35
40
|
});
|
|
36
|
-
}
|
|
41
|
+
});
|
|
37
42
|
//#endregion
|
|
38
43
|
export { Toolbar, ToolbarGroup, ToolbarSeparator, ToolbarSpacer };
|
|
39
44
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolbar.js","names":[],"sources":["../../src/components/toolbar.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\
|
|
1
|
+
{"version":3,"file":"toolbar.js","names":[],"sources":["../../src/components/toolbar.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nexport interface ToolbarProps extends React.ComponentProps<'div'> {\n /** Names the group for screen readers (\"List tools\"). */\n 'aria-label'?: string;\n}\n\n/** A labelled group of controls; Tab moves between them as in any form. */\nconst Toolbar = React.forwardRef<HTMLDivElement, ToolbarProps>(function Toolbar(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n data-slot=\"toolbar\"\n role=\"group\"\n className={cn(\n 'flex min-h-10 flex-wrap items-center gap-1 rounded-lg border border-border bg-surface p-1.5 shadow-xs',\n className,\n )}\n {...props}\n />\n );\n});\n\nconst ToolbarGroup = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(\n function ToolbarGroup({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n data-slot=\"toolbar-group\"\n className={cn('flex items-center gap-1', className)}\n {...props}\n />\n );\n },\n);\n\nconst ToolbarSeparator = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(\n function ToolbarSeparator({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n data-slot=\"toolbar-separator\"\n aria-hidden=\"true\"\n className={cn('mx-1 h-5 w-px bg-border', className)}\n {...props}\n />\n );\n },\n);\n\nconst ToolbarSpacer = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(\n function ToolbarSpacer({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n data-slot=\"toolbar-spacer\"\n aria-hidden=\"true\"\n className={cn('min-w-2 flex-1', className)}\n {...props}\n />\n );\n },\n);\n\nexport { Toolbar, ToolbarGroup, ToolbarSeparator, ToolbarSpacer };\n"],"mappings":";;;;;;AAUA,IAAM,UAAU,MAAM,WAAyC,SAAS,QACtE,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,aAAU;EACV,MAAK;EACL,WAAW,GACT,yGACA,SACF;EACA,GAAI;CACL,CAAA;AAEL,CAAC;AAED,IAAM,eAAe,MAAM,WACzB,SAAS,aAAa,EAAE,WAAW,GAAG,SAAS,KAAK;CAClD,OACE,oBAAC,OAAD;EACO;EACL,aAAU;EACV,WAAW,GAAG,2BAA2B,SAAS;EAClD,GAAI;CACL,CAAA;AAEL,CACF;AAEA,IAAM,mBAAmB,MAAM,WAC7B,SAAS,iBAAiB,EAAE,WAAW,GAAG,SAAS,KAAK;CACtD,OACE,oBAAC,OAAD;EACO;EACL,aAAU;EACV,eAAY;EACZ,WAAW,GAAG,2BAA2B,SAAS;EAClD,GAAI;CACL,CAAA;AAEL,CACF;AAEA,IAAM,gBAAgB,MAAM,WAC1B,SAAS,cAAc,EAAE,WAAW,GAAG,SAAS,KAAK;CACnD,OACE,oBAAC,OAAD;EACO;EACL,aAAU;EACV,eAAY;EACZ,WAAW,GAAG,kBAAkB,SAAS;EACzC,GAAI;CACL,CAAA;AAEL,CACF"}
|
|
@@ -3,6 +3,7 @@ import * as TooltipPrimitive from '../primitives/vendor/radix/react-tooltip';
|
|
|
3
3
|
declare function TooltipProvider({ delayDuration, ...props }: React.ComponentProps<typeof TooltipPrimitive.Provider>): React.JSX.Element;
|
|
4
4
|
/** Wraps its own provider, so a lone tooltip works without app-level setup. */
|
|
5
5
|
declare function Tooltip(props: React.ComponentProps<typeof TooltipPrimitive.Root>): React.JSX.Element;
|
|
6
|
-
declare
|
|
7
|
-
|
|
6
|
+
declare const TooltipTrigger: React.ForwardRefExoticComponent<Omit<TooltipPrimitive.TooltipTriggerProps & React.RefAttributes<HTMLButtonElement>, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
7
|
+
/** A dark label on any theme, 8px radius, elevation 3. */
|
|
8
|
+
declare const TooltipContent: React.ForwardRefExoticComponent<Omit<TooltipPrimitive.TooltipContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
8
9
|
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|
|
@@ -26,21 +26,24 @@ function Tooltip(props) {
|
|
|
26
26
|
...props
|
|
27
27
|
}) });
|
|
28
28
|
}
|
|
29
|
-
function TooltipTrigger(props) {
|
|
29
|
+
var TooltipTrigger = React.forwardRef(function TooltipTrigger(props, ref) {
|
|
30
30
|
return /* @__PURE__ */ jsx(Trigger, {
|
|
31
|
+
ref,
|
|
31
32
|
"data-slot": "tooltip-trigger",
|
|
32
33
|
...props
|
|
33
34
|
});
|
|
34
|
-
}
|
|
35
|
-
|
|
35
|
+
});
|
|
36
|
+
/** A dark label on any theme, 8px radius, elevation 3. */
|
|
37
|
+
var TooltipContent = React.forwardRef(function TooltipContent({ className, sideOffset = 6, children, ...props }, ref) {
|
|
36
38
|
return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs(Content, {
|
|
39
|
+
ref,
|
|
37
40
|
"data-slot": "tooltip-content",
|
|
38
41
|
sideOffset,
|
|
39
42
|
className: cn("z-50 w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) animate-in [--bl-scale:0.98] data-[side=bottom]:[--bl-ty:-4px] data-[side=top]:[--bl-ty:4px] data-[side=left]:[--bl-tx:4px] data-[side=right]:[--bl-tx:-4px] rounded-sm border border-white/10 bg-code px-2 py-1 text-[11.5px] leading-4 font-medium text-balance text-code-foreground shadow-md data-[state=closed]:animate-out", className),
|
|
40
43
|
...props,
|
|
41
44
|
children: [children, /* @__PURE__ */ jsx(Arrow, { className: "fill-code" })]
|
|
42
45
|
}) });
|
|
43
|
-
}
|
|
46
|
+
});
|
|
44
47
|
//#endregion
|
|
45
48
|
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|
|
46
49
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip.js","names":["TooltipPrimitive.Provider","TooltipPrimitive.Root","TooltipPrimitive.Trigger","TooltipPrimitive.Portal","TooltipPrimitive.Content","TooltipPrimitive.Arrow"],"sources":["../../src/components/tooltip.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport * as TooltipPrimitive from '../primitives/vendor/radix/react-tooltip';\n\nconst HasTooltipProvider = React.createContext(false);\n\nfunction TooltipProvider({\n delayDuration = 200,\n ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\n return (\n <HasTooltipProvider.Provider value={true}>\n <TooltipPrimitive.Provider\n data-slot=\"tooltip-provider\"\n delayDuration={delayDuration}\n {...props}\n />\n </HasTooltipProvider.Provider>\n );\n}\n\n/** Wraps its own provider, so a lone tooltip works without app-level setup. */\nfunction Tooltip(props: React.ComponentProps<typeof TooltipPrimitive.Root>) {\n const hasProvider = React.useContext(HasTooltipProvider);\n if (hasProvider) return <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />;\n return (\n <TooltipProvider>\n <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />\n </TooltipProvider>\n );\n}\n\
|
|
1
|
+
{"version":3,"file":"tooltip.js","names":["TooltipPrimitive.Provider","TooltipPrimitive.Root","TooltipPrimitive.Trigger","TooltipPrimitive.Portal","TooltipPrimitive.Content","TooltipPrimitive.Arrow"],"sources":["../../src/components/tooltip.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport * as TooltipPrimitive from '../primitives/vendor/radix/react-tooltip';\n\nconst HasTooltipProvider = React.createContext(false);\n\nfunction TooltipProvider({\n delayDuration = 200,\n ...props\n}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {\n return (\n <HasTooltipProvider.Provider value={true}>\n <TooltipPrimitive.Provider\n data-slot=\"tooltip-provider\"\n delayDuration={delayDuration}\n {...props}\n />\n </HasTooltipProvider.Provider>\n );\n}\n\n/** Wraps its own provider, so a lone tooltip works without app-level setup. */\nfunction Tooltip(props: React.ComponentProps<typeof TooltipPrimitive.Root>) {\n const hasProvider = React.useContext(HasTooltipProvider);\n if (hasProvider) return <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />;\n return (\n <TooltipProvider>\n <TooltipPrimitive.Root data-slot=\"tooltip\" {...props} />\n </TooltipProvider>\n );\n}\n\nconst TooltipTrigger = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<typeof TooltipPrimitive.Trigger>\n>(function TooltipTrigger(props, ref) {\n return <TooltipPrimitive.Trigger ref={ref} data-slot=\"tooltip-trigger\" {...props} />;\n});\n\n/** A dark label on any theme, 8px radius, elevation 3. */\nconst TooltipContent = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof TooltipPrimitive.Content>\n>(function TooltipContent({ className, sideOffset = 6, children, ...props }, ref) {\n return (\n <TooltipPrimitive.Portal>\n <TooltipPrimitive.Content\n ref={ref}\n data-slot=\"tooltip-content\"\n sideOffset={sideOffset}\n className={cn(\n 'z-50 w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) animate-in [--bl-scale:0.98] data-[side=bottom]:[--bl-ty:-4px] data-[side=top]:[--bl-ty:4px] data-[side=left]:[--bl-tx:4px] data-[side=right]:[--bl-tx:-4px] rounded-sm border border-white/10 bg-code px-2 py-1 text-[11.5px] leading-4 font-medium text-balance text-code-foreground shadow-md data-[state=closed]:animate-out',\n className,\n )}\n {...props}\n >\n {children}\n <TooltipPrimitive.Arrow className=\"fill-code\" />\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n );\n});\n\nexport { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };\n"],"mappings":";;;;;;AAKA,IAAM,qBAAqB,MAAM,cAAc,KAAK;AAEpD,SAAS,gBAAgB,EACvB,gBAAgB,KAChB,GAAG,SACsD;CACzD,OACE,oBAAC,mBAAmB,UAApB;EAA6B,OAAO;EAClC,UAAA,oBAACA,UAAD;GACE,aAAU;GACK;GACf,GAAI;EACL,CAAA;CAC0B,CAAA;AAEjC;;AAGA,SAAS,QAAQ,OAA2D;CAE1E,IADoB,MAAM,WAAW,kBACjC,GAAa,OAAO,oBAACC,MAAD;EAAuB,aAAU;EAAU,GAAI;CAAQ,CAAA;CAC/E,OACE,oBAAC,iBAAD,EAAA,UACE,oBAACA,MAAD;EAAuB,aAAU;EAAU,GAAI;CAAQ,CAAA,EACxC,CAAA;AAErB;AAEA,IAAM,iBAAiB,MAAM,WAG3B,SAAS,eAAe,OAAO,KAAK;CACpC,OAAO,oBAACC,SAAD;EAA+B;EAAK,aAAU;EAAkB,GAAI;CAAQ,CAAA;AACrF,CAAC;;AAGD,IAAM,iBAAiB,MAAM,WAG3B,SAAS,eAAe,EAAE,WAAW,aAAa,GAAG,UAAU,GAAG,SAAS,KAAK;CAChF,OACE,oBAACC,QAAD,EAAA,UACE,qBAACC,SAAD;EACO;EACL,aAAU;EACE;EACZ,WAAW,GACT,0YACA,SACF;EACA,GAAI;EARN,UAAA,CAUG,UACD,oBAACC,OAAD,EAAwB,WAAU,YAAa,CAAA,CACvB;CACH,CAAA,EAAA,CAAA;AAE7B,CAAC"}
|
package/dist/components/tour.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { cn, focusRingClasses, touchTargetClasses } from "../lib/utils.js";
|
|
3
3
|
import { Root as Slot } from "../primitives/vendor/radix/react-slot/slot.js";
|
|
4
4
|
import { Button } from "./button.js";
|
|
5
5
|
import { Anchor, Content, Portal, Root } from "../primitives/vendor/radix/react-popover/popover.js";
|
|
@@ -249,7 +249,7 @@ function Tour({ steps, open, step, onStepChange, onEnd, missingTarget = "center"
|
|
|
249
249
|
type: "button",
|
|
250
250
|
"aria-label": labels.close,
|
|
251
251
|
onClick: () => onEnd("close", step),
|
|
252
|
-
className: "-me-1 -mt-1 flex size-7 shrink-0 items-center justify-center rounded-sm text-muted-foreground
|
|
252
|
+
className: cn("-me-1 -mt-1 flex size-7 shrink-0 items-center justify-center rounded-sm text-muted-foreground hover:bg-muted hover:text-foreground", focusRingClasses, touchTargetClasses),
|
|
253
253
|
children: /* @__PURE__ */ jsx(X, {
|
|
254
254
|
"aria-hidden": true,
|
|
255
255
|
className: "size-4"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tour.js","names":["Slot.Root","_Fragment","PopoverPrimitive.Root","PopoverPrimitive.Anchor","PopoverPrimitive.Portal","PopoverPrimitive.Content"],"sources":["../../src/components/tour.tsx"],"sourcesContent":["import X from '@burtson-labs/icons/react/x';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport * as PopoverPrimitive from '../primitives/vendor/radix/react-popover';\nimport * as Slot from '../primitives/vendor/radix/react-slot';\n\nimport { Button } from './button';\nimport { surfaceClasses } from './popover';\n\n/**\n * A registered target id (see TourAnchor), or a function returning the\n * element. Ids are stable; never target generated class names or nth-child.\n */\nexport type TourTarget = string | (() => Element | null);\n\n/** Marks an element as a tour target. Merges onto its one child. */\nfunction TourAnchor({ id, children }: { id: string; children: React.ReactElement }) {\n return <Slot.Root data-tour-target={id}>{children}</Slot.Root>;\n}\n\n/** The same marker as TourAnchor, for elements you render yourself. */\nexport const tourTarget = (id: string) => ({ 'data-tour-target': id });\n\nconst isVisible = (el: Element | null): el is Element =>\n Boolean(el?.isConnected && el.getClientRects().length > 0);\n\nexport function findTourTarget(target: TourTarget | undefined): Element | null {\n if (!target) return null;\n const el =\n typeof target === 'function'\n ? target()\n : (Array.from(document.querySelectorAll('[data-tour-target]')).find(\n (node) => node.getAttribute('data-tour-target') === target,\n ) ?? null);\n return isVisible(el) ? el : null;\n}\n\n/**\n * Resolves when the target is mounted and visible, or with null after\n * `timeoutMs` or when `signal` aborts. Handles targets that render late\n * (lazy panels, async data) without polling forever.\n */\nexport function waitForTourTarget(\n target: TourTarget | undefined,\n timeoutMs: number,\n signal: AbortSignal,\n): Promise<Element | null> {\n return new Promise((resolve) => {\n const now = findTourTarget(target);\n if (now || !target || timeoutMs <= 0 || signal.aborted) return resolve(now);\n let done = false;\n const finish = (el: Element | null) => {\n if (done) return;\n done = true;\n observer.disconnect();\n clearInterval(poll);\n clearTimeout(timer);\n signal.removeEventListener('abort', abort);\n resolve(el);\n };\n const check = () => {\n const el = findTourTarget(target);\n if (el) finish(el);\n };\n const abort = () => finish(null);\n const observer = new MutationObserver(check);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true });\n // Visibility can change through CSS alone, which no mutation reports.\n const poll = setInterval(check, 150);\n const timer = setTimeout(() => finish(null), timeoutMs);\n signal.addEventListener('abort', abort);\n });\n}\n\n/**\n * Escape belongs to whatever the person is typing in (a field, CodeMirror's\n * contenteditable, xterm's hidden textarea), not to the tour.\n */\nconst typingElsewhere = (container: HTMLElement | null) => {\n const active = document.activeElement;\n if (!(active instanceof HTMLElement) || container?.contains(active)) return false;\n return active.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(active.tagName);\n};\n\nconst prefersReducedMotion = () =>\n typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n/** Follows an element's box across scroll and resize; null until measured. */\nfunction useRect(el: Element | null) {\n const [measured, setMeasured] = React.useState<{ el: Element; rect: DOMRect } | null>(null);\n React.useEffect(() => {\n if (!el) return;\n let frame = 0;\n const update = () => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => setMeasured({ el, rect: el.getBoundingClientRect() }));\n };\n update();\n const ro = new ResizeObserver(update);\n ro.observe(el);\n window.addEventListener('scroll', update, true);\n window.addEventListener('resize', update);\n return () => {\n cancelAnimationFrame(frame);\n ro.disconnect();\n window.removeEventListener('scroll', update, true);\n window.removeEventListener('resize', update);\n };\n }, [el]);\n return measured && measured.el === el ? measured.rect : null;\n}\n\n/** A ring around the target, optionally dimming the rest of the page. Never blocks clicks. */\nfunction TourHighlight({ rect, dim }: { rect: DOMRect; dim: boolean }) {\n const pad = 4;\n return (\n <div\n aria-hidden\n data-slot=\"tour-highlight\"\n className={cn(\n 'pointer-events-none fixed z-40 rounded-md ring-2 ring-brand motion-safe:transition-[top,left,width,height] motion-safe:duration-200',\n dim && 'shadow-[0_0_0_9999px_rgb(10_8_14/0.45)]',\n )}\n style={{\n top: rect.top - pad,\n left: rect.left - pad,\n width: rect.width + pad * 2,\n height: rect.height + pad * 2,\n }}\n />\n );\n}\n\n/** A zero-size box a third of the way down the viewport, for steps without a target. */\nconst centerRect = () =>\n new DOMRect(window.innerWidth / 2, Math.max(80, window.innerHeight / 3), 0, 0);\n\nconst contentClasses = cn(\n surfaceClasses,\n 'w-80 max-w-[calc(100vw-2rem)] p-4 text-[13px] outline-none origin-(--radix-popover-content-transform-origin)',\n);\n\nexport interface TourStep {\n id: string;\n /** Omit for a centred step with no anchor. */\n target?: TourTarget;\n title: React.ReactNode;\n content: React.ReactNode;\n side?: 'top' | 'right' | 'bottom' | 'left';\n align?: 'start' | 'center' | 'end';\n /**\n * Reveal what the step points at (open a panel, switch a route) before it\n * shows. Aborted if the person leaves the step first; a rejection shows the\n * step centred rather than stranding the tour.\n */\n prepare?: (signal: AbortSignal) => void | Promise<void>;\n}\n\nexport type TourEndReason = 'complete' | 'skip' | 'close' | 'escape';\n\nexport interface TourLabels {\n next: string;\n back: string;\n skip: string;\n done: string;\n close: string;\n progress: (step: number, total: number) => string;\n}\n\nconst defaultLabels: TourLabels = {\n next: 'Next',\n back: 'Back',\n skip: 'Skip tour',\n done: 'Done',\n close: 'Close tour',\n progress: (step, total) => `${step} of ${total}`,\n};\n\nexport interface TourProps {\n steps: TourStep[];\n open: boolean;\n /** Controlled, zero-based. */\n step: number;\n onStepChange: (step: number) => void;\n /** The tour is over. Store dismissal or completion yourself, keyed by tour id and revision. */\n onEnd: (reason: TourEndReason, step: number) => void;\n /** `center` (default) shows a step whose target never appears in the middle; `skip` moves past it. */\n missingTarget?: 'center' | 'skip';\n /** How long to wait for a late target. Default 2000ms. */\n waitMs?: number;\n /** Dim the page outside the target. Default true. */\n dim?: boolean;\n labels?: Partial<TourLabels>;\n /** Where focus goes if the element that started the tour is gone. */\n fallbackFocus?: () => HTMLElement | null;\n}\n\n/**\n * A guided sequence of anchored steps. It is non-modal: the page stays\n * usable and outside clicks do not end it. Focus moves into each step,\n * Escape ends the tour (unless the person is typing in a field or editor\n * outside it), and focus returns to where it was when the tour started.\n * The app owns the copy, the eligibility rules and what it persists.\n */\nfunction Tour({\n steps,\n open,\n step,\n onStepChange,\n onEnd,\n missingTarget = 'center',\n waitMs = 2000,\n dim = true,\n labels: labelOverrides,\n fallbackFocus,\n}: TourProps) {\n const labels = { ...defaultLabels, ...labelOverrides };\n const current = steps[step];\n // The resolved step: which step it is for, and its target (null: centred).\n const stepKey = current ? `${step}:${current.id}` : '';\n const [resolved, setResolved] = React.useState<{ key: string; el: Element | null } | null>(null);\n const ready = open && resolved?.key === stepKey;\n const target = ready ? (resolved?.el ?? null) : null;\n const rect = useRect(target);\n const contentRef = React.useRef<HTMLDivElement>(null);\n // Radix mounts the content after the popper measures, so focus follows the node.\n const [contentEl, setContentEl] = React.useState<HTMLDivElement | null>(null);\n const returnFocus = React.useRef<HTMLElement | null>(null);\n const direction = React.useRef(1);\n const titleId = React.useId();\n const bodyId = React.useId();\n\n // Remember who started the tour; hand focus back when it ends.\n React.useLayoutEffect(() => {\n if (!open) return;\n const active = document.activeElement;\n returnFocus.current = active instanceof HTMLElement && active !== document.body ? active : null;\n return () => {\n const back = returnFocus.current;\n const el = back && isVisible(back) ? back : (fallbackFocus?.() ?? null);\n el?.focus();\n };\n // fallbackFocus is read at close time; re-running on its identity would steal focus.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n\n React.useEffect(() => {\n if (!open || !current) return;\n const controller = new AbortController();\n const { signal } = controller;\n void (async () => {\n let prepared = true;\n try {\n await current.prepare?.(signal);\n } catch {\n prepared = false;\n }\n if (signal.aborted) return;\n const el = prepared ? await waitForTourTarget(current.target, waitMs, signal) : null;\n if (signal.aborted) return;\n if (!el && current.target && missingTarget === 'skip') {\n const next = step + direction.current;\n if (next >= 0 && next < steps.length) return onStepChange(next);\n }\n if (el)\n el.scrollIntoView?.({\n block: 'nearest',\n behavior: prefersReducedMotion() ? 'auto' : 'smooth',\n });\n setResolved({ key: stepKey, el });\n })();\n return () => controller.abort();\n // steps/onStepChange identity changes should not restart a step in progress.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, stepKey, waitMs, missingTarget]);\n\n // Shown once resolved and, for an anchored step, measured.\n const shown = ready && (!target || rect !== null);\n React.useEffect(() => {\n if (shown) contentEl?.focus();\n }, [shown, step, contentEl]);\n\n if (!open || !current) return null;\n const last = step === steps.length - 1;\n const go = (to: number) => {\n direction.current = to < step ? -1 : 1;\n onStepChange(to);\n };\n // Wait one frame for the target's box rather than flashing the step centred.\n if (!shown && ready) return null;\n const anchor = rect ?? centerRect();\n\n return (\n <>\n {ready && rect && <TourHighlight rect={rect} dim={dim} />}\n <PopoverPrimitive.Root open={shown} modal={false}>\n <PopoverPrimitive.Anchor\n virtualRef={{ current: { getBoundingClientRect: () => anchor } }}\n />\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={(el) => {\n contentRef.current = el;\n setContentEl(el);\n }}\n data-slot=\"tour\"\n data-step={current.id}\n tabIndex={-1}\n aria-labelledby={titleId}\n aria-describedby={bodyId}\n side={rect ? (current.side ?? 'bottom') : 'bottom'}\n align={rect ? (current.align ?? 'center') : 'center'}\n sideOffset={10}\n collisionPadding={12}\n className={contentClasses}\n onOpenAutoFocus={(event) => event.preventDefault()}\n onCloseAutoFocus={(event) => event.preventDefault()}\n onInteractOutside={(event) => event.preventDefault()}\n onEscapeKeyDown={(event) => {\n if (typingElsewhere(contentRef.current)) event.preventDefault();\n else onEnd('escape', step);\n }}\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-xs text-muted-foreground\">\n {labels.progress(step + 1, steps.length)}\n </p>\n <h2 id={titleId} className=\"mt-0.5 text-sm font-semibold text-foreground\">\n {current.title}\n </h2>\n </div>\n <button\n type=\"button\"\n aria-label={labels.close}\n onClick={() => onEnd('close', step)}\n className=\"-me-1 -mt-1 flex size-7 shrink-0 items-center justify-center rounded-sm text-muted-foreground outline-none hover:bg-muted hover:text-foreground focus-visible:shadow-[inset_0_0_0_1px_var(--ring)]\"\n >\n <X aria-hidden className=\"size-4\" />\n </button>\n </div>\n <div id={bodyId} className=\"mt-2 text-muted-foreground\">\n {current.content}\n </div>\n <div className=\"mt-4 flex items-center gap-2\">\n {!last && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"-ms-2\"\n onClick={() => onEnd('skip', step)}\n >\n {labels.skip}\n </Button>\n )}\n <div className=\"ms-auto flex gap-2\">\n {step > 0 && (\n <Button variant=\"outline\" size=\"sm\" onClick={() => go(step - 1)}>\n {labels.back}\n </Button>\n )}\n <Button size=\"sm\" onClick={() => (last ? onEnd('complete', step) : go(step + 1))}>\n {last ? labels.done : labels.next}\n </Button>\n </div>\n </div>\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n </>\n );\n}\n\nexport interface SpotlightProps {\n target: TourTarget;\n open: boolean;\n onDismiss: (reason: 'dismiss' | 'escape') => void;\n title: React.ReactNode;\n children: React.ReactNode;\n side?: 'top' | 'right' | 'bottom' | 'left';\n align?: 'start' | 'center' | 'end';\n /** Default \"Got it\". */\n dismissLabel?: string;\n /** An extra button, e.g. \"Add a provider\". */\n action?: React.ReactNode;\n /** Ring the target. Default true. */\n highlight?: boolean;\n className?: string;\n}\n\n/**\n * One contextual hint beside a target: a new feature, a setup nudge. It is\n * informational, so it does not take focus or block the page. Escape (unless\n * the person is typing elsewhere) or the dismiss button closes it. If the target is\n * not on screen, nothing renders.\n */\nfunction Spotlight({\n target,\n open,\n onDismiss,\n title,\n children,\n side = 'bottom',\n align = 'center',\n dismissLabel = 'Got it',\n action,\n highlight = true,\n className,\n}: SpotlightProps) {\n const key = typeof target === 'string' ? target : 'fn';\n const [found, setFound] = React.useState<{ key: string; el: Element | null } | null>(null);\n const el = open && found?.key === key ? found.el : null;\n const rect = useRect(el);\n const titleId = React.useId();\n const contentRef = React.useRef<HTMLDivElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n const controller = new AbortController();\n void waitForTourTarget(target, 2000, controller.signal).then((found) => {\n if (!controller.signal.aborted) setFound({ key, el: found });\n });\n return () => controller.abort();\n // A new function identity for `target` should not re-run the lookup.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, key]);\n\n if (!open || !el || !rect) return null;\n return (\n <>\n {highlight && <TourHighlight rect={rect} dim={false} />}\n <PopoverPrimitive.Root open modal={false}>\n <PopoverPrimitive.Anchor virtualRef={{ current: { getBoundingClientRect: () => rect } }} />\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={contentRef}\n data-slot=\"spotlight\"\n role=\"note\"\n aria-labelledby={titleId}\n side={side}\n align={align}\n sideOffset={10}\n collisionPadding={12}\n className={cn(contentClasses, 'w-72', className)}\n onOpenAutoFocus={(event) => event.preventDefault()}\n onCloseAutoFocus={(event) => event.preventDefault()}\n onInteractOutside={(event) => event.preventDefault()}\n onEscapeKeyDown={(event) => {\n if (typingElsewhere(contentRef.current)) event.preventDefault();\n else onDismiss('escape');\n }}\n >\n <span className=\"sr-only\" aria-live=\"polite\">\n Tip: {title}\n </span>\n <p id={titleId} className=\"text-sm font-semibold text-foreground\">\n {title}\n </p>\n <div className=\"mt-1.5 text-muted-foreground\">{children}</div>\n <div className=\"mt-3 flex justify-end gap-2\">\n {action}\n <Button\n size=\"sm\"\n variant={action ? 'ghost' : 'default'}\n onClick={() => onDismiss('dismiss')}\n >\n {dismissLabel}\n </Button>\n </div>\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n </>\n );\n}\n\nexport { Spotlight, Tour, TourAnchor };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tour.js","names":["Slot.Root","_Fragment","PopoverPrimitive.Root","PopoverPrimitive.Anchor","PopoverPrimitive.Portal","PopoverPrimitive.Content"],"sources":["../../src/components/tour.tsx"],"sourcesContent":["import X from '@burtson-labs/icons/react/x';\nimport * as React from 'react';\n\nimport { cn, focusRingClasses, touchTargetClasses } from '../lib/utils';\nimport * as PopoverPrimitive from '../primitives/vendor/radix/react-popover';\nimport * as Slot from '../primitives/vendor/radix/react-slot';\n\nimport { Button } from './button';\nimport { surfaceClasses } from './popover';\n\n/**\n * A registered target id (see TourAnchor), or a function returning the\n * element. Ids are stable; never target generated class names or nth-child.\n */\nexport type TourTarget = string | (() => Element | null);\n\n/** Marks an element as a tour target. Merges onto its one child. */\nfunction TourAnchor({ id, children }: { id: string; children: React.ReactElement }) {\n return <Slot.Root data-tour-target={id}>{children}</Slot.Root>;\n}\n\n/** The same marker as TourAnchor, for elements you render yourself. */\nexport const tourTarget = (id: string) => ({ 'data-tour-target': id });\n\nconst isVisible = (el: Element | null): el is Element =>\n Boolean(el?.isConnected && el.getClientRects().length > 0);\n\nexport function findTourTarget(target: TourTarget | undefined): Element | null {\n if (!target) return null;\n const el =\n typeof target === 'function'\n ? target()\n : (Array.from(document.querySelectorAll('[data-tour-target]')).find(\n (node) => node.getAttribute('data-tour-target') === target,\n ) ?? null);\n return isVisible(el) ? el : null;\n}\n\n/**\n * Resolves when the target is mounted and visible, or with null after\n * `timeoutMs` or when `signal` aborts. Handles targets that render late\n * (lazy panels, async data) without polling forever.\n */\nexport function waitForTourTarget(\n target: TourTarget | undefined,\n timeoutMs: number,\n signal: AbortSignal,\n): Promise<Element | null> {\n return new Promise((resolve) => {\n const now = findTourTarget(target);\n if (now || !target || timeoutMs <= 0 || signal.aborted) return resolve(now);\n let done = false;\n const finish = (el: Element | null) => {\n if (done) return;\n done = true;\n observer.disconnect();\n clearInterval(poll);\n clearTimeout(timer);\n signal.removeEventListener('abort', abort);\n resolve(el);\n };\n const check = () => {\n const el = findTourTarget(target);\n if (el) finish(el);\n };\n const abort = () => finish(null);\n const observer = new MutationObserver(check);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true });\n // Visibility can change through CSS alone, which no mutation reports.\n const poll = setInterval(check, 150);\n const timer = setTimeout(() => finish(null), timeoutMs);\n signal.addEventListener('abort', abort);\n });\n}\n\n/**\n * Escape belongs to whatever the person is typing in (a field, CodeMirror's\n * contenteditable, xterm's hidden textarea), not to the tour.\n */\nconst typingElsewhere = (container: HTMLElement | null) => {\n const active = document.activeElement;\n if (!(active instanceof HTMLElement) || container?.contains(active)) return false;\n return active.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(active.tagName);\n};\n\nconst prefersReducedMotion = () =>\n typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n/** Follows an element's box across scroll and resize; null until measured. */\nfunction useRect(el: Element | null) {\n const [measured, setMeasured] = React.useState<{ el: Element; rect: DOMRect } | null>(null);\n React.useEffect(() => {\n if (!el) return;\n let frame = 0;\n const update = () => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => setMeasured({ el, rect: el.getBoundingClientRect() }));\n };\n update();\n const ro = new ResizeObserver(update);\n ro.observe(el);\n window.addEventListener('scroll', update, true);\n window.addEventListener('resize', update);\n return () => {\n cancelAnimationFrame(frame);\n ro.disconnect();\n window.removeEventListener('scroll', update, true);\n window.removeEventListener('resize', update);\n };\n }, [el]);\n return measured && measured.el === el ? measured.rect : null;\n}\n\n/** A ring around the target, optionally dimming the rest of the page. Never blocks clicks. */\nfunction TourHighlight({ rect, dim }: { rect: DOMRect; dim: boolean }) {\n const pad = 4;\n return (\n <div\n aria-hidden\n data-slot=\"tour-highlight\"\n className={cn(\n 'pointer-events-none fixed z-40 rounded-md ring-2 ring-brand motion-safe:transition-[top,left,width,height] motion-safe:duration-200',\n dim && 'shadow-[0_0_0_9999px_rgb(10_8_14/0.45)]',\n )}\n style={{\n top: rect.top - pad,\n left: rect.left - pad,\n width: rect.width + pad * 2,\n height: rect.height + pad * 2,\n }}\n />\n );\n}\n\n/** A zero-size box a third of the way down the viewport, for steps without a target. */\nconst centerRect = () =>\n new DOMRect(window.innerWidth / 2, Math.max(80, window.innerHeight / 3), 0, 0);\n\nconst contentClasses = cn(\n surfaceClasses,\n 'w-80 max-w-[calc(100vw-2rem)] p-4 text-[13px] outline-none origin-(--radix-popover-content-transform-origin)',\n);\n\nexport interface TourStep {\n id: string;\n /** Omit for a centred step with no anchor. */\n target?: TourTarget;\n title: React.ReactNode;\n content: React.ReactNode;\n side?: 'top' | 'right' | 'bottom' | 'left';\n align?: 'start' | 'center' | 'end';\n /**\n * Reveal what the step points at (open a panel, switch a route) before it\n * shows. Aborted if the person leaves the step first; a rejection shows the\n * step centred rather than stranding the tour.\n */\n prepare?: (signal: AbortSignal) => void | Promise<void>;\n}\n\nexport type TourEndReason = 'complete' | 'skip' | 'close' | 'escape';\n\nexport interface TourLabels {\n next: string;\n back: string;\n skip: string;\n done: string;\n close: string;\n progress: (step: number, total: number) => string;\n}\n\nconst defaultLabels: TourLabels = {\n next: 'Next',\n back: 'Back',\n skip: 'Skip tour',\n done: 'Done',\n close: 'Close tour',\n progress: (step, total) => `${step} of ${total}`,\n};\n\nexport interface TourProps {\n steps: TourStep[];\n open: boolean;\n /** Controlled, zero-based. */\n step: number;\n onStepChange: (step: number) => void;\n /** The tour is over. Store dismissal or completion yourself, keyed by tour id and revision. */\n onEnd: (reason: TourEndReason, step: number) => void;\n /** `center` (default) shows a step whose target never appears in the middle; `skip` moves past it. */\n missingTarget?: 'center' | 'skip';\n /** How long to wait for a late target. Default 2000ms. */\n waitMs?: number;\n /** Dim the page outside the target. Default true. */\n dim?: boolean;\n labels?: Partial<TourLabels>;\n /** Where focus goes if the element that started the tour is gone. */\n fallbackFocus?: () => HTMLElement | null;\n}\n\n/**\n * A guided sequence of anchored steps. It is non-modal: the page stays\n * usable and outside clicks do not end it. Focus moves into each step,\n * Escape ends the tour (unless the person is typing in a field or editor\n * outside it), and focus returns to where it was when the tour started.\n * The app owns the copy, the eligibility rules and what it persists.\n */\nfunction Tour({\n steps,\n open,\n step,\n onStepChange,\n onEnd,\n missingTarget = 'center',\n waitMs = 2000,\n dim = true,\n labels: labelOverrides,\n fallbackFocus,\n}: TourProps) {\n const labels = { ...defaultLabels, ...labelOverrides };\n const current = steps[step];\n // The resolved step: which step it is for, and its target (null: centred).\n const stepKey = current ? `${step}:${current.id}` : '';\n const [resolved, setResolved] = React.useState<{ key: string; el: Element | null } | null>(null);\n const ready = open && resolved?.key === stepKey;\n const target = ready ? (resolved?.el ?? null) : null;\n const rect = useRect(target);\n const contentRef = React.useRef<HTMLDivElement>(null);\n // Radix mounts the content after the popper measures, so focus follows the node.\n const [contentEl, setContentEl] = React.useState<HTMLDivElement | null>(null);\n const returnFocus = React.useRef<HTMLElement | null>(null);\n const direction = React.useRef(1);\n const titleId = React.useId();\n const bodyId = React.useId();\n\n // Remember who started the tour; hand focus back when it ends.\n React.useLayoutEffect(() => {\n if (!open) return;\n const active = document.activeElement;\n returnFocus.current = active instanceof HTMLElement && active !== document.body ? active : null;\n return () => {\n const back = returnFocus.current;\n const el = back && isVisible(back) ? back : (fallbackFocus?.() ?? null);\n el?.focus();\n };\n // fallbackFocus is read at close time; re-running on its identity would steal focus.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n\n React.useEffect(() => {\n if (!open || !current) return;\n const controller = new AbortController();\n const { signal } = controller;\n void (async () => {\n let prepared = true;\n try {\n await current.prepare?.(signal);\n } catch {\n prepared = false;\n }\n if (signal.aborted) return;\n const el = prepared ? await waitForTourTarget(current.target, waitMs, signal) : null;\n if (signal.aborted) return;\n if (!el && current.target && missingTarget === 'skip') {\n const next = step + direction.current;\n if (next >= 0 && next < steps.length) return onStepChange(next);\n }\n if (el)\n el.scrollIntoView?.({\n block: 'nearest',\n behavior: prefersReducedMotion() ? 'auto' : 'smooth',\n });\n setResolved({ key: stepKey, el });\n })();\n return () => controller.abort();\n // steps/onStepChange identity changes should not restart a step in progress.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, stepKey, waitMs, missingTarget]);\n\n // Shown once resolved and, for an anchored step, measured.\n const shown = ready && (!target || rect !== null);\n React.useEffect(() => {\n if (shown) contentEl?.focus();\n }, [shown, step, contentEl]);\n\n if (!open || !current) return null;\n const last = step === steps.length - 1;\n const go = (to: number) => {\n direction.current = to < step ? -1 : 1;\n onStepChange(to);\n };\n // Wait one frame for the target's box rather than flashing the step centred.\n if (!shown && ready) return null;\n const anchor = rect ?? centerRect();\n\n return (\n <>\n {ready && rect && <TourHighlight rect={rect} dim={dim} />}\n <PopoverPrimitive.Root open={shown} modal={false}>\n <PopoverPrimitive.Anchor\n virtualRef={{ current: { getBoundingClientRect: () => anchor } }}\n />\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={(el) => {\n contentRef.current = el;\n setContentEl(el);\n }}\n data-slot=\"tour\"\n data-step={current.id}\n tabIndex={-1}\n aria-labelledby={titleId}\n aria-describedby={bodyId}\n side={rect ? (current.side ?? 'bottom') : 'bottom'}\n align={rect ? (current.align ?? 'center') : 'center'}\n sideOffset={10}\n collisionPadding={12}\n className={contentClasses}\n onOpenAutoFocus={(event) => event.preventDefault()}\n onCloseAutoFocus={(event) => event.preventDefault()}\n onInteractOutside={(event) => event.preventDefault()}\n onEscapeKeyDown={(event) => {\n if (typingElsewhere(contentRef.current)) event.preventDefault();\n else onEnd('escape', step);\n }}\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-xs text-muted-foreground\">\n {labels.progress(step + 1, steps.length)}\n </p>\n <h2 id={titleId} className=\"mt-0.5 text-sm font-semibold text-foreground\">\n {current.title}\n </h2>\n </div>\n <button\n type=\"button\"\n aria-label={labels.close}\n onClick={() => onEnd('close', step)}\n className={cn(\n '-me-1 -mt-1 flex size-7 shrink-0 items-center justify-center rounded-sm text-muted-foreground hover:bg-muted hover:text-foreground',\n focusRingClasses,\n touchTargetClasses,\n )}\n >\n <X aria-hidden className=\"size-4\" />\n </button>\n </div>\n <div id={bodyId} className=\"mt-2 text-muted-foreground\">\n {current.content}\n </div>\n <div className=\"mt-4 flex items-center gap-2\">\n {!last && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"-ms-2\"\n onClick={() => onEnd('skip', step)}\n >\n {labels.skip}\n </Button>\n )}\n <div className=\"ms-auto flex gap-2\">\n {step > 0 && (\n <Button variant=\"outline\" size=\"sm\" onClick={() => go(step - 1)}>\n {labels.back}\n </Button>\n )}\n <Button size=\"sm\" onClick={() => (last ? onEnd('complete', step) : go(step + 1))}>\n {last ? labels.done : labels.next}\n </Button>\n </div>\n </div>\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n </>\n );\n}\n\nexport interface SpotlightProps {\n target: TourTarget;\n open: boolean;\n onDismiss: (reason: 'dismiss' | 'escape') => void;\n title: React.ReactNode;\n children: React.ReactNode;\n side?: 'top' | 'right' | 'bottom' | 'left';\n align?: 'start' | 'center' | 'end';\n /** Default \"Got it\". */\n dismissLabel?: string;\n /** An extra button, e.g. \"Add a provider\". */\n action?: React.ReactNode;\n /** Ring the target. Default true. */\n highlight?: boolean;\n className?: string;\n}\n\n/**\n * One contextual hint beside a target: a new feature, a setup nudge. It is\n * informational, so it does not take focus or block the page. Escape (unless\n * the person is typing elsewhere) or the dismiss button closes it. If the target is\n * not on screen, nothing renders.\n */\nfunction Spotlight({\n target,\n open,\n onDismiss,\n title,\n children,\n side = 'bottom',\n align = 'center',\n dismissLabel = 'Got it',\n action,\n highlight = true,\n className,\n}: SpotlightProps) {\n const key = typeof target === 'string' ? target : 'fn';\n const [found, setFound] = React.useState<{ key: string; el: Element | null } | null>(null);\n const el = open && found?.key === key ? found.el : null;\n const rect = useRect(el);\n const titleId = React.useId();\n const contentRef = React.useRef<HTMLDivElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n const controller = new AbortController();\n void waitForTourTarget(target, 2000, controller.signal).then((found) => {\n if (!controller.signal.aborted) setFound({ key, el: found });\n });\n return () => controller.abort();\n // A new function identity for `target` should not re-run the lookup.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open, key]);\n\n if (!open || !el || !rect) return null;\n return (\n <>\n {highlight && <TourHighlight rect={rect} dim={false} />}\n <PopoverPrimitive.Root open modal={false}>\n <PopoverPrimitive.Anchor virtualRef={{ current: { getBoundingClientRect: () => rect } }} />\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={contentRef}\n data-slot=\"spotlight\"\n role=\"note\"\n aria-labelledby={titleId}\n side={side}\n align={align}\n sideOffset={10}\n collisionPadding={12}\n className={cn(contentClasses, 'w-72', className)}\n onOpenAutoFocus={(event) => event.preventDefault()}\n onCloseAutoFocus={(event) => event.preventDefault()}\n onInteractOutside={(event) => event.preventDefault()}\n onEscapeKeyDown={(event) => {\n if (typingElsewhere(contentRef.current)) event.preventDefault();\n else onDismiss('escape');\n }}\n >\n <span className=\"sr-only\" aria-live=\"polite\">\n Tip: {title}\n </span>\n <p id={titleId} className=\"text-sm font-semibold text-foreground\">\n {title}\n </p>\n <div className=\"mt-1.5 text-muted-foreground\">{children}</div>\n <div className=\"mt-3 flex justify-end gap-2\">\n {action}\n <Button\n size=\"sm\"\n variant={action ? 'ghost' : 'default'}\n onClick={() => onDismiss('dismiss')}\n >\n {dismissLabel}\n </Button>\n </div>\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n </>\n );\n}\n\nexport { Spotlight, Tour, TourAnchor };\n"],"mappings":"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"}
|
|
@@ -72,5 +72,5 @@ export declare function flattenTree(nodes: TreeNode[], expanded: ReadonlySet<str
|
|
|
72
72
|
* render flat (role="treeitem" with aria-level), one tab stop for the tree,
|
|
73
73
|
* arrows to move, Right/Left to open and close, Home/End, typeahead.
|
|
74
74
|
*/
|
|
75
|
-
declare
|
|
75
|
+
declare const TreeView: React.ForwardRefExoticComponent<Omit<TreeViewProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
76
76
|
export { TreeView };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { cn, focusRingClasses } from "../lib/utils.js";
|
|
3
3
|
import { Spinner } from "./spinner.js";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -35,7 +35,7 @@ var isTypingTarget = (target) => target instanceof HTMLElement && (target.isCont
|
|
|
35
35
|
* render flat (role="treeitem" with aria-level), one tab stop for the tree,
|
|
36
36
|
* arrows to move, Right/Left to open and close, Home/End, typeahead.
|
|
37
37
|
*/
|
|
38
|
-
function TreeView({ nodes, expanded, onExpandedChange, selected, onSelectedChange, selectionMode = "single", selectionFollowsFocus = false, onAction, actionOnClick = false, expandOnClick, indentGuides = false, onLoadChildren, renderLabel, density = "default", empty, onRowContextMenu, className, ...props }) {
|
|
38
|
+
var TreeView = React.forwardRef(function TreeView({ nodes, expanded, onExpandedChange, selected, onSelectedChange, selectionMode = "single", selectionFollowsFocus = false, onAction, actionOnClick = false, expandOnClick, indentGuides = false, onLoadChildren, renderLabel, density = "default", empty, onRowContextMenu, className, ...props }, ref) {
|
|
39
39
|
const expandedSet = React.useMemo(() => new Set(expanded), [expanded]);
|
|
40
40
|
const selectedSet = React.useMemo(() => new Set(selected), [selected]);
|
|
41
41
|
const rows = React.useMemo(() => flattenTree(nodes, expandedSet), [nodes, expandedSet]);
|
|
@@ -181,7 +181,11 @@ function TreeView({ nodes, expanded, onExpandedChange, selected, onSelectedChang
|
|
|
181
181
|
children: empty
|
|
182
182
|
});
|
|
183
183
|
return /* @__PURE__ */ jsx("div", {
|
|
184
|
-
ref:
|
|
184
|
+
ref: (node) => {
|
|
185
|
+
treeRef.current = node;
|
|
186
|
+
if (typeof ref === "function") ref(node);
|
|
187
|
+
else if (ref) ref.current = node;
|
|
188
|
+
},
|
|
185
189
|
role: "tree",
|
|
186
190
|
tabIndex: -1,
|
|
187
191
|
"data-slot": "tree-view",
|
|
@@ -221,7 +225,7 @@ function TreeView({ nodes, expanded, onExpandedChange, selected, onSelectedChang
|
|
|
221
225
|
tabIndex: node.id === tabStopId ? 0 : -1,
|
|
222
226
|
"data-state": isSelected ? "selected" : void 0,
|
|
223
227
|
style: { paddingInlineStart: `${(row.level - 1) * 12 + 4}px` },
|
|
224
|
-
className: cn("relative flex h-7 shrink-0 cursor-default items-center gap-1 rounded-sm pe-2
|
|
228
|
+
className: cn("relative flex h-7 shrink-0 cursor-default items-center gap-1 rounded-sm pe-2 group-data-[density=compact]/tree:h-6 pointer-coarse:h-11", "hover:bg-muted/70 data-[state=selected]:bg-brand-soft/70 data-[state=selected]:text-foreground", focusRingClasses, "focus-visible:outline-offset-[-2px] aria-disabled:opacity-50", node.className),
|
|
225
229
|
onFocus: () => setFocusedId(node.id),
|
|
226
230
|
onClick: (event) => {
|
|
227
231
|
if (isTypingTarget(event.target)) return;
|
|
@@ -280,7 +284,7 @@ function TreeView({ nodes, expanded, onExpandedChange, selected, onSelectedChang
|
|
|
280
284
|
})] }, node.id);
|
|
281
285
|
})
|
|
282
286
|
});
|
|
283
|
-
}
|
|
287
|
+
});
|
|
284
288
|
//#endregion
|
|
285
289
|
export { TreeView, flattenTree };
|
|
286
290
|
|