@vllnt/ui 0.2.1-canary.ab2c69a → 0.2.1-canary.bb791f1
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/alert-pulse/alert-pulse.js +93 -0
- package/dist/components/alert-pulse/index.js +6 -0
- 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/context-lens/context-lens.js +98 -0
- package/dist/components/context-lens/index.js +6 -0
- package/dist/components/floating-toolbar/floating-toolbar.js +66 -0
- package/dist/components/floating-toolbar/index.js +6 -0
- package/dist/components/follow-mode/follow-mode.js +89 -0
- package/dist/components/follow-mode/index.js +6 -0
- package/dist/components/handoff-beacon/handoff-beacon.js +78 -0
- package/dist/components/handoff-beacon/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 +108 -0
- package/dist/components/jarvis-dock/index.js +6 -0
- package/dist/components/jarvis-dock/jarvis-dock.js +98 -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/multi-select-lasso/index.js +6 -0
- package/dist/components/multi-select-lasso/multi-select-lasso.js +76 -0
- package/dist/components/object-inspector/index.js +6 -0
- package/dist/components/object-inspector/object-inspector.js +136 -0
- package/dist/components/playback-ghost/index.js +6 -0
- package/dist/components/playback-ghost/playback-ghost.js +83 -0
- package/dist/components/policy-delivery-panel/index.js +6 -0
- package/dist/components/policy-delivery-panel/policy-delivery-panel.js +99 -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/property-section/index.js +6 -0
- package/dist/components/property-section/property-section.js +101 -0
- package/dist/components/relationship-inspector/index.js +6 -0
- package/dist/components/relationship-inspector/relationship-inspector.js +102 -0
- package/dist/components/routing-assignment-panel/index.js +6 -0
- package/dist/components/routing-assignment-panel/routing-assignment-panel.js +122 -0
- package/dist/components/run-timeline/index.js +6 -0
- package/dist/components/run-timeline/run-timeline.js +221 -0
- package/dist/components/runtime-overview-panel/index.js +6 -0
- package/dist/components/runtime-overview-panel/runtime-overview-panel.js +89 -0
- package/dist/components/selection-halo/index.js +6 -0
- package/dist/components/selection-halo/selection-halo.js +72 -0
- package/dist/components/snap-guides/index.js +6 -0
- package/dist/components/snap-guides/snap-guides.js +45 -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/sticky-metric/index.js +6 -0
- package/dist/components/sticky-metric/sticky-metric.js +83 -0
- package/dist/components/threshold-ring/index.js +6 -0
- package/dist/components/threshold-ring/threshold-ring.js +160 -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 +2168 -107
- package/package.json +1 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { forwardRef } from "react";
|
|
4
|
+
import { cn } from "../../lib/utils";
|
|
5
|
+
const SEVERITY_STROKE = {
|
|
6
|
+
error: "stroke-red-500",
|
|
7
|
+
info: "stroke-blue-500",
|
|
8
|
+
warn: "stroke-amber-500"
|
|
9
|
+
};
|
|
10
|
+
const SEVERITY_FILL = {
|
|
11
|
+
error: "fill-red-500/20",
|
|
12
|
+
info: "fill-blue-500/20",
|
|
13
|
+
warn: "fill-amber-500/20"
|
|
14
|
+
};
|
|
15
|
+
const SEVERITY_LABEL = {
|
|
16
|
+
error: "Error",
|
|
17
|
+
info: "Info",
|
|
18
|
+
warn: "Warning"
|
|
19
|
+
};
|
|
20
|
+
const safeRadius = (value) => value < 6 ? 6 : value;
|
|
21
|
+
const AlertPulse = forwardRef(
|
|
22
|
+
(props, ref) => {
|
|
23
|
+
const {
|
|
24
|
+
className,
|
|
25
|
+
cx,
|
|
26
|
+
cy,
|
|
27
|
+
labels,
|
|
28
|
+
radius = 36,
|
|
29
|
+
reducedMotion = false,
|
|
30
|
+
severity = "warn",
|
|
31
|
+
...rest
|
|
32
|
+
} = props;
|
|
33
|
+
const r = safeRadius(radius);
|
|
34
|
+
const ariaLabel = labels?.region ?? SEVERITY_LABEL[severity];
|
|
35
|
+
const size = r * 2 + 24;
|
|
36
|
+
return /* @__PURE__ */ jsxs(
|
|
37
|
+
"svg",
|
|
38
|
+
{
|
|
39
|
+
"aria-label": ariaLabel,
|
|
40
|
+
className: cn(
|
|
41
|
+
"pointer-events-none absolute z-20 overflow-visible",
|
|
42
|
+
className
|
|
43
|
+
),
|
|
44
|
+
"data-alert-pulse": true,
|
|
45
|
+
"data-alert-severity": severity,
|
|
46
|
+
height: size,
|
|
47
|
+
ref,
|
|
48
|
+
role: "img",
|
|
49
|
+
style: {
|
|
50
|
+
left: cx - size / 2,
|
|
51
|
+
top: cy - size / 2
|
|
52
|
+
},
|
|
53
|
+
width: size,
|
|
54
|
+
...rest,
|
|
55
|
+
children: [
|
|
56
|
+
/* @__PURE__ */ jsx(
|
|
57
|
+
"circle",
|
|
58
|
+
{
|
|
59
|
+
className: cn("origin-center", SEVERITY_STROKE[severity]),
|
|
60
|
+
cx: size / 2,
|
|
61
|
+
cy: size / 2,
|
|
62
|
+
fill: "none",
|
|
63
|
+
r,
|
|
64
|
+
strokeOpacity: 0.7,
|
|
65
|
+
strokeWidth: 2
|
|
66
|
+
}
|
|
67
|
+
),
|
|
68
|
+
/* @__PURE__ */ jsx(
|
|
69
|
+
"circle",
|
|
70
|
+
{
|
|
71
|
+
className: cn(
|
|
72
|
+
"origin-center",
|
|
73
|
+
SEVERITY_STROKE[severity],
|
|
74
|
+
SEVERITY_FILL[severity],
|
|
75
|
+
reducedMotion ? null : "animate-ping"
|
|
76
|
+
),
|
|
77
|
+
cx: size / 2,
|
|
78
|
+
cy: size / 2,
|
|
79
|
+
"data-alert-pulse-ring": true,
|
|
80
|
+
r,
|
|
81
|
+
strokeOpacity: 0.4,
|
|
82
|
+
strokeWidth: 2
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
AlertPulse.displayName = "AlertPulse";
|
|
91
|
+
export {
|
|
92
|
+
AlertPulse
|
|
93
|
+
};
|
|
@@ -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,98 @@
|
|
|
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 DEFAULT_LABELS = {
|
|
6
|
+
region: "Context lens"
|
|
7
|
+
};
|
|
8
|
+
const clamp01 = (value) => {
|
|
9
|
+
if (value < 0) {
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
if (value > 1) {
|
|
13
|
+
return 1;
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
};
|
|
17
|
+
const safeRadius = (value) => value < 0 ? 0 : value;
|
|
18
|
+
const ContextLens = forwardRef(
|
|
19
|
+
(props, ref) => {
|
|
20
|
+
const { className, focus, labels, opacity = 0.55, ...rest } = props;
|
|
21
|
+
const maskId = useId();
|
|
22
|
+
if (!focus) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
26
|
+
const inner = safeRadius(focus.inner);
|
|
27
|
+
const outer = safeRadius(Math.max(focus.outer, inner));
|
|
28
|
+
const fillOpacity = clamp01(opacity);
|
|
29
|
+
return /* @__PURE__ */ jsxs(
|
|
30
|
+
"svg",
|
|
31
|
+
{
|
|
32
|
+
"aria-hidden": "true",
|
|
33
|
+
"aria-label": resolvedLabels.region,
|
|
34
|
+
className: cn(
|
|
35
|
+
"pointer-events-none absolute inset-0 z-20 h-full w-full",
|
|
36
|
+
className
|
|
37
|
+
),
|
|
38
|
+
"data-context-lens": true,
|
|
39
|
+
ref,
|
|
40
|
+
...rest,
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs(
|
|
43
|
+
"radialGradient",
|
|
44
|
+
{
|
|
45
|
+
cx: focus.cx,
|
|
46
|
+
cy: focus.cy,
|
|
47
|
+
"data-context-lens-gradient": true,
|
|
48
|
+
gradientUnits: "userSpaceOnUse",
|
|
49
|
+
id: maskId,
|
|
50
|
+
r: outer === 0 ? 1 : outer,
|
|
51
|
+
children: [
|
|
52
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "black", stopOpacity: 0 }),
|
|
53
|
+
/* @__PURE__ */ jsx(
|
|
54
|
+
"stop",
|
|
55
|
+
{
|
|
56
|
+
offset: outer === 0 ? "100%" : `${inner / outer * 100}%`,
|
|
57
|
+
stopColor: "black",
|
|
58
|
+
stopOpacity: 0
|
|
59
|
+
}
|
|
60
|
+
),
|
|
61
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "black", stopOpacity: 1 })
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
) }),
|
|
65
|
+
/* @__PURE__ */ jsx(
|
|
66
|
+
"rect",
|
|
67
|
+
{
|
|
68
|
+
"data-context-lens-dim": true,
|
|
69
|
+
fill: "black",
|
|
70
|
+
fillOpacity,
|
|
71
|
+
height: "100%",
|
|
72
|
+
mask: `url(#${maskId}-mask)`,
|
|
73
|
+
width: "100%",
|
|
74
|
+
x: 0,
|
|
75
|
+
y: 0
|
|
76
|
+
}
|
|
77
|
+
),
|
|
78
|
+
/* @__PURE__ */ jsxs("mask", { id: `${maskId}-mask`, children: [
|
|
79
|
+
/* @__PURE__ */ jsx("rect", { fill: "white", height: "100%", width: "100%", x: 0, y: 0 }),
|
|
80
|
+
/* @__PURE__ */ jsx(
|
|
81
|
+
"circle",
|
|
82
|
+
{
|
|
83
|
+
cx: focus.cx,
|
|
84
|
+
cy: focus.cy,
|
|
85
|
+
fill: `url(#${maskId})`,
|
|
86
|
+
r: outer
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
] })
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
ContextLens.displayName = "ContextLens";
|
|
96
|
+
export {
|
|
97
|
+
ContextLens
|
|
98
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
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 VARIANT_CLASSES = {
|
|
8
|
+
destructive: "border-red-300 bg-red-500/10 text-red-700 hover:bg-red-500/20 dark:text-red-300",
|
|
9
|
+
ghost: "border-border bg-background text-foreground hover:bg-accent",
|
|
10
|
+
primary: "border-primary bg-primary text-primary-foreground hover:bg-primary/90"
|
|
11
|
+
};
|
|
12
|
+
const DEFAULT_LABELS = {
|
|
13
|
+
region: "Selection actions"
|
|
14
|
+
};
|
|
15
|
+
const FloatingToolbar = forwardRef(
|
|
16
|
+
(props, ref) => {
|
|
17
|
+
const { actions, className, labels, x, y, ...rest } = props;
|
|
18
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
19
|
+
return /* @__PURE__ */ jsx(
|
|
20
|
+
"div",
|
|
21
|
+
{
|
|
22
|
+
"aria-label": resolvedLabels.region,
|
|
23
|
+
className: cn(
|
|
24
|
+
"absolute z-30 flex -translate-y-full items-center gap-1 rounded-md border border-border bg-background/95 p-1 shadow-md backdrop-blur",
|
|
25
|
+
className
|
|
26
|
+
),
|
|
27
|
+
"data-floating-toolbar": true,
|
|
28
|
+
ref,
|
|
29
|
+
role: "toolbar",
|
|
30
|
+
style: { left: `${x.toString()}px`, top: `${y.toString()}px` },
|
|
31
|
+
...rest,
|
|
32
|
+
children: actions.map((action) => {
|
|
33
|
+
const variant = action.variant ?? "ghost";
|
|
34
|
+
const handleClick = () => {
|
|
35
|
+
action.onActivate();
|
|
36
|
+
};
|
|
37
|
+
return /* @__PURE__ */ jsxs(
|
|
38
|
+
"button",
|
|
39
|
+
{
|
|
40
|
+
"aria-label": action.ariaLabel ?? void 0,
|
|
41
|
+
className: cn(
|
|
42
|
+
"inline-flex h-7 items-center gap-1 rounded-md border px-2 text-xs font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
43
|
+
VARIANT_CLASSES[variant],
|
|
44
|
+
action.disabled ? "cursor-not-allowed opacity-50" : ""
|
|
45
|
+
),
|
|
46
|
+
"data-action-id": action.id,
|
|
47
|
+
"data-variant": variant,
|
|
48
|
+
disabled: action.disabled,
|
|
49
|
+
onClick: handleClick,
|
|
50
|
+
type: "button",
|
|
51
|
+
children: [
|
|
52
|
+
action.glyph ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "inline-flex h-3 w-3", children: action.glyph }) : null,
|
|
53
|
+
/* @__PURE__ */ jsx("span", { children: action.label })
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
action.id
|
|
57
|
+
);
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
FloatingToolbar.displayName = "FloatingToolbar";
|
|
64
|
+
export {
|
|
65
|
+
FloatingToolbar
|
|
66
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
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 PALETTE = {
|
|
8
|
+
amber: { chip: "bg-amber-500 text-white", ring: "ring-amber-500" },
|
|
9
|
+
blue: { chip: "bg-blue-500 text-white", ring: "ring-blue-500" },
|
|
10
|
+
emerald: { chip: "bg-emerald-500 text-white", ring: "ring-emerald-500" },
|
|
11
|
+
purple: { chip: "bg-purple-500 text-white", ring: "ring-purple-500" },
|
|
12
|
+
red: { chip: "bg-red-500 text-white", ring: "ring-red-500" },
|
|
13
|
+
rose: { chip: "bg-rose-500 text-white", ring: "ring-rose-500" }
|
|
14
|
+
};
|
|
15
|
+
const DEFAULT_LABELS = {
|
|
16
|
+
region: "Follow mode",
|
|
17
|
+
stop: "Stop"
|
|
18
|
+
};
|
|
19
|
+
const FollowMode = forwardRef(
|
|
20
|
+
(props, ref) => {
|
|
21
|
+
const {
|
|
22
|
+
children,
|
|
23
|
+
className,
|
|
24
|
+
color = "blue",
|
|
25
|
+
labels,
|
|
26
|
+
name,
|
|
27
|
+
onStop,
|
|
28
|
+
...rest
|
|
29
|
+
} = props;
|
|
30
|
+
const palette = PALETTE[color];
|
|
31
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
32
|
+
return /* @__PURE__ */ jsxs(
|
|
33
|
+
"div",
|
|
34
|
+
{
|
|
35
|
+
"aria-label": resolvedLabels.region,
|
|
36
|
+
className: cn(
|
|
37
|
+
"relative h-full w-full rounded-2xl ring-2 ring-inset",
|
|
38
|
+
palette.ring,
|
|
39
|
+
className
|
|
40
|
+
),
|
|
41
|
+
"data-follow-color": color,
|
|
42
|
+
ref,
|
|
43
|
+
...rest,
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ jsxs(
|
|
46
|
+
"div",
|
|
47
|
+
{
|
|
48
|
+
className: "pointer-events-auto absolute left-1/2 top-2 z-30 flex -translate-x-1/2 items-center gap-1 rounded-full px-2 py-0.5 text-[11px] font-semibold shadow-sm",
|
|
49
|
+
"data-follow-chip": true,
|
|
50
|
+
children: [
|
|
51
|
+
/* @__PURE__ */ jsxs(
|
|
52
|
+
"span",
|
|
53
|
+
{
|
|
54
|
+
className: cn(
|
|
55
|
+
"inline-flex items-center gap-1 rounded-full px-1.5 py-0.5",
|
|
56
|
+
palette.chip
|
|
57
|
+
),
|
|
58
|
+
children: [
|
|
59
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u25B8" }),
|
|
60
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
61
|
+
"Following ",
|
|
62
|
+
name
|
|
63
|
+
] })
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
),
|
|
67
|
+
onStop ? /* @__PURE__ */ jsx(
|
|
68
|
+
"button",
|
|
69
|
+
{
|
|
70
|
+
"aria-label": resolvedLabels.stop,
|
|
71
|
+
className: "inline-flex h-5 items-center rounded-full border border-border bg-background px-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
72
|
+
onClick: onStop,
|
|
73
|
+
type: "button",
|
|
74
|
+
children: resolvedLabels.stop
|
|
75
|
+
}
|
|
76
|
+
) : null
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
),
|
|
80
|
+
children
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
FollowMode.displayName = "FollowMode";
|
|
87
|
+
export {
|
|
88
|
+
FollowMode
|
|
89
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
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 LEVEL_RING = {
|
|
8
|
+
info: "ring-blue-500",
|
|
9
|
+
request: "ring-amber-500",
|
|
10
|
+
urgent: "ring-red-500 animate-pulse"
|
|
11
|
+
};
|
|
12
|
+
const LEVEL_DOT = {
|
|
13
|
+
info: "bg-blue-500",
|
|
14
|
+
request: "bg-amber-500",
|
|
15
|
+
urgent: "bg-red-500"
|
|
16
|
+
};
|
|
17
|
+
const DEFAULT_LABELS = {
|
|
18
|
+
region: "Attention"
|
|
19
|
+
};
|
|
20
|
+
const HandoffBeacon = forwardRef(
|
|
21
|
+
(props, ref) => {
|
|
22
|
+
const {
|
|
23
|
+
className,
|
|
24
|
+
labels,
|
|
25
|
+
level = "info",
|
|
26
|
+
message,
|
|
27
|
+
source,
|
|
28
|
+
x,
|
|
29
|
+
y,
|
|
30
|
+
...rest
|
|
31
|
+
} = props;
|
|
32
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
33
|
+
return /* @__PURE__ */ jsxs(
|
|
34
|
+
"div",
|
|
35
|
+
{
|
|
36
|
+
"aria-label": resolvedLabels.region,
|
|
37
|
+
className: cn(
|
|
38
|
+
"pointer-events-none absolute z-30 flex -translate-x-1/2 -translate-y-1/2 flex-col items-center gap-1",
|
|
39
|
+
className
|
|
40
|
+
),
|
|
41
|
+
"data-handoff-level": level,
|
|
42
|
+
ref,
|
|
43
|
+
role: "status",
|
|
44
|
+
style: { left: `${x.toString()}px`, top: `${y.toString()}px` },
|
|
45
|
+
...rest,
|
|
46
|
+
children: [
|
|
47
|
+
/* @__PURE__ */ jsx(
|
|
48
|
+
"span",
|
|
49
|
+
{
|
|
50
|
+
"aria-hidden": "true",
|
|
51
|
+
className: cn(
|
|
52
|
+
"block h-3 w-3 rounded-full ring-2 ring-offset-2 ring-offset-background",
|
|
53
|
+
LEVEL_DOT[level],
|
|
54
|
+
LEVEL_RING[level]
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
),
|
|
58
|
+
source || message ? /* @__PURE__ */ jsx(
|
|
59
|
+
"div",
|
|
60
|
+
{
|
|
61
|
+
className: "pointer-events-auto rounded-md border border-border bg-popover px-2 py-1 text-center text-[11px] font-medium shadow-md",
|
|
62
|
+
"data-handoff-card": true,
|
|
63
|
+
children: source ? /* @__PURE__ */ jsxs("p", { className: "text-foreground", children: [
|
|
64
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold", children: source }),
|
|
65
|
+
message ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: " \xB7 " }) : null,
|
|
66
|
+
message ? /* @__PURE__ */ jsx("span", { children: message }) : null
|
|
67
|
+
] }) : message ? /* @__PURE__ */ jsx("p", { className: "text-foreground", children: message }) : null
|
|
68
|
+
}
|
|
69
|
+
) : null
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
HandoffBeacon.displayName = "HandoffBeacon";
|
|
76
|
+
export {
|
|
77
|
+
HandoffBeacon
|
|
78
|
+
};
|