@vllnt/ui 0.2.1-canary.4abeac1 → 0.2.1-canary.73a93ee
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/comment-pin/comment-pin.js +104 -0
- package/dist/components/comment-pin/index.js +6 -0
- package/dist/components/index.js +40 -0
- package/dist/components/infinite-plane/index.js +6 -0
- package/dist/components/infinite-plane/infinite-plane.js +75 -0
- package/dist/components/live-cursor/index.js +6 -0
- package/dist/components/live-cursor/live-cursor.js +62 -0
- package/dist/components/presence-stack/index.js +6 -0
- package/dist/components/presence-stack/presence-stack.js +108 -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/selection-presence/index.js +6 -0
- package/dist/components/selection-presence/selection-presence.js +50 -0
- package/dist/components/thread-bubble/index.js +6 -0
- package/dist/components/thread-bubble/thread-bubble.js +85 -0
- package/dist/components/viewport-bookmarks/index.js +6 -0
- package/dist/components/viewport-bookmarks/viewport-bookmarks.js +116 -0
- package/dist/components/world-breadcrumbs/index.js +6 -0
- package/dist/components/world-breadcrumbs/world-breadcrumbs.js +114 -0
- package/dist/index.d.ts +783 -2
- package/package.json +1 -1
|
@@ -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
|
+
};
|
package/dist/components/index.js
CHANGED
|
@@ -306,6 +306,9 @@ import {
|
|
|
306
306
|
ChatDockSection
|
|
307
307
|
} from "./chat-dock-section";
|
|
308
308
|
import { GlassPanel } from "./glass-panel";
|
|
309
|
+
import {
|
|
310
|
+
InfinitePlane
|
|
311
|
+
} from "./infinite-plane";
|
|
309
312
|
import { LeftRail } from "./left-rail";
|
|
310
313
|
import {
|
|
311
314
|
MiniMapPanel
|
|
@@ -323,6 +326,12 @@ import { Sidebar } from "./sidebar";
|
|
|
323
326
|
import { SidebarProvider, useSidebar } from "./sidebar-provider";
|
|
324
327
|
import { TableOfContents } from "./table-of-contents";
|
|
325
328
|
import { TopBar } from "./top-bar";
|
|
329
|
+
import {
|
|
330
|
+
ViewportBookmarks
|
|
331
|
+
} from "./viewport-bookmarks";
|
|
332
|
+
import {
|
|
333
|
+
WorldBreadcrumbs
|
|
334
|
+
} from "./world-breadcrumbs";
|
|
326
335
|
import { ZoomHUD } from "./zoom-hud";
|
|
327
336
|
import {
|
|
328
337
|
ActivityLog
|
|
@@ -530,15 +539,36 @@ import {
|
|
|
530
539
|
useFlowDiagram
|
|
531
540
|
} from "./flow-diagram";
|
|
532
541
|
import { AnchorPort } from "./anchor-port";
|
|
542
|
+
import {
|
|
543
|
+
CommentPin
|
|
544
|
+
} from "./comment-pin";
|
|
533
545
|
import {
|
|
534
546
|
ConnectorEdge
|
|
535
547
|
} from "./connector-edge";
|
|
536
548
|
import { EdgeLabel } from "./edge-label";
|
|
537
549
|
import { GroupHull } from "./group-hull";
|
|
550
|
+
import {
|
|
551
|
+
LiveCursor
|
|
552
|
+
} from "./live-cursor";
|
|
538
553
|
import {
|
|
539
554
|
ObjectCard
|
|
540
555
|
} from "./object-card";
|
|
541
556
|
import { ObjectHandle } from "./object-handle";
|
|
557
|
+
import {
|
|
558
|
+
PresenceStack
|
|
559
|
+
} from "./presence-stack";
|
|
560
|
+
import {
|
|
561
|
+
PresenceSyncIndicator
|
|
562
|
+
} from "./presence-sync-indicator";
|
|
563
|
+
import {
|
|
564
|
+
RunTimeline
|
|
565
|
+
} from "./run-timeline";
|
|
566
|
+
import {
|
|
567
|
+
SelectionPresence
|
|
568
|
+
} from "./selection-presence";
|
|
569
|
+
import {
|
|
570
|
+
ThreadBubble
|
|
571
|
+
} from "./thread-bubble";
|
|
542
572
|
import {
|
|
543
573
|
ConversationEmpty,
|
|
544
574
|
ConversationHeader,
|
|
@@ -633,6 +663,7 @@ export {
|
|
|
633
663
|
CommandList,
|
|
634
664
|
CommandSeparator,
|
|
635
665
|
CommandShortcut,
|
|
666
|
+
CommentPin,
|
|
636
667
|
CommonMistake,
|
|
637
668
|
Comparison,
|
|
638
669
|
CompletionDialog,
|
|
@@ -737,6 +768,7 @@ export {
|
|
|
737
768
|
HoverCard,
|
|
738
769
|
HoverCardContent,
|
|
739
770
|
HoverCardTrigger,
|
|
771
|
+
InfinitePlane,
|
|
740
772
|
InlineInput,
|
|
741
773
|
Input,
|
|
742
774
|
InputOTP,
|
|
@@ -750,6 +782,7 @@ export {
|
|
|
750
782
|
LearningObjectives,
|
|
751
783
|
LeftRail,
|
|
752
784
|
LineChart,
|
|
785
|
+
LiveCursor,
|
|
753
786
|
LiveFeed,
|
|
754
787
|
MDXContent,
|
|
755
788
|
MarketTreemap,
|
|
@@ -798,6 +831,8 @@ export {
|
|
|
798
831
|
PopoverContent,
|
|
799
832
|
PopoverTrigger,
|
|
800
833
|
Prerequisites,
|
|
834
|
+
PresenceStack,
|
|
835
|
+
PresenceSyncIndicator,
|
|
801
836
|
ProTip,
|
|
802
837
|
ProfileSection,
|
|
803
838
|
ProgressBar,
|
|
@@ -818,6 +853,7 @@ export {
|
|
|
818
853
|
ResizablePanelGroup,
|
|
819
854
|
RightDock,
|
|
820
855
|
RoleBadge,
|
|
856
|
+
RunTimeline,
|
|
821
857
|
ScopeSelector,
|
|
822
858
|
ScrollArea,
|
|
823
859
|
ScrollBar,
|
|
@@ -835,6 +871,7 @@ export {
|
|
|
835
871
|
SelectSeparator,
|
|
836
872
|
SelectTrigger,
|
|
837
873
|
SelectValue,
|
|
874
|
+
SelectionPresence,
|
|
838
875
|
Separator,
|
|
839
876
|
SeverityBadge,
|
|
840
877
|
ShareDialog,
|
|
@@ -890,6 +927,7 @@ export {
|
|
|
890
927
|
ThemeProvider,
|
|
891
928
|
ThemeToggle,
|
|
892
929
|
ThinkingBlock,
|
|
930
|
+
ThreadBubble,
|
|
893
931
|
TickerTape,
|
|
894
932
|
Toast,
|
|
895
933
|
ToastAction,
|
|
@@ -916,9 +954,11 @@ export {
|
|
|
916
954
|
UsageBreakdown,
|
|
917
955
|
VideoEmbed,
|
|
918
956
|
ViewSwitcher,
|
|
957
|
+
ViewportBookmarks,
|
|
919
958
|
WalletCard,
|
|
920
959
|
Watchlist,
|
|
921
960
|
WorkspaceSwitcher,
|
|
961
|
+
WorldBreadcrumbs,
|
|
922
962
|
WorldClockBar,
|
|
923
963
|
ZoomHUD,
|
|
924
964
|
alertVariants,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import {
|
|
4
|
+
forwardRef
|
|
5
|
+
} from "react";
|
|
6
|
+
import { cn } from "../../lib/utils";
|
|
7
|
+
const DEFAULT_LABELS = {
|
|
8
|
+
region: "Infinite plane"
|
|
9
|
+
};
|
|
10
|
+
const safeSpacing = (value) => value < 4 ? 4 : value;
|
|
11
|
+
const safeZoom = (value) => {
|
|
12
|
+
if (value < 0.1) {
|
|
13
|
+
return 0.1;
|
|
14
|
+
}
|
|
15
|
+
if (value > 10) {
|
|
16
|
+
return 10;
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
};
|
|
20
|
+
const buildBackground = (input) => {
|
|
21
|
+
if (input.pattern === "blank") {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
const size = safeSpacing(input.spacing) * safeZoom(input.zoom);
|
|
25
|
+
const pos = `${input.translate.x}px ${input.translate.y}px`;
|
|
26
|
+
if (input.pattern === "grid") {
|
|
27
|
+
return {
|
|
28
|
+
backgroundImage: "linear-gradient(to right, hsl(var(--border)) 1px, transparent 1px), linear-gradient(to bottom, hsl(var(--border)) 1px, transparent 1px)",
|
|
29
|
+
backgroundPosition: pos,
|
|
30
|
+
backgroundSize: `${size}px ${size}px`
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
backgroundImage: "radial-gradient(circle, hsl(var(--border)) 1px, transparent 1px)",
|
|
35
|
+
backgroundPosition: pos,
|
|
36
|
+
backgroundSize: `${size}px ${size}px`
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const InfinitePlane = forwardRef(
|
|
40
|
+
(props, ref) => {
|
|
41
|
+
const {
|
|
42
|
+
children,
|
|
43
|
+
className,
|
|
44
|
+
labels,
|
|
45
|
+
pattern = "dot",
|
|
46
|
+
spacing = 32,
|
|
47
|
+
translate = { x: 0, y: 0 },
|
|
48
|
+
zoom = 1,
|
|
49
|
+
...rest
|
|
50
|
+
} = props;
|
|
51
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
52
|
+
const background = buildBackground({ pattern, spacing, translate, zoom });
|
|
53
|
+
return /* @__PURE__ */ jsx(
|
|
54
|
+
"div",
|
|
55
|
+
{
|
|
56
|
+
"aria-label": resolvedLabels.region,
|
|
57
|
+
className: cn(
|
|
58
|
+
"relative h-full w-full overflow-hidden bg-background",
|
|
59
|
+
className
|
|
60
|
+
),
|
|
61
|
+
"data-infinite-plane": true,
|
|
62
|
+
"data-infinite-plane-pattern": pattern,
|
|
63
|
+
ref,
|
|
64
|
+
role: "region",
|
|
65
|
+
style: background,
|
|
66
|
+
...rest,
|
|
67
|
+
children
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
InfinitePlane.displayName = "InfinitePlane";
|
|
73
|
+
export {
|
|
74
|
+
InfinitePlane
|
|
75
|
+
};
|
|
@@ -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,108 @@
|
|
|
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 STATUS_DOT = {
|
|
8
|
+
active: "bg-emerald-500",
|
|
9
|
+
away: "bg-amber-500",
|
|
10
|
+
idle: "bg-muted-foreground",
|
|
11
|
+
offline: "bg-muted-foreground/40"
|
|
12
|
+
};
|
|
13
|
+
const DEFAULT_LABELS = {
|
|
14
|
+
overflowSuffix: "more",
|
|
15
|
+
region: "Live presence"
|
|
16
|
+
};
|
|
17
|
+
const Avatar = (props) => {
|
|
18
|
+
const { user } = props;
|
|
19
|
+
const status = user.status ?? "active";
|
|
20
|
+
return /* @__PURE__ */ jsxs(
|
|
21
|
+
"span",
|
|
22
|
+
{
|
|
23
|
+
className: "relative -ml-2 inline-flex h-7 w-7 items-center justify-center rounded-full border-2 border-background text-[11px] font-semibold text-white shadow-sm first:ml-0",
|
|
24
|
+
"data-presence-stack-status": status,
|
|
25
|
+
"data-presence-stack-user": user.id,
|
|
26
|
+
style: { backgroundColor: user.color ?? "var(--foreground)" },
|
|
27
|
+
title: user.name,
|
|
28
|
+
children: [
|
|
29
|
+
user.initial,
|
|
30
|
+
/* @__PURE__ */ jsx(
|
|
31
|
+
"span",
|
|
32
|
+
{
|
|
33
|
+
"aria-hidden": "true",
|
|
34
|
+
className: cn(
|
|
35
|
+
"absolute -bottom-0.5 -right-0.5 h-2 w-2 rounded-full border border-background",
|
|
36
|
+
STATUS_DOT[status]
|
|
37
|
+
),
|
|
38
|
+
"data-presence-stack-dot": true
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
const PresenceStack = forwardRef(
|
|
46
|
+
(props, ref) => {
|
|
47
|
+
const {
|
|
48
|
+
className,
|
|
49
|
+
labels,
|
|
50
|
+
max = 5,
|
|
51
|
+
onOverflowActivate,
|
|
52
|
+
users,
|
|
53
|
+
...rest
|
|
54
|
+
} = props;
|
|
55
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
56
|
+
const visible = max >= users.length ? users : users.slice(0, max);
|
|
57
|
+
const hidden = users.length - visible.length;
|
|
58
|
+
const handleOverflow = () => {
|
|
59
|
+
onOverflowActivate?.();
|
|
60
|
+
};
|
|
61
|
+
return /* @__PURE__ */ jsxs(
|
|
62
|
+
"div",
|
|
63
|
+
{
|
|
64
|
+
"aria-label": resolvedLabels.region,
|
|
65
|
+
className: cn("inline-flex items-center pl-2", className),
|
|
66
|
+
"data-presence-stack": true,
|
|
67
|
+
ref,
|
|
68
|
+
role: "group",
|
|
69
|
+
...rest,
|
|
70
|
+
children: [
|
|
71
|
+
visible.map((user) => /* @__PURE__ */ jsx(Avatar, { user }, user.id)),
|
|
72
|
+
hidden > 0 ? renderOverflow({
|
|
73
|
+
count: hidden,
|
|
74
|
+
handleClick: handleOverflow,
|
|
75
|
+
handlerProvided: Boolean(onOverflowActivate),
|
|
76
|
+
labels: resolvedLabels
|
|
77
|
+
}) : null
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
PresenceStack.displayName = "PresenceStack";
|
|
84
|
+
const renderOverflow = (input) => {
|
|
85
|
+
const text = `+${input.count}`;
|
|
86
|
+
const aria = `${input.count} ${input.labels.overflowSuffix}`;
|
|
87
|
+
const className = "relative -ml-2 inline-flex h-7 min-w-7 items-center justify-center rounded-full border-2 border-background bg-muted px-1.5 text-[10px] font-semibold text-muted-foreground shadow-sm";
|
|
88
|
+
if (input.handlerProvided) {
|
|
89
|
+
return /* @__PURE__ */ jsx(
|
|
90
|
+
"button",
|
|
91
|
+
{
|
|
92
|
+
"aria-label": aria,
|
|
93
|
+
className: cn(
|
|
94
|
+
className,
|
|
95
|
+
"transition-colors hover:bg-muted/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
96
|
+
),
|
|
97
|
+
"data-presence-stack-overflow": true,
|
|
98
|
+
onClick: input.handleClick,
|
|
99
|
+
type: "button",
|
|
100
|
+
children: text
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return /* @__PURE__ */ jsx("span", { "aria-label": aria, className, "data-presence-stack-overflow": true, children: text });
|
|
105
|
+
};
|
|
106
|
+
export {
|
|
107
|
+
PresenceStack
|
|
108
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
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 STATE_LABEL = {
|
|
8
|
+
error: "Sync error",
|
|
9
|
+
live: "Live",
|
|
10
|
+
offline: "Offline",
|
|
11
|
+
reconnecting: "Reconnecting",
|
|
12
|
+
syncing: "Syncing"
|
|
13
|
+
};
|
|
14
|
+
const STATE_DOT = {
|
|
15
|
+
error: "bg-red-500",
|
|
16
|
+
live: "bg-emerald-500",
|
|
17
|
+
offline: "bg-muted-foreground",
|
|
18
|
+
reconnecting: "bg-amber-500 animate-pulse",
|
|
19
|
+
syncing: "bg-blue-500 animate-pulse"
|
|
20
|
+
};
|
|
21
|
+
const STATE_TEXT = {
|
|
22
|
+
error: "text-red-700 dark:text-red-300",
|
|
23
|
+
live: "text-emerald-700 dark:text-emerald-300",
|
|
24
|
+
offline: "text-muted-foreground",
|
|
25
|
+
reconnecting: "text-amber-700 dark:text-amber-300",
|
|
26
|
+
syncing: "text-blue-700 dark:text-blue-300"
|
|
27
|
+
};
|
|
28
|
+
const DEFAULT_LABELS = {
|
|
29
|
+
region: "Presence sync"
|
|
30
|
+
};
|
|
31
|
+
const PresenceSyncIndicator = forwardRef((props, ref) => {
|
|
32
|
+
const { className, label, labels, state, status, ...rest } = props;
|
|
33
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
34
|
+
const text = label ?? STATE_LABEL[state];
|
|
35
|
+
return /* @__PURE__ */ jsxs(
|
|
36
|
+
"div",
|
|
37
|
+
{
|
|
38
|
+
"aria-label": `${resolvedLabels.region}: ${STATE_LABEL[state]}`,
|
|
39
|
+
className: cn(
|
|
40
|
+
"inline-flex items-center gap-1.5 rounded-full border border-border bg-background px-2 py-1 text-[11px] shadow-sm",
|
|
41
|
+
className
|
|
42
|
+
),
|
|
43
|
+
"data-presence-state": state,
|
|
44
|
+
"data-presence-sync": true,
|
|
45
|
+
ref,
|
|
46
|
+
role: "status",
|
|
47
|
+
...rest,
|
|
48
|
+
children: [
|
|
49
|
+
/* @__PURE__ */ jsx(
|
|
50
|
+
"span",
|
|
51
|
+
{
|
|
52
|
+
"aria-hidden": "true",
|
|
53
|
+
className: cn("h-1.5 w-1.5 rounded-full", STATE_DOT[state]),
|
|
54
|
+
"data-presence-sync-dot": true
|
|
55
|
+
}
|
|
56
|
+
),
|
|
57
|
+
/* @__PURE__ */ jsx("span", { className: cn("font-medium", STATE_TEXT[state]), children: text }),
|
|
58
|
+
status ? /* @__PURE__ */ jsx(
|
|
59
|
+
"span",
|
|
60
|
+
{
|
|
61
|
+
className: "text-[10px] text-muted-foreground",
|
|
62
|
+
"data-presence-sync-status": true,
|
|
63
|
+
children: status
|
|
64
|
+
}
|
|
65
|
+
) : null
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
PresenceSyncIndicator.displayName = "PresenceSyncIndicator";
|
|
71
|
+
export {
|
|
72
|
+
PresenceSyncIndicator
|
|
73
|
+
};
|