@vllnt/ui 0.2.1-canary.4abeac1 → 0.2.1-canary.7fde2d1
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 +36 -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/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 +664 -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,33 @@ 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
|
+
SelectionPresence
|
|
565
|
+
} from "./selection-presence";
|
|
566
|
+
import {
|
|
567
|
+
ThreadBubble
|
|
568
|
+
} from "./thread-bubble";
|
|
542
569
|
import {
|
|
543
570
|
ConversationEmpty,
|
|
544
571
|
ConversationHeader,
|
|
@@ -633,6 +660,7 @@ export {
|
|
|
633
660
|
CommandList,
|
|
634
661
|
CommandSeparator,
|
|
635
662
|
CommandShortcut,
|
|
663
|
+
CommentPin,
|
|
636
664
|
CommonMistake,
|
|
637
665
|
Comparison,
|
|
638
666
|
CompletionDialog,
|
|
@@ -737,6 +765,7 @@ export {
|
|
|
737
765
|
HoverCard,
|
|
738
766
|
HoverCardContent,
|
|
739
767
|
HoverCardTrigger,
|
|
768
|
+
InfinitePlane,
|
|
740
769
|
InlineInput,
|
|
741
770
|
Input,
|
|
742
771
|
InputOTP,
|
|
@@ -750,6 +779,7 @@ export {
|
|
|
750
779
|
LearningObjectives,
|
|
751
780
|
LeftRail,
|
|
752
781
|
LineChart,
|
|
782
|
+
LiveCursor,
|
|
753
783
|
LiveFeed,
|
|
754
784
|
MDXContent,
|
|
755
785
|
MarketTreemap,
|
|
@@ -798,6 +828,8 @@ export {
|
|
|
798
828
|
PopoverContent,
|
|
799
829
|
PopoverTrigger,
|
|
800
830
|
Prerequisites,
|
|
831
|
+
PresenceStack,
|
|
832
|
+
PresenceSyncIndicator,
|
|
801
833
|
ProTip,
|
|
802
834
|
ProfileSection,
|
|
803
835
|
ProgressBar,
|
|
@@ -835,6 +867,7 @@ export {
|
|
|
835
867
|
SelectSeparator,
|
|
836
868
|
SelectTrigger,
|
|
837
869
|
SelectValue,
|
|
870
|
+
SelectionPresence,
|
|
838
871
|
Separator,
|
|
839
872
|
SeverityBadge,
|
|
840
873
|
ShareDialog,
|
|
@@ -890,6 +923,7 @@ export {
|
|
|
890
923
|
ThemeProvider,
|
|
891
924
|
ThemeToggle,
|
|
892
925
|
ThinkingBlock,
|
|
926
|
+
ThreadBubble,
|
|
893
927
|
TickerTape,
|
|
894
928
|
Toast,
|
|
895
929
|
ToastAction,
|
|
@@ -916,9 +950,11 @@ export {
|
|
|
916
950
|
UsageBreakdown,
|
|
917
951
|
VideoEmbed,
|
|
918
952
|
ViewSwitcher,
|
|
953
|
+
ViewportBookmarks,
|
|
919
954
|
WalletCard,
|
|
920
955
|
Watchlist,
|
|
921
956
|
WorkspaceSwitcher,
|
|
957
|
+
WorldBreadcrumbs,
|
|
922
958
|
WorldClockBar,
|
|
923
959
|
ZoomHUD,
|
|
924
960
|
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
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
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: "Selection presence"
|
|
9
|
+
};
|
|
10
|
+
const SelectionPresence = forwardRef((props, ref) => {
|
|
11
|
+
const { className, color, height, labels, name, width, x, y, ...rest } = props;
|
|
12
|
+
const resolvedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
13
|
+
const accent = color ?? "var(--foreground)";
|
|
14
|
+
const ariaLabel = typeof name === "string" ? `${resolvedLabels.region}: ${name}` : resolvedLabels.region;
|
|
15
|
+
return /* @__PURE__ */ jsx(
|
|
16
|
+
"div",
|
|
17
|
+
{
|
|
18
|
+
"aria-label": ariaLabel,
|
|
19
|
+
className: cn(
|
|
20
|
+
"pointer-events-none absolute z-20 rounded-md border-2 border-dashed",
|
|
21
|
+
className
|
|
22
|
+
),
|
|
23
|
+
"data-selection-presence": true,
|
|
24
|
+
ref,
|
|
25
|
+
role: "img",
|
|
26
|
+
style: {
|
|
27
|
+
backgroundColor: `color-mix(in srgb, ${accent} 10%, transparent)`,
|
|
28
|
+
borderColor: accent,
|
|
29
|
+
height,
|
|
30
|
+
left: x,
|
|
31
|
+
top: y,
|
|
32
|
+
width
|
|
33
|
+
},
|
|
34
|
+
...rest,
|
|
35
|
+
children: name === null || name === void 0 ? null : /* @__PURE__ */ jsx(
|
|
36
|
+
"span",
|
|
37
|
+
{
|
|
38
|
+
className: "absolute -top-5 left-0 inline-flex items-center rounded-md px-1.5 py-0.5 text-[10px] font-medium text-white shadow-sm",
|
|
39
|
+
"data-selection-presence-chip": true,
|
|
40
|
+
style: { backgroundColor: accent },
|
|
41
|
+
children: name
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
SelectionPresence.displayName = "SelectionPresence";
|
|
48
|
+
export {
|
|
49
|
+
SelectionPresence
|
|
50
|
+
};
|