@vllnt/ui 0.2.1-canary.ab2c69a → 0.2.1-canary.b3f1dff
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/dist/components/bottom-activity-strip/bottom-activity-strip.js +91 -0
- package/dist/components/bottom-activity-strip/index.js +6 -0
- package/dist/components/comment-pin/comment-pin.js +104 -0
- package/dist/components/comment-pin/index.js +6 -0
- package/dist/components/heat-overlay/heat-overlay.js +92 -0
- package/dist/components/heat-overlay/index.js +6 -0
- package/dist/components/index.js +40 -0
- package/dist/components/live-cursor/index.js +6 -0
- package/dist/components/live-cursor/live-cursor.js +62 -0
- package/dist/components/metric-cluster/index.js +6 -0
- package/dist/components/metric-cluster/metric-cluster.js +96 -0
- package/dist/components/playback-ghost/index.js +6 -0
- package/dist/components/playback-ghost/playback-ghost.js +83 -0
- package/dist/components/presence-sync-indicator/index.js +6 -0
- package/dist/components/presence-sync-indicator/presence-sync-indicator.js +73 -0
- package/dist/components/run-timeline/index.js +6 -0
- package/dist/components/run-timeline/run-timeline.js +221 -0
- package/dist/components/state-badge-overlay/index.js +6 -0
- package/dist/components/state-badge-overlay/state-badge-overlay.js +90 -0
- package/dist/components/timeline-scrubber/index.js +6 -0
- package/dist/components/timeline-scrubber/timeline-scrubber.js +179 -0
- package/dist/index.d.ts +826 -1
- package/package.json +1 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import {
|
|
4
|
+
forwardRef
|
|
5
|
+
} from "react";
|
|
6
|
+
import { cn } from "../../lib/utils";
|
|
7
|
+
const TONE_DOT = {
|
|
8
|
+
danger: "bg-red-500",
|
|
9
|
+
info: "bg-blue-500",
|
|
10
|
+
neutral: "bg-muted-foreground",
|
|
11
|
+
success: "bg-emerald-500",
|
|
12
|
+
warn: "bg-amber-500"
|
|
13
|
+
};
|
|
14
|
+
const DEFAULT_LABELS = {
|
|
15
|
+
empty: "No recent activity",
|
|
16
|
+
region: "Recent activity"
|
|
17
|
+
};
|
|
18
|
+
const ChipBody = (props) => {
|
|
19
|
+
const { event } = props;
|
|
20
|
+
const tone = event.tone ?? "neutral";
|
|
21
|
+
return /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 whitespace-nowrap", children: [
|
|
22
|
+
/* @__PURE__ */ jsx(
|
|
23
|
+
"span",
|
|
24
|
+
{
|
|
25
|
+
"aria-hidden": "true",
|
|
26
|
+
className: cn("h-1.5 w-1.5 rounded-full", TONE_DOT[tone])
|
|
27
|
+
}
|
|
28
|
+
),
|
|
29
|
+
/* @__PURE__ */ jsx("span", { className: "text-foreground", children: event.label }),
|
|
30
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground", "data-strip-event-ts": true, children: event.ts })
|
|
31
|
+
] });
|
|
32
|
+
};
|
|
33
|
+
const Chip = (props) => {
|
|
34
|
+
const { event } = props;
|
|
35
|
+
const tone = event.tone ?? "neutral";
|
|
36
|
+
if (event.onActivate) {
|
|
37
|
+
const handleClick = () => {
|
|
38
|
+
event.onActivate?.();
|
|
39
|
+
};
|
|
40
|
+
return /* @__PURE__ */ jsx(
|
|
41
|
+
"button",
|
|
42
|
+
{
|
|
43
|
+
className: "flex items-center rounded-full border border-border bg-background px-2 py-1 text-[11px] transition-colors hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
44
|
+
"data-strip-event": event.id,
|
|
45
|
+
"data-strip-event-tone": tone,
|
|
46
|
+
onClick: handleClick,
|
|
47
|
+
type: "button",
|
|
48
|
+
children: /* @__PURE__ */ jsx(ChipBody, { event })
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return /* @__PURE__ */ jsx(
|
|
53
|
+
"span",
|
|
54
|
+
{
|
|
55
|
+
className: "flex items-center rounded-full border border-border bg-background px-2 py-1 text-[11px]",
|
|
56
|
+
"data-strip-event": event.id,
|
|
57
|
+
"data-strip-event-tone": tone,
|
|
58
|
+
children: /* @__PURE__ */ jsx(ChipBody, { event })
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
const BottomActivityStrip = forwardRef((props, ref) => {
|
|
63
|
+
const { className, events, labels, maxEvents, ...rest } = props;
|
|
64
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
65
|
+
const visible = maxEvents === void 0 || maxEvents >= events.length ? events : events.slice(0, maxEvents);
|
|
66
|
+
return /* @__PURE__ */ jsx(
|
|
67
|
+
"section",
|
|
68
|
+
{
|
|
69
|
+
"aria-label": resolvedLabels.region,
|
|
70
|
+
className: cn(
|
|
71
|
+
"flex w-full items-center gap-2 overflow-x-auto rounded-md border border-border bg-background/90 px-2 py-1 text-foreground",
|
|
72
|
+
className
|
|
73
|
+
),
|
|
74
|
+
"data-bottom-activity-strip": true,
|
|
75
|
+
ref,
|
|
76
|
+
...rest,
|
|
77
|
+
children: visible.length === 0 ? /* @__PURE__ */ jsx(
|
|
78
|
+
"span",
|
|
79
|
+
{
|
|
80
|
+
className: "px-2 text-[11px] text-muted-foreground",
|
|
81
|
+
"data-strip-state": "empty",
|
|
82
|
+
children: resolvedLabels.empty
|
|
83
|
+
}
|
|
84
|
+
) : visible.map((event) => /* @__PURE__ */ jsx(Chip, { event }, event.id))
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
BottomActivityStrip.displayName = "BottomActivityStrip";
|
|
89
|
+
export {
|
|
90
|
+
BottomActivityStrip
|
|
91
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import {
|
|
4
|
+
forwardRef
|
|
5
|
+
} from "react";
|
|
6
|
+
import { cn } from "../../lib/utils";
|
|
7
|
+
const DEFAULT_LABELS = {
|
|
8
|
+
region: "Comment",
|
|
9
|
+
unreadSuffix: "unread"
|
|
10
|
+
};
|
|
11
|
+
const STATE_FILL = {
|
|
12
|
+
open: "bg-foreground text-background",
|
|
13
|
+
resolved: "bg-muted text-muted-foreground"
|
|
14
|
+
};
|
|
15
|
+
const PinBody = (props) => {
|
|
16
|
+
const showBadge = typeof props.unread === "number" && props.unread > 0;
|
|
17
|
+
const useAccent = props.accent && props.state === "open";
|
|
18
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
19
|
+
/* @__PURE__ */ jsx(
|
|
20
|
+
"span",
|
|
21
|
+
{
|
|
22
|
+
"aria-hidden": "true",
|
|
23
|
+
className: cn(
|
|
24
|
+
"flex h-7 w-7 items-center justify-center rounded-full border border-background text-[11px] font-semibold shadow-sm",
|
|
25
|
+
STATE_FILL[props.state]
|
|
26
|
+
),
|
|
27
|
+
"data-comment-pin-body": true,
|
|
28
|
+
style: useAccent ? { backgroundColor: props.accent, color: "white" } : void 0,
|
|
29
|
+
children: props.authorInitial ?? "\u2022"
|
|
30
|
+
}
|
|
31
|
+
),
|
|
32
|
+
showBadge ? /* @__PURE__ */ jsx(
|
|
33
|
+
"span",
|
|
34
|
+
{
|
|
35
|
+
"aria-hidden": "true",
|
|
36
|
+
className: "absolute -right-1 -top-1 inline-flex min-h-[14px] min-w-[14px] items-center justify-center rounded-full bg-red-500 px-1 text-[9px] font-medium text-white",
|
|
37
|
+
"data-comment-pin-unread": true,
|
|
38
|
+
children: props.unread
|
|
39
|
+
}
|
|
40
|
+
) : null
|
|
41
|
+
] });
|
|
42
|
+
};
|
|
43
|
+
const CommentPin = forwardRef(
|
|
44
|
+
(props, ref) => {
|
|
45
|
+
const {
|
|
46
|
+
authorInitial,
|
|
47
|
+
className,
|
|
48
|
+
color,
|
|
49
|
+
labels,
|
|
50
|
+
onActivate,
|
|
51
|
+
state = "open",
|
|
52
|
+
unread,
|
|
53
|
+
x,
|
|
54
|
+
y,
|
|
55
|
+
...rest
|
|
56
|
+
} = props;
|
|
57
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
58
|
+
const showBadge = typeof unread === "number" && unread > 0;
|
|
59
|
+
const ariaLabel = showBadge ? `${resolvedLabels.region}, ${unread} ${resolvedLabels.unreadSuffix}` : resolvedLabels.region;
|
|
60
|
+
const handleClick = () => {
|
|
61
|
+
onActivate?.();
|
|
62
|
+
};
|
|
63
|
+
const body = /* @__PURE__ */ jsx(
|
|
64
|
+
PinBody,
|
|
65
|
+
{
|
|
66
|
+
accent: color,
|
|
67
|
+
authorInitial,
|
|
68
|
+
state,
|
|
69
|
+
unread
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
return /* @__PURE__ */ jsx(
|
|
73
|
+
"div",
|
|
74
|
+
{
|
|
75
|
+
"aria-label": ariaLabel,
|
|
76
|
+
className: cn(
|
|
77
|
+
"absolute z-30 inline-flex -translate-x-1/2 -translate-y-1/2",
|
|
78
|
+
className
|
|
79
|
+
),
|
|
80
|
+
"data-comment-pin": true,
|
|
81
|
+
"data-comment-pin-state": state,
|
|
82
|
+
ref,
|
|
83
|
+
role: "img",
|
|
84
|
+
style: { left: x, top: y },
|
|
85
|
+
...rest,
|
|
86
|
+
children: onActivate ? /* @__PURE__ */ jsx(
|
|
87
|
+
"button",
|
|
88
|
+
{
|
|
89
|
+
"aria-label": ariaLabel,
|
|
90
|
+
className: "relative inline-flex rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
91
|
+
"data-comment-pin-trigger": true,
|
|
92
|
+
onClick: handleClick,
|
|
93
|
+
type: "button",
|
|
94
|
+
children: body
|
|
95
|
+
}
|
|
96
|
+
) : /* @__PURE__ */ jsx("span", { className: "relative inline-flex", children: body })
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
CommentPin.displayName = "CommentPin";
|
|
102
|
+
export {
|
|
103
|
+
CommentPin
|
|
104
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef, useId } from "react";
|
|
4
|
+
import { cn } from "../../lib/utils";
|
|
5
|
+
const TONE_FILL = {
|
|
6
|
+
cool: "fill-blue-500",
|
|
7
|
+
danger: "fill-red-500",
|
|
8
|
+
neutral: "fill-foreground",
|
|
9
|
+
warn: "fill-amber-500"
|
|
10
|
+
};
|
|
11
|
+
const DEFAULT_LABELS = {
|
|
12
|
+
region: "Heat overlay"
|
|
13
|
+
};
|
|
14
|
+
const clamp01 = (v) => {
|
|
15
|
+
if (v < 0) {
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
if (v > 1) {
|
|
19
|
+
return 1;
|
|
20
|
+
}
|
|
21
|
+
return v;
|
|
22
|
+
};
|
|
23
|
+
const HeatBlob = (props) => {
|
|
24
|
+
const { defaultTone, gradientId, intensity, point } = props;
|
|
25
|
+
const weight = clamp01(point.weight);
|
|
26
|
+
const tone = point.tone ?? defaultTone;
|
|
27
|
+
return /* @__PURE__ */ jsx(
|
|
28
|
+
"circle",
|
|
29
|
+
{
|
|
30
|
+
className: cn("mix-blend-multiply", TONE_FILL[tone]),
|
|
31
|
+
cx: point.x,
|
|
32
|
+
cy: point.y,
|
|
33
|
+
"data-heat-point": point.id,
|
|
34
|
+
"data-heat-tone": tone,
|
|
35
|
+
fill: `url(#${gradientId})`,
|
|
36
|
+
fillOpacity: weight * 0.6,
|
|
37
|
+
r: Math.max(8, intensity * weight)
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
const HeatOverlay = forwardRef(
|
|
42
|
+
(props, ref) => {
|
|
43
|
+
const {
|
|
44
|
+
className,
|
|
45
|
+
defaultTone = "warn",
|
|
46
|
+
intensity = 48,
|
|
47
|
+
labels,
|
|
48
|
+
points,
|
|
49
|
+
...rest
|
|
50
|
+
} = props;
|
|
51
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
52
|
+
const gradientId = useId();
|
|
53
|
+
if (points.length === 0) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return /* @__PURE__ */ jsxs(
|
|
57
|
+
"svg",
|
|
58
|
+
{
|
|
59
|
+
"aria-label": resolvedLabels.region,
|
|
60
|
+
className: cn(
|
|
61
|
+
"pointer-events-none absolute inset-0 z-10 h-full w-full",
|
|
62
|
+
className
|
|
63
|
+
),
|
|
64
|
+
"data-heat-overlay": true,
|
|
65
|
+
ref,
|
|
66
|
+
role: "img",
|
|
67
|
+
...rest,
|
|
68
|
+
children: [
|
|
69
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("radialGradient", { cx: "50%", cy: "50%", id: gradientId, r: "50%", children: [
|
|
70
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "currentColor", stopOpacity: "1" }),
|
|
71
|
+
/* @__PURE__ */ jsx("stop", { offset: "70%", stopColor: "currentColor", stopOpacity: "0.4" }),
|
|
72
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "currentColor", stopOpacity: "0" })
|
|
73
|
+
] }) }),
|
|
74
|
+
points.map((point) => /* @__PURE__ */ jsx(
|
|
75
|
+
HeatBlob,
|
|
76
|
+
{
|
|
77
|
+
defaultTone,
|
|
78
|
+
gradientId,
|
|
79
|
+
intensity,
|
|
80
|
+
point
|
|
81
|
+
},
|
|
82
|
+
point.id
|
|
83
|
+
))
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
HeatOverlay.displayName = "HeatOverlay";
|
|
90
|
+
export {
|
|
91
|
+
HeatOverlay
|
|
92
|
+
};
|
package/dist/components/index.js
CHANGED
|
@@ -539,24 +539,54 @@ import {
|
|
|
539
539
|
useFlowDiagram
|
|
540
540
|
} from "./flow-diagram";
|
|
541
541
|
import { AnchorPort } from "./anchor-port";
|
|
542
|
+
import {
|
|
543
|
+
BottomActivityStrip
|
|
544
|
+
} from "./bottom-activity-strip";
|
|
545
|
+
import {
|
|
546
|
+
CommentPin
|
|
547
|
+
} from "./comment-pin";
|
|
542
548
|
import {
|
|
543
549
|
ConnectorEdge
|
|
544
550
|
} from "./connector-edge";
|
|
545
551
|
import { EdgeLabel } from "./edge-label";
|
|
546
552
|
import { GroupHull } from "./group-hull";
|
|
553
|
+
import {
|
|
554
|
+
HeatOverlay
|
|
555
|
+
} from "./heat-overlay";
|
|
556
|
+
import {
|
|
557
|
+
LiveCursor
|
|
558
|
+
} from "./live-cursor";
|
|
559
|
+
import {
|
|
560
|
+
MetricCluster
|
|
561
|
+
} from "./metric-cluster";
|
|
547
562
|
import {
|
|
548
563
|
ObjectCard
|
|
549
564
|
} from "./object-card";
|
|
550
565
|
import { ObjectHandle } from "./object-handle";
|
|
566
|
+
import {
|
|
567
|
+
PlaybackGhost
|
|
568
|
+
} from "./playback-ghost";
|
|
551
569
|
import {
|
|
552
570
|
PresenceStack
|
|
553
571
|
} from "./presence-stack";
|
|
572
|
+
import {
|
|
573
|
+
PresenceSyncIndicator
|
|
574
|
+
} from "./presence-sync-indicator";
|
|
575
|
+
import {
|
|
576
|
+
RunTimeline
|
|
577
|
+
} from "./run-timeline";
|
|
554
578
|
import {
|
|
555
579
|
SelectionPresence
|
|
556
580
|
} from "./selection-presence";
|
|
581
|
+
import {
|
|
582
|
+
StateBadgeOverlay
|
|
583
|
+
} from "./state-badge-overlay";
|
|
557
584
|
import {
|
|
558
585
|
ThreadBubble
|
|
559
586
|
} from "./thread-bubble";
|
|
587
|
+
import {
|
|
588
|
+
TimelineScrubber
|
|
589
|
+
} from "./timeline-scrubber";
|
|
560
590
|
import {
|
|
561
591
|
ConversationEmpty,
|
|
562
592
|
ConversationHeader,
|
|
@@ -613,6 +643,7 @@ export {
|
|
|
613
643
|
BeforeAfter,
|
|
614
644
|
BlogCard,
|
|
615
645
|
BorderBeam,
|
|
646
|
+
BottomActivityStrip,
|
|
616
647
|
BottomBar,
|
|
617
648
|
Breadcrumb,
|
|
618
649
|
Button,
|
|
@@ -651,6 +682,7 @@ export {
|
|
|
651
682
|
CommandList,
|
|
652
683
|
CommandSeparator,
|
|
653
684
|
CommandShortcut,
|
|
685
|
+
CommentPin,
|
|
654
686
|
CommonMistake,
|
|
655
687
|
Comparison,
|
|
656
688
|
CompletionDialog,
|
|
@@ -750,6 +782,7 @@ export {
|
|
|
750
782
|
GlassPanel,
|
|
751
783
|
Glossary,
|
|
752
784
|
GroupHull,
|
|
785
|
+
HeatOverlay,
|
|
753
786
|
Highlight,
|
|
754
787
|
HorizontalScrollRow,
|
|
755
788
|
HoverCard,
|
|
@@ -769,6 +802,7 @@ export {
|
|
|
769
802
|
LearningObjectives,
|
|
770
803
|
LeftRail,
|
|
771
804
|
LineChart,
|
|
805
|
+
LiveCursor,
|
|
772
806
|
LiveFeed,
|
|
773
807
|
MDXContent,
|
|
774
808
|
MarketTreemap,
|
|
@@ -789,6 +823,7 @@ export {
|
|
|
789
823
|
MenubarSubContent,
|
|
790
824
|
MenubarSubTrigger,
|
|
791
825
|
MenubarTrigger,
|
|
826
|
+
MetricCluster,
|
|
792
827
|
MetricGauge,
|
|
793
828
|
MiniMapPanel,
|
|
794
829
|
ModelSelector,
|
|
@@ -812,12 +847,14 @@ export {
|
|
|
812
847
|
Pagination,
|
|
813
848
|
PasswordInput,
|
|
814
849
|
PlanBadge,
|
|
850
|
+
PlaybackGhost,
|
|
815
851
|
Popover,
|
|
816
852
|
PopoverAnchor,
|
|
817
853
|
PopoverContent,
|
|
818
854
|
PopoverTrigger,
|
|
819
855
|
Prerequisites,
|
|
820
856
|
PresenceStack,
|
|
857
|
+
PresenceSyncIndicator,
|
|
821
858
|
ProTip,
|
|
822
859
|
ProfileSection,
|
|
823
860
|
ProgressBar,
|
|
@@ -838,6 +875,7 @@ export {
|
|
|
838
875
|
ResizablePanelGroup,
|
|
839
876
|
RightDock,
|
|
840
877
|
RoleBadge,
|
|
878
|
+
RunTimeline,
|
|
841
879
|
ScopeSelector,
|
|
842
880
|
ScrollArea,
|
|
843
881
|
ScrollBar,
|
|
@@ -881,6 +919,7 @@ export {
|
|
|
881
919
|
SparklineGrid,
|
|
882
920
|
Spinner,
|
|
883
921
|
StatCard,
|
|
922
|
+
StateBadgeOverlay,
|
|
884
923
|
StatusBoard,
|
|
885
924
|
StatusIndicator,
|
|
886
925
|
Step,
|
|
@@ -913,6 +952,7 @@ export {
|
|
|
913
952
|
ThinkingBlock,
|
|
914
953
|
ThreadBubble,
|
|
915
954
|
TickerTape,
|
|
955
|
+
TimelineScrubber,
|
|
916
956
|
Toast,
|
|
917
957
|
ToastAction,
|
|
918
958
|
ToastClose,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import {
|
|
4
|
+
forwardRef
|
|
5
|
+
} from "react";
|
|
6
|
+
import { cn } from "../../lib/utils";
|
|
7
|
+
const DEFAULT_LABELS = {
|
|
8
|
+
region: "Live cursor"
|
|
9
|
+
};
|
|
10
|
+
const LiveCursor = forwardRef(
|
|
11
|
+
(props, ref) => {
|
|
12
|
+
const { className, color, labels, name, status, x, y, ...rest } = props;
|
|
13
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
14
|
+
const resolvedColor = color ?? "var(--foreground)";
|
|
15
|
+
return /* @__PURE__ */ jsxs(
|
|
16
|
+
"div",
|
|
17
|
+
{
|
|
18
|
+
"aria-label": typeof name === "string" ? `${resolvedLabels.region}: ${name}` : resolvedLabels.region,
|
|
19
|
+
className: cn(
|
|
20
|
+
"pointer-events-none absolute z-30 flex items-start gap-1",
|
|
21
|
+
className
|
|
22
|
+
),
|
|
23
|
+
"data-live-cursor": true,
|
|
24
|
+
ref,
|
|
25
|
+
role: "img",
|
|
26
|
+
style: { left: x, top: y },
|
|
27
|
+
...rest,
|
|
28
|
+
children: [
|
|
29
|
+
/* @__PURE__ */ jsx(
|
|
30
|
+
"svg",
|
|
31
|
+
{
|
|
32
|
+
"aria-hidden": "true",
|
|
33
|
+
className: "-ml-1 -mt-1 drop-shadow-sm",
|
|
34
|
+
"data-live-cursor-pointer": true,
|
|
35
|
+
fill: resolvedColor,
|
|
36
|
+
height: 18,
|
|
37
|
+
viewBox: "0 0 16 18",
|
|
38
|
+
width: 16,
|
|
39
|
+
children: /* @__PURE__ */ jsx("path", { d: "M0 0 L0 14 L4 11 L7 17 L10 16 L7 10 L13 10 Z" })
|
|
40
|
+
}
|
|
41
|
+
),
|
|
42
|
+
name === null || name === void 0 ? null : /* @__PURE__ */ jsxs(
|
|
43
|
+
"span",
|
|
44
|
+
{
|
|
45
|
+
className: "ml-2 mt-2 inline-flex flex-col rounded-md px-1.5 py-0.5 text-[10px] font-medium text-white shadow-sm",
|
|
46
|
+
"data-live-cursor-chip": true,
|
|
47
|
+
style: { backgroundColor: resolvedColor },
|
|
48
|
+
children: [
|
|
49
|
+
/* @__PURE__ */ jsx("span", { children: name }),
|
|
50
|
+
status ? /* @__PURE__ */ jsx("span", { className: "text-[9px] opacity-80", "data-live-cursor-status": true, children: status }) : null
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
LiveCursor.displayName = "LiveCursor";
|
|
60
|
+
export {
|
|
61
|
+
LiveCursor
|
|
62
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import {
|
|
4
|
+
forwardRef
|
|
5
|
+
} from "react";
|
|
6
|
+
import { cn } from "../../lib/utils";
|
|
7
|
+
const TONE_DOT = {
|
|
8
|
+
danger: "bg-red-500",
|
|
9
|
+
neutral: "bg-muted-foreground",
|
|
10
|
+
success: "bg-emerald-500",
|
|
11
|
+
warn: "bg-amber-500"
|
|
12
|
+
};
|
|
13
|
+
const ANCHOR_TRANSFORM = {
|
|
14
|
+
"bottom-left": "translate(-100%, 100%)",
|
|
15
|
+
"bottom-right": "translate(0%, 100%)",
|
|
16
|
+
"top-left": "translate(-100%, -100%)",
|
|
17
|
+
"top-right": "translate(0%, -100%)"
|
|
18
|
+
};
|
|
19
|
+
const DEFAULT_LABELS = {
|
|
20
|
+
region: "Metric cluster"
|
|
21
|
+
};
|
|
22
|
+
const Row = (props) => {
|
|
23
|
+
const { entry } = props;
|
|
24
|
+
const tone = entry.tone ?? "neutral";
|
|
25
|
+
return /* @__PURE__ */ jsxs(
|
|
26
|
+
"li",
|
|
27
|
+
{
|
|
28
|
+
className: "flex items-center justify-between gap-2 text-[11px]",
|
|
29
|
+
"data-metric-cluster-row": entry.id,
|
|
30
|
+
"data-metric-cluster-tone": tone,
|
|
31
|
+
children: [
|
|
32
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-muted-foreground", children: [
|
|
33
|
+
/* @__PURE__ */ jsx(
|
|
34
|
+
"span",
|
|
35
|
+
{
|
|
36
|
+
"aria-hidden": "true",
|
|
37
|
+
className: cn("h-1.5 w-1.5 rounded-full", TONE_DOT[tone])
|
|
38
|
+
}
|
|
39
|
+
),
|
|
40
|
+
entry.label
|
|
41
|
+
] }),
|
|
42
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: entry.value })
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
const MetricCluster = forwardRef(
|
|
48
|
+
(props, ref) => {
|
|
49
|
+
const {
|
|
50
|
+
anchor = "top-right",
|
|
51
|
+
className,
|
|
52
|
+
labels,
|
|
53
|
+
metrics,
|
|
54
|
+
title,
|
|
55
|
+
x,
|
|
56
|
+
y,
|
|
57
|
+
...rest
|
|
58
|
+
} = props;
|
|
59
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
60
|
+
return /* @__PURE__ */ jsxs(
|
|
61
|
+
"div",
|
|
62
|
+
{
|
|
63
|
+
"aria-label": resolvedLabels.region,
|
|
64
|
+
className: cn(
|
|
65
|
+
"pointer-events-none absolute z-20 flex flex-col gap-1 rounded-md border border-border bg-background/90 p-2 shadow-sm backdrop-blur",
|
|
66
|
+
className
|
|
67
|
+
),
|
|
68
|
+
"data-metric-cluster": true,
|
|
69
|
+
"data-metric-cluster-anchor": anchor,
|
|
70
|
+
ref,
|
|
71
|
+
role: "status",
|
|
72
|
+
style: {
|
|
73
|
+
left: x,
|
|
74
|
+
top: y,
|
|
75
|
+
transform: ANCHOR_TRANSFORM[anchor]
|
|
76
|
+
},
|
|
77
|
+
...rest,
|
|
78
|
+
children: [
|
|
79
|
+
title ? /* @__PURE__ */ jsx(
|
|
80
|
+
"header",
|
|
81
|
+
{
|
|
82
|
+
className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground",
|
|
83
|
+
"data-metric-cluster-title": true,
|
|
84
|
+
children: title
|
|
85
|
+
}
|
|
86
|
+
) : null,
|
|
87
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-0.5", children: metrics.map((entry) => /* @__PURE__ */ jsx(Row, { entry }, entry.id)) })
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
MetricCluster.displayName = "MetricCluster";
|
|
94
|
+
export {
|
|
95
|
+
MetricCluster
|
|
96
|
+
};
|