@liveblocks/react-ui 3.15.0-thread2 → 3.15.1-rc1

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.
Files changed (157) hide show
  1. package/README.md +6 -16
  2. package/dist/_private/index.cjs +3 -5
  3. package/dist/_private/index.cjs.map +1 -1
  4. package/dist/_private/index.d.cts +6 -4
  5. package/dist/_private/index.d.ts +6 -4
  6. package/dist/_private/index.js +2 -2
  7. package/dist/components/AvatarStack.cjs +122 -0
  8. package/dist/components/AvatarStack.cjs.map +1 -0
  9. package/dist/components/AvatarStack.js +120 -0
  10. package/dist/components/AvatarStack.js.map +1 -0
  11. package/dist/components/Comment.cjs +10 -28
  12. package/dist/components/Comment.cjs.map +1 -1
  13. package/dist/components/Comment.js +12 -11
  14. package/dist/components/Comment.js.map +1 -1
  15. package/dist/components/CommentPin.cjs +38 -0
  16. package/dist/components/CommentPin.cjs.map +1 -0
  17. package/dist/components/CommentPin.js +36 -0
  18. package/dist/components/CommentPin.js.map +1 -0
  19. package/dist/components/Composer.cjs +2 -4
  20. package/dist/components/Composer.cjs.map +1 -1
  21. package/dist/components/Composer.js +3 -5
  22. package/dist/components/Composer.js.map +1 -1
  23. package/dist/components/Cursor.cjs +40 -0
  24. package/dist/components/Cursor.cjs.map +1 -0
  25. package/dist/components/Cursor.js +38 -0
  26. package/dist/components/Cursor.js.map +1 -0
  27. package/dist/components/Cursors.cjs +256 -0
  28. package/dist/components/Cursors.cjs.map +1 -0
  29. package/dist/components/Cursors.js +254 -0
  30. package/dist/components/Cursors.js.map +1 -0
  31. package/dist/components/FloatingComposer.cjs +97 -0
  32. package/dist/components/FloatingComposer.cjs.map +1 -0
  33. package/dist/components/FloatingComposer.js +95 -0
  34. package/dist/components/FloatingComposer.js.map +1 -0
  35. package/dist/components/FloatingThread.cjs +83 -0
  36. package/dist/components/FloatingThread.cjs.map +1 -0
  37. package/dist/components/FloatingThread.js +81 -0
  38. package/dist/components/FloatingThread.js.map +1 -0
  39. package/dist/components/InboxNotification.cjs +4 -6
  40. package/dist/components/InboxNotification.cjs.map +1 -1
  41. package/dist/components/InboxNotification.js +5 -7
  42. package/dist/components/InboxNotification.js.map +1 -1
  43. package/dist/components/Thread.cjs +19 -28
  44. package/dist/components/Thread.cjs.map +1 -1
  45. package/dist/components/Thread.js +19 -9
  46. package/dist/components/Thread.js.map +1 -1
  47. package/dist/components/internal/AiComposer.cjs +1 -2
  48. package/dist/components/internal/AiComposer.cjs.map +1 -1
  49. package/dist/components/internal/AiComposer.js +1 -2
  50. package/dist/components/internal/AiComposer.js.map +1 -1
  51. package/dist/components/internal/Avatar.cjs +10 -13
  52. package/dist/components/internal/Avatar.cjs.map +1 -1
  53. package/dist/components/internal/Avatar.js +11 -14
  54. package/dist/components/internal/Avatar.js.map +1 -1
  55. package/dist/components/internal/CodeBlock.cjs +1 -2
  56. package/dist/components/internal/CodeBlock.cjs.map +1 -1
  57. package/dist/components/internal/CodeBlock.js +1 -2
  58. package/dist/components/internal/CodeBlock.js.map +1 -1
  59. package/dist/components/internal/Dropdown.cjs +8 -28
  60. package/dist/components/internal/Dropdown.cjs.map +1 -1
  61. package/dist/components/internal/Dropdown.js +8 -7
  62. package/dist/components/internal/Dropdown.js.map +1 -1
  63. package/dist/components/internal/EmojiPicker.cjs +7 -27
  64. package/dist/components/internal/EmojiPicker.cjs.map +1 -1
  65. package/dist/components/internal/EmojiPicker.js +7 -6
  66. package/dist/components/internal/EmojiPicker.js.map +1 -1
  67. package/dist/components/internal/List.cjs +2 -2
  68. package/dist/components/internal/List.cjs.map +1 -1
  69. package/dist/components/internal/List.js +2 -2
  70. package/dist/components/internal/List.js.map +1 -1
  71. package/dist/components/internal/Tooltip.cjs +8 -28
  72. package/dist/components/internal/Tooltip.cjs.map +1 -1
  73. package/dist/components/internal/Tooltip.js +8 -7
  74. package/dist/components/internal/Tooltip.js.map +1 -1
  75. package/dist/icon.cjs +2 -0
  76. package/dist/icon.cjs.map +1 -1
  77. package/dist/icon.js +1 -0
  78. package/dist/icon.js.map +1 -1
  79. package/dist/icons/Plus.cjs +11 -0
  80. package/dist/icons/Plus.cjs.map +1 -0
  81. package/dist/icons/Plus.js +9 -0
  82. package/dist/icons/Plus.js.map +1 -0
  83. package/dist/icons/index.cjs +2 -0
  84. package/dist/icons/index.cjs.map +1 -1
  85. package/dist/icons/index.js +1 -0
  86. package/dist/icons/index.js.map +1 -1
  87. package/dist/index.cjs +12 -0
  88. package/dist/index.cjs.map +1 -1
  89. package/dist/index.d.cts +240 -137
  90. package/dist/index.d.ts +240 -137
  91. package/dist/index.js +6 -0
  92. package/dist/index.js.map +1 -1
  93. package/dist/primitives/AiComposer/index.cjs +5 -4
  94. package/dist/primitives/AiComposer/index.cjs.map +1 -1
  95. package/dist/primitives/AiComposer/index.js +5 -4
  96. package/dist/primitives/AiComposer/index.js.map +1 -1
  97. package/dist/primitives/AiMessage/index.cjs +2 -2
  98. package/dist/primitives/AiMessage/index.cjs.map +1 -1
  99. package/dist/primitives/AiMessage/index.js +2 -2
  100. package/dist/primitives/AiMessage/index.js.map +1 -1
  101. package/dist/primitives/Collapsible/index.cjs +4 -4
  102. package/dist/primitives/Collapsible/index.cjs.map +1 -1
  103. package/dist/primitives/Collapsible/index.js +4 -4
  104. package/dist/primitives/Collapsible/index.js.map +1 -1
  105. package/dist/primitives/Comment/index.cjs +4 -4
  106. package/dist/primitives/Comment/index.cjs.map +1 -1
  107. package/dist/primitives/Comment/index.js +4 -4
  108. package/dist/primitives/Comment/index.js.map +1 -1
  109. package/dist/primitives/Composer/index.cjs +23 -35
  110. package/dist/primitives/Composer/index.cjs.map +1 -1
  111. package/dist/primitives/Composer/index.js +23 -16
  112. package/dist/primitives/Composer/index.js.map +1 -1
  113. package/dist/primitives/Duration.cjs +2 -2
  114. package/dist/primitives/Duration.cjs.map +1 -1
  115. package/dist/primitives/Duration.js +2 -2
  116. package/dist/primitives/Duration.js.map +1 -1
  117. package/dist/primitives/FileSize.cjs +2 -2
  118. package/dist/primitives/FileSize.cjs.map +1 -1
  119. package/dist/primitives/FileSize.js +2 -2
  120. package/dist/primitives/FileSize.js.map +1 -1
  121. package/dist/primitives/Markdown.cjs +2 -2
  122. package/dist/primitives/Markdown.cjs.map +1 -1
  123. package/dist/primitives/Markdown.js +2 -2
  124. package/dist/primitives/Markdown.js.map +1 -1
  125. package/dist/primitives/Timestamp.cjs +2 -2
  126. package/dist/primitives/Timestamp.cjs.map +1 -1
  127. package/dist/primitives/Timestamp.js +2 -2
  128. package/dist/primitives/Timestamp.js.map +1 -1
  129. package/dist/utils/Portal.cjs +2 -2
  130. package/dist/utils/Portal.cjs.map +1 -1
  131. package/dist/utils/Portal.js +2 -2
  132. package/dist/utils/Portal.js.map +1 -1
  133. package/dist/utils/animation-loop.cjs +44 -0
  134. package/dist/utils/animation-loop.cjs.map +1 -0
  135. package/dist/utils/animation-loop.js +42 -0
  136. package/dist/utils/animation-loop.js.map +1 -0
  137. package/dist/utils/px.cjs +14 -0
  138. package/dist/utils/px.cjs.map +1 -0
  139. package/dist/utils/px.js +12 -0
  140. package/dist/utils/px.js.map +1 -0
  141. package/dist/utils/use-pre-resolve-user.cjs +18 -0
  142. package/dist/utils/use-pre-resolve-user.cjs.map +1 -0
  143. package/dist/utils/use-pre-resolve-user.js +16 -0
  144. package/dist/utils/use-pre-resolve-user.js.map +1 -0
  145. package/dist/version.cjs +1 -1
  146. package/dist/version.cjs.map +1 -1
  147. package/dist/version.js +1 -1
  148. package/dist/version.js.map +1 -1
  149. package/package.json +7 -10
  150. package/src/styles/dark/index.css +1 -1
  151. package/src/styles/index.css +259 -4
  152. package/styles/dark/attributes.css +1 -1
  153. package/styles/dark/attributes.css.map +1 -1
  154. package/styles/dark/media-query.css +1 -1
  155. package/styles/dark/media-query.css.map +1 -1
  156. package/styles.css +1 -1
  157. package/styles.css.map +1 -1
@@ -0,0 +1,256 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var core = require('@liveblocks/core');
5
+ var react = require('@liveblocks/react');
6
+ var _private = require('@liveblocks/react/_private');
7
+ var react$1 = require('react');
8
+ var animationLoop = require('../utils/animation-loop.cjs');
9
+ var cn = require('../utils/cn.cjs');
10
+ var useWindowFocus = require('../utils/use-window-focus.cjs');
11
+ var Cursor = require('./Cursor.cjs');
12
+
13
+ const STIFFNESS = 320;
14
+ const DAMPING = 32;
15
+ const EPSILON = 0.01;
16
+ const DEFAULT_PRESENCE_KEY = "cursor";
17
+ function $string(value) {
18
+ return typeof value === "string" ? value : void 0;
19
+ }
20
+ function $coordinates(value) {
21
+ if (core.isPlainObject(value) && typeof value.x === "number" && typeof value.y === "number") {
22
+ return value;
23
+ }
24
+ return void 0;
25
+ }
26
+ const loop = animationLoop.makeAnimationLoop();
27
+ function makeCoordinatesSpring() {
28
+ const updates = core.makeEventSource();
29
+ let value = null;
30
+ let target = null;
31
+ const velocity = { x: 0, y: 0 };
32
+ const spring = {
33
+ active: false,
34
+ step(dt) {
35
+ if (value === null || target === null) {
36
+ spring.active = false;
37
+ return;
38
+ }
39
+ const k = STIFFNESS;
40
+ const d = DAMPING;
41
+ const dx = value.x - target.x;
42
+ const dy = value.y - target.y;
43
+ velocity.x += (-k * dx - d * velocity.x) * dt;
44
+ velocity.y += (-k * dy - d * velocity.y) * dt;
45
+ const nx = value.x + velocity.x * dt;
46
+ const ny = value.y + velocity.y * dt;
47
+ if (nx !== value.x || ny !== value.y) {
48
+ value.x = nx;
49
+ value.y = ny;
50
+ updates.notify(value);
51
+ }
52
+ if (Math.abs(velocity.x) < EPSILON && Math.abs(velocity.y) < EPSILON && Math.abs(target.x - value.x) < EPSILON && Math.abs(target.y - value.y) < EPSILON) {
53
+ if (value.x !== target.x || value.y !== target.y) {
54
+ value.x = target.x;
55
+ value.y = target.y;
56
+ updates.notify(value);
57
+ }
58
+ velocity.x = 0;
59
+ velocity.y = 0;
60
+ spring.active = false;
61
+ }
62
+ }
63
+ };
64
+ return {
65
+ get() {
66
+ return value;
67
+ },
68
+ set(point) {
69
+ if (point === null) {
70
+ value = null;
71
+ target = null;
72
+ velocity.x = 0;
73
+ velocity.y = 0;
74
+ spring.active = false;
75
+ loop.remove(spring);
76
+ updates.notify(null);
77
+ return;
78
+ }
79
+ if (value === null) {
80
+ value = { x: point.x, y: point.y };
81
+ target = point;
82
+ velocity.x = 0;
83
+ velocity.y = 0;
84
+ updates.notify(value);
85
+ return;
86
+ }
87
+ target = point;
88
+ if (!spring.active && (value.x !== target.x || value.y !== target.y)) {
89
+ spring.active = true;
90
+ loop.add(spring);
91
+ }
92
+ },
93
+ subscribe: updates.subscribe,
94
+ dispose() {
95
+ spring.active = false;
96
+ loop.remove(spring);
97
+ updates.dispose();
98
+ }
99
+ };
100
+ }
101
+ function PresenceCursor({
102
+ connectionId,
103
+ presenceKey,
104
+ sizeRef,
105
+ sizeEvents
106
+ }) {
107
+ const room = react.useRoom();
108
+ const cursorRef = react$1.useRef(null);
109
+ const userId = react.useOther(connectionId, (other) => $string(other.id));
110
+ const { user, isLoading } = react.useUser(userId ?? "");
111
+ const hasUserInfo = userId !== void 0 && !isLoading;
112
+ const color = $string(user?.color);
113
+ const name = $string(user?.name);
114
+ _private.useLayoutEffect(() => {
115
+ const spring = makeCoordinatesSpring();
116
+ function update() {
117
+ const element = cursorRef.current;
118
+ const coordinates = spring.get();
119
+ if (!element) {
120
+ return;
121
+ }
122
+ if (!hasUserInfo || coordinates === null) {
123
+ element.style.transform = "translate3d(0, 0, 0)";
124
+ element.style.display = "none";
125
+ return;
126
+ }
127
+ if (sizeRef.current) {
128
+ element.style.transform = `translate3d(${coordinates.x * sizeRef.current.width}px, ${coordinates.y * sizeRef.current.height}px, 0)`;
129
+ }
130
+ element.style.display = "";
131
+ }
132
+ const unsubscribeSpring = spring.subscribe(update);
133
+ const unsubscribeSize = sizeEvents.subscribe(update);
134
+ update();
135
+ const unsubscribeOther = room.events.others.subscribe(({ others }) => {
136
+ const other = others.find((other2) => other2.connectionId === connectionId);
137
+ const cursor = $coordinates(other?.presence[presenceKey]);
138
+ spring.set(cursor ?? null);
139
+ });
140
+ return () => {
141
+ spring.dispose();
142
+ unsubscribeSpring();
143
+ unsubscribeSize();
144
+ unsubscribeOther();
145
+ };
146
+ }, [room, connectionId, presenceKey, sizeRef, sizeEvents, hasUserInfo]);
147
+ return /* @__PURE__ */ jsxRuntime.jsx(
148
+ Cursor.Cursor,
149
+ {
150
+ color,
151
+ label: name,
152
+ ref: cursorRef,
153
+ style: { display: "none" }
154
+ }
155
+ );
156
+ }
157
+ const Cursors = react$1.forwardRef(
158
+ ({ className, children, presenceKey = DEFAULT_PRESENCE_KEY, ...props }, forwardedRef) => {
159
+ const ref = react$1.useRef(null);
160
+ const updateMyPresence = react.useUpdateMyPresence();
161
+ const othersConnectionIds = react.useOthersConnectionIds();
162
+ const sizeRef = react$1.useRef(null);
163
+ const [sizeEvents] = react$1.useState(() => core.makeEventSource());
164
+ const isWindowFocused = useWindowFocus.useWindowFocus();
165
+ react$1.useEffect(() => {
166
+ const element = ref.current;
167
+ if (!element) {
168
+ return;
169
+ }
170
+ function setSize(size) {
171
+ sizeRef.current = size;
172
+ sizeEvents.notify();
173
+ }
174
+ const observer = new ResizeObserver((entries) => {
175
+ for (const entry of entries) {
176
+ if (entry.target === element) {
177
+ setSize({
178
+ width: entry.contentRect.width,
179
+ height: entry.contentRect.height
180
+ });
181
+ }
182
+ }
183
+ });
184
+ setSize({
185
+ width: element.clientWidth,
186
+ height: element.clientHeight
187
+ });
188
+ observer.observe(element);
189
+ return () => {
190
+ observer.disconnect();
191
+ };
192
+ }, [sizeEvents]);
193
+ const handlePointerMove = react$1.useCallback(
194
+ (event) => {
195
+ const element = ref.current;
196
+ if (!element) {
197
+ return;
198
+ }
199
+ const bounds = element.getBoundingClientRect();
200
+ if (bounds.width === 0 || bounds.height === 0) {
201
+ return;
202
+ }
203
+ updateMyPresence({
204
+ [presenceKey]: {
205
+ x: (event.clientX - bounds.left) / bounds.width,
206
+ y: (event.clientY - bounds.top) / bounds.height
207
+ }
208
+ });
209
+ },
210
+ [updateMyPresence, presenceKey]
211
+ );
212
+ const handlePointerLeave = react$1.useCallback(() => {
213
+ updateMyPresence({
214
+ [presenceKey]: null
215
+ });
216
+ }, [updateMyPresence, presenceKey]);
217
+ react$1.useEffect(() => {
218
+ if (!isWindowFocused) {
219
+ updateMyPresence({
220
+ [presenceKey]: null
221
+ });
222
+ }
223
+ }, [isWindowFocused, updateMyPresence, presenceKey]);
224
+ react$1.useImperativeHandle(
225
+ forwardedRef,
226
+ () => ref.current,
227
+ []
228
+ );
229
+ return /* @__PURE__ */ jsxRuntime.jsxs(
230
+ "div",
231
+ {
232
+ className: cn.cn("lb-root lb-cursors", className),
233
+ ...props,
234
+ onPointerMove: handlePointerMove,
235
+ onPointerLeave: handlePointerLeave,
236
+ ref,
237
+ children: [
238
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-cursors-container", children: othersConnectionIds.map((connectionId) => /* @__PURE__ */ jsxRuntime.jsx(
239
+ PresenceCursor,
240
+ {
241
+ connectionId,
242
+ presenceKey,
243
+ sizeRef,
244
+ sizeEvents
245
+ },
246
+ connectionId
247
+ )) }),
248
+ children
249
+ ]
250
+ }
251
+ );
252
+ }
253
+ );
254
+
255
+ exports.Cursors = Cursors;
256
+ //# sourceMappingURL=Cursors.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Cursors.cjs","sources":["../../src/components/Cursors.tsx"],"sourcesContent":["import type { EventSource } from \"@liveblocks/core\";\nimport { isPlainObject, makeEventSource } from \"@liveblocks/core\";\nimport {\n useOther,\n useOthersConnectionIds,\n useRoom,\n useUpdateMyPresence,\n useUser,\n} from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n MutableRefObject,\n PointerEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\n\nimport { type Animatable, makeAnimationLoop } from \"../utils/animation-loop\";\nimport { cn } from \"../utils/cn\";\nimport { useWindowFocus } from \"../utils/use-window-focus\";\nimport { Cursor } from \"./Cursor\";\n\nconst STIFFNESS = 320;\nconst DAMPING = 32;\nconst EPSILON = 0.01;\n\nconst DEFAULT_PRESENCE_KEY = \"cursor\";\n\nexport interface CursorsProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The key used to store the cursors in users' Presence.\n * This can be used to have multiple `Cursors` in a single room.\n */\n presenceKey?: string;\n}\n\ntype Coordinates = {\n x: number;\n y: number;\n};\n\ntype Size = {\n width: number;\n height: number;\n};\n\nfunction $string(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction $coordinates(value: unknown): Coordinates | undefined {\n if (\n isPlainObject(value) &&\n typeof value.x === \"number\" &&\n typeof value.y === \"number\"\n ) {\n return value as Coordinates;\n }\n\n return undefined;\n}\n\n// Use a shared animation loop for all (active) springs.\nconst loop = makeAnimationLoop();\n\nfunction makeCoordinatesSpring() {\n const updates = makeEventSource<Coordinates | null>();\n let value: Coordinates | null = null;\n let target: Coordinates | null = null;\n const velocity = { x: 0, y: 0 };\n\n const spring: Animatable = {\n active: false,\n step(dt: number) {\n if (value === null || target === null) {\n spring.active = false;\n return;\n }\n\n const k = STIFFNESS;\n const d = DAMPING;\n const dx = value.x - target.x;\n const dy = value.y - target.y;\n\n velocity.x += (-k * dx - d * velocity.x) * dt;\n velocity.y += (-k * dy - d * velocity.y) * dt;\n\n const nx = value.x + velocity.x * dt;\n const ny = value.y + velocity.y * dt;\n\n if (nx !== value.x || ny !== value.y) {\n value.x = nx;\n value.y = ny;\n updates.notify(value);\n }\n\n if (\n Math.abs(velocity.x) < EPSILON &&\n Math.abs(velocity.y) < EPSILON &&\n Math.abs(target.x - value.x) < EPSILON &&\n Math.abs(target.y - value.y) < EPSILON\n ) {\n if (value.x !== target.x || value.y !== target.y) {\n value.x = target.x;\n value.y = target.y;\n updates.notify(value);\n }\n\n velocity.x = 0;\n velocity.y = 0;\n spring.active = false;\n }\n },\n };\n\n return {\n get() {\n return value;\n },\n set(point: Coordinates | null) {\n if (point === null) {\n value = null;\n target = null;\n velocity.x = 0;\n velocity.y = 0;\n spring.active = false;\n loop.remove(spring);\n updates.notify(null);\n return;\n }\n\n if (value === null) {\n value = { x: point.x, y: point.y };\n target = point;\n velocity.x = 0;\n velocity.y = 0;\n updates.notify(value);\n return;\n }\n\n target = point;\n\n if (!spring.active && (value.x !== target.x || value.y !== target.y)) {\n spring.active = true;\n loop.add(spring);\n }\n },\n subscribe: updates.subscribe,\n dispose() {\n spring.active = false;\n loop.remove(spring);\n updates.dispose();\n },\n };\n}\n\nfunction PresenceCursor({\n connectionId,\n presenceKey,\n sizeRef,\n sizeEvents,\n}: {\n connectionId: number;\n presenceKey: string;\n sizeRef: MutableRefObject<Size | null>;\n sizeEvents: EventSource<void>;\n}) {\n const room = useRoom();\n const cursorRef = useRef<HTMLDivElement>(null);\n const userId = useOther(connectionId, (other) => $string(other.id));\n const { user, isLoading } = useUser(userId ?? \"\");\n const hasUserInfo = userId !== undefined && !isLoading;\n const color = $string(user?.color);\n const name = $string(user?.name);\n\n useLayoutEffect(() => {\n const spring = makeCoordinatesSpring();\n\n function update() {\n const element = cursorRef.current;\n const coordinates = spring.get();\n\n if (!element) {\n return;\n }\n\n if (!hasUserInfo || coordinates === null) {\n element.style.transform = \"translate3d(0, 0, 0)\";\n element.style.display = \"none\";\n return;\n }\n\n if (sizeRef.current) {\n element.style.transform = `translate3d(${coordinates.x * sizeRef.current.width}px, ${coordinates.y * sizeRef.current.height}px, 0)`;\n }\n\n element.style.display = \"\";\n }\n\n const unsubscribeSpring = spring.subscribe(update);\n const unsubscribeSize = sizeEvents.subscribe(update);\n update();\n\n const unsubscribeOther = room.events.others.subscribe(({ others }) => {\n const other = others.find((other) => other.connectionId === connectionId);\n const cursor = $coordinates(other?.presence[presenceKey]);\n\n spring.set(cursor ?? null);\n });\n\n return () => {\n spring.dispose();\n unsubscribeSpring();\n unsubscribeSize();\n unsubscribeOther();\n };\n }, [room, connectionId, presenceKey, sizeRef, sizeEvents, hasUserInfo]);\n\n return (\n <Cursor\n color={color}\n label={name}\n ref={cursorRef}\n style={{ display: \"none\" }}\n />\n );\n}\n\n/**\n * Displays multiplayer cursors.\n */\nexport const Cursors = forwardRef<HTMLDivElement, CursorsProps>(\n (\n { className, children, presenceKey = DEFAULT_PRESENCE_KEY, ...props },\n forwardedRef\n ) => {\n const ref = useRef<HTMLDivElement>(null);\n const updateMyPresence = useUpdateMyPresence();\n const othersConnectionIds = useOthersConnectionIds();\n const sizeRef = useRef<Size | null>(null);\n const [sizeEvents] = useState(() => makeEventSource<void>());\n const isWindowFocused = useWindowFocus();\n\n useEffect(() => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n function setSize(size: Size) {\n sizeRef.current = size;\n sizeEvents.notify();\n }\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n if (entry.target === element) {\n setSize({\n width: entry.contentRect.width,\n height: entry.contentRect.height,\n });\n }\n }\n });\n\n setSize({\n width: element.clientWidth,\n height: element.clientHeight,\n });\n\n observer.observe(element);\n\n return () => {\n observer.disconnect();\n };\n }, [sizeEvents]);\n\n const handlePointerMove = useCallback(\n (event: PointerEvent) => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n const bounds = element.getBoundingClientRect();\n\n if (bounds.width === 0 || bounds.height === 0) {\n return;\n }\n\n updateMyPresence({\n [presenceKey]: {\n x: (event.clientX - bounds.left) / bounds.width,\n y: (event.clientY - bounds.top) / bounds.height,\n },\n });\n },\n [updateMyPresence, presenceKey]\n );\n\n const handlePointerLeave = useCallback(() => {\n updateMyPresence({\n [presenceKey]: null,\n });\n }, [updateMyPresence, presenceKey]);\n\n useEffect(() => {\n if (!isWindowFocused) {\n updateMyPresence({\n [presenceKey]: null,\n });\n }\n }, [isWindowFocused, updateMyPresence, presenceKey]);\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => ref.current,\n []\n );\n\n return (\n <div\n className={cn(\"lb-root lb-cursors\", className)}\n {...props}\n onPointerMove={handlePointerMove}\n onPointerLeave={handlePointerLeave}\n ref={ref}\n >\n <div className=\"lb-cursors-container\">\n {othersConnectionIds.map((connectionId) => (\n <PresenceCursor\n key={connectionId}\n connectionId={connectionId}\n presenceKey={presenceKey}\n sizeRef={sizeRef}\n sizeEvents={sizeEvents}\n />\n ))}\n </div>\n\n {children}\n </div>\n );\n }\n);\n"],"names":["isPlainObject","makeAnimationLoop","makeEventSource","useRoom","useRef","useOther","useUser","useLayoutEffect","other","jsx","Cursor","forwardRef","useUpdateMyPresence","useOthersConnectionIds","useState","useWindowFocus","useEffect","useCallback","useImperativeHandle","jsxs","cn"],"mappings":"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"}
@@ -0,0 +1,254 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { isPlainObject, makeEventSource } from '@liveblocks/core';
3
+ import { useRoom, useOther, useUser, useUpdateMyPresence, useOthersConnectionIds } from '@liveblocks/react';
4
+ import { useLayoutEffect } from '@liveblocks/react/_private';
5
+ import { useRef, forwardRef, useState, useEffect, useCallback, useImperativeHandle } from 'react';
6
+ import { makeAnimationLoop } from '../utils/animation-loop.js';
7
+ import { cn } from '../utils/cn.js';
8
+ import { useWindowFocus } from '../utils/use-window-focus.js';
9
+ import { Cursor } from './Cursor.js';
10
+
11
+ const STIFFNESS = 320;
12
+ const DAMPING = 32;
13
+ const EPSILON = 0.01;
14
+ const DEFAULT_PRESENCE_KEY = "cursor";
15
+ function $string(value) {
16
+ return typeof value === "string" ? value : void 0;
17
+ }
18
+ function $coordinates(value) {
19
+ if (isPlainObject(value) && typeof value.x === "number" && typeof value.y === "number") {
20
+ return value;
21
+ }
22
+ return void 0;
23
+ }
24
+ const loop = makeAnimationLoop();
25
+ function makeCoordinatesSpring() {
26
+ const updates = makeEventSource();
27
+ let value = null;
28
+ let target = null;
29
+ const velocity = { x: 0, y: 0 };
30
+ const spring = {
31
+ active: false,
32
+ step(dt) {
33
+ if (value === null || target === null) {
34
+ spring.active = false;
35
+ return;
36
+ }
37
+ const k = STIFFNESS;
38
+ const d = DAMPING;
39
+ const dx = value.x - target.x;
40
+ const dy = value.y - target.y;
41
+ velocity.x += (-k * dx - d * velocity.x) * dt;
42
+ velocity.y += (-k * dy - d * velocity.y) * dt;
43
+ const nx = value.x + velocity.x * dt;
44
+ const ny = value.y + velocity.y * dt;
45
+ if (nx !== value.x || ny !== value.y) {
46
+ value.x = nx;
47
+ value.y = ny;
48
+ updates.notify(value);
49
+ }
50
+ if (Math.abs(velocity.x) < EPSILON && Math.abs(velocity.y) < EPSILON && Math.abs(target.x - value.x) < EPSILON && Math.abs(target.y - value.y) < EPSILON) {
51
+ if (value.x !== target.x || value.y !== target.y) {
52
+ value.x = target.x;
53
+ value.y = target.y;
54
+ updates.notify(value);
55
+ }
56
+ velocity.x = 0;
57
+ velocity.y = 0;
58
+ spring.active = false;
59
+ }
60
+ }
61
+ };
62
+ return {
63
+ get() {
64
+ return value;
65
+ },
66
+ set(point) {
67
+ if (point === null) {
68
+ value = null;
69
+ target = null;
70
+ velocity.x = 0;
71
+ velocity.y = 0;
72
+ spring.active = false;
73
+ loop.remove(spring);
74
+ updates.notify(null);
75
+ return;
76
+ }
77
+ if (value === null) {
78
+ value = { x: point.x, y: point.y };
79
+ target = point;
80
+ velocity.x = 0;
81
+ velocity.y = 0;
82
+ updates.notify(value);
83
+ return;
84
+ }
85
+ target = point;
86
+ if (!spring.active && (value.x !== target.x || value.y !== target.y)) {
87
+ spring.active = true;
88
+ loop.add(spring);
89
+ }
90
+ },
91
+ subscribe: updates.subscribe,
92
+ dispose() {
93
+ spring.active = false;
94
+ loop.remove(spring);
95
+ updates.dispose();
96
+ }
97
+ };
98
+ }
99
+ function PresenceCursor({
100
+ connectionId,
101
+ presenceKey,
102
+ sizeRef,
103
+ sizeEvents
104
+ }) {
105
+ const room = useRoom();
106
+ const cursorRef = useRef(null);
107
+ const userId = useOther(connectionId, (other) => $string(other.id));
108
+ const { user, isLoading } = useUser(userId ?? "");
109
+ const hasUserInfo = userId !== void 0 && !isLoading;
110
+ const color = $string(user?.color);
111
+ const name = $string(user?.name);
112
+ useLayoutEffect(() => {
113
+ const spring = makeCoordinatesSpring();
114
+ function update() {
115
+ const element = cursorRef.current;
116
+ const coordinates = spring.get();
117
+ if (!element) {
118
+ return;
119
+ }
120
+ if (!hasUserInfo || coordinates === null) {
121
+ element.style.transform = "translate3d(0, 0, 0)";
122
+ element.style.display = "none";
123
+ return;
124
+ }
125
+ if (sizeRef.current) {
126
+ element.style.transform = `translate3d(${coordinates.x * sizeRef.current.width}px, ${coordinates.y * sizeRef.current.height}px, 0)`;
127
+ }
128
+ element.style.display = "";
129
+ }
130
+ const unsubscribeSpring = spring.subscribe(update);
131
+ const unsubscribeSize = sizeEvents.subscribe(update);
132
+ update();
133
+ const unsubscribeOther = room.events.others.subscribe(({ others }) => {
134
+ const other = others.find((other2) => other2.connectionId === connectionId);
135
+ const cursor = $coordinates(other?.presence[presenceKey]);
136
+ spring.set(cursor ?? null);
137
+ });
138
+ return () => {
139
+ spring.dispose();
140
+ unsubscribeSpring();
141
+ unsubscribeSize();
142
+ unsubscribeOther();
143
+ };
144
+ }, [room, connectionId, presenceKey, sizeRef, sizeEvents, hasUserInfo]);
145
+ return /* @__PURE__ */ jsx(
146
+ Cursor,
147
+ {
148
+ color,
149
+ label: name,
150
+ ref: cursorRef,
151
+ style: { display: "none" }
152
+ }
153
+ );
154
+ }
155
+ const Cursors = forwardRef(
156
+ ({ className, children, presenceKey = DEFAULT_PRESENCE_KEY, ...props }, forwardedRef) => {
157
+ const ref = useRef(null);
158
+ const updateMyPresence = useUpdateMyPresence();
159
+ const othersConnectionIds = useOthersConnectionIds();
160
+ const sizeRef = useRef(null);
161
+ const [sizeEvents] = useState(() => makeEventSource());
162
+ const isWindowFocused = useWindowFocus();
163
+ useEffect(() => {
164
+ const element = ref.current;
165
+ if (!element) {
166
+ return;
167
+ }
168
+ function setSize(size) {
169
+ sizeRef.current = size;
170
+ sizeEvents.notify();
171
+ }
172
+ const observer = new ResizeObserver((entries) => {
173
+ for (const entry of entries) {
174
+ if (entry.target === element) {
175
+ setSize({
176
+ width: entry.contentRect.width,
177
+ height: entry.contentRect.height
178
+ });
179
+ }
180
+ }
181
+ });
182
+ setSize({
183
+ width: element.clientWidth,
184
+ height: element.clientHeight
185
+ });
186
+ observer.observe(element);
187
+ return () => {
188
+ observer.disconnect();
189
+ };
190
+ }, [sizeEvents]);
191
+ const handlePointerMove = useCallback(
192
+ (event) => {
193
+ const element = ref.current;
194
+ if (!element) {
195
+ return;
196
+ }
197
+ const bounds = element.getBoundingClientRect();
198
+ if (bounds.width === 0 || bounds.height === 0) {
199
+ return;
200
+ }
201
+ updateMyPresence({
202
+ [presenceKey]: {
203
+ x: (event.clientX - bounds.left) / bounds.width,
204
+ y: (event.clientY - bounds.top) / bounds.height
205
+ }
206
+ });
207
+ },
208
+ [updateMyPresence, presenceKey]
209
+ );
210
+ const handlePointerLeave = useCallback(() => {
211
+ updateMyPresence({
212
+ [presenceKey]: null
213
+ });
214
+ }, [updateMyPresence, presenceKey]);
215
+ useEffect(() => {
216
+ if (!isWindowFocused) {
217
+ updateMyPresence({
218
+ [presenceKey]: null
219
+ });
220
+ }
221
+ }, [isWindowFocused, updateMyPresence, presenceKey]);
222
+ useImperativeHandle(
223
+ forwardedRef,
224
+ () => ref.current,
225
+ []
226
+ );
227
+ return /* @__PURE__ */ jsxs(
228
+ "div",
229
+ {
230
+ className: cn("lb-root lb-cursors", className),
231
+ ...props,
232
+ onPointerMove: handlePointerMove,
233
+ onPointerLeave: handlePointerLeave,
234
+ ref,
235
+ children: [
236
+ /* @__PURE__ */ jsx("div", { className: "lb-cursors-container", children: othersConnectionIds.map((connectionId) => /* @__PURE__ */ jsx(
237
+ PresenceCursor,
238
+ {
239
+ connectionId,
240
+ presenceKey,
241
+ sizeRef,
242
+ sizeEvents
243
+ },
244
+ connectionId
245
+ )) }),
246
+ children
247
+ ]
248
+ }
249
+ );
250
+ }
251
+ );
252
+
253
+ export { Cursors };
254
+ //# sourceMappingURL=Cursors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Cursors.js","sources":["../../src/components/Cursors.tsx"],"sourcesContent":["import type { EventSource } from \"@liveblocks/core\";\nimport { isPlainObject, makeEventSource } from \"@liveblocks/core\";\nimport {\n useOther,\n useOthersConnectionIds,\n useRoom,\n useUpdateMyPresence,\n useUser,\n} from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n MutableRefObject,\n PointerEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\n\nimport { type Animatable, makeAnimationLoop } from \"../utils/animation-loop\";\nimport { cn } from \"../utils/cn\";\nimport { useWindowFocus } from \"../utils/use-window-focus\";\nimport { Cursor } from \"./Cursor\";\n\nconst STIFFNESS = 320;\nconst DAMPING = 32;\nconst EPSILON = 0.01;\n\nconst DEFAULT_PRESENCE_KEY = \"cursor\";\n\nexport interface CursorsProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The key used to store the cursors in users' Presence.\n * This can be used to have multiple `Cursors` in a single room.\n */\n presenceKey?: string;\n}\n\ntype Coordinates = {\n x: number;\n y: number;\n};\n\ntype Size = {\n width: number;\n height: number;\n};\n\nfunction $string(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction $coordinates(value: unknown): Coordinates | undefined {\n if (\n isPlainObject(value) &&\n typeof value.x === \"number\" &&\n typeof value.y === \"number\"\n ) {\n return value as Coordinates;\n }\n\n return undefined;\n}\n\n// Use a shared animation loop for all (active) springs.\nconst loop = makeAnimationLoop();\n\nfunction makeCoordinatesSpring() {\n const updates = makeEventSource<Coordinates | null>();\n let value: Coordinates | null = null;\n let target: Coordinates | null = null;\n const velocity = { x: 0, y: 0 };\n\n const spring: Animatable = {\n active: false,\n step(dt: number) {\n if (value === null || target === null) {\n spring.active = false;\n return;\n }\n\n const k = STIFFNESS;\n const d = DAMPING;\n const dx = value.x - target.x;\n const dy = value.y - target.y;\n\n velocity.x += (-k * dx - d * velocity.x) * dt;\n velocity.y += (-k * dy - d * velocity.y) * dt;\n\n const nx = value.x + velocity.x * dt;\n const ny = value.y + velocity.y * dt;\n\n if (nx !== value.x || ny !== value.y) {\n value.x = nx;\n value.y = ny;\n updates.notify(value);\n }\n\n if (\n Math.abs(velocity.x) < EPSILON &&\n Math.abs(velocity.y) < EPSILON &&\n Math.abs(target.x - value.x) < EPSILON &&\n Math.abs(target.y - value.y) < EPSILON\n ) {\n if (value.x !== target.x || value.y !== target.y) {\n value.x = target.x;\n value.y = target.y;\n updates.notify(value);\n }\n\n velocity.x = 0;\n velocity.y = 0;\n spring.active = false;\n }\n },\n };\n\n return {\n get() {\n return value;\n },\n set(point: Coordinates | null) {\n if (point === null) {\n value = null;\n target = null;\n velocity.x = 0;\n velocity.y = 0;\n spring.active = false;\n loop.remove(spring);\n updates.notify(null);\n return;\n }\n\n if (value === null) {\n value = { x: point.x, y: point.y };\n target = point;\n velocity.x = 0;\n velocity.y = 0;\n updates.notify(value);\n return;\n }\n\n target = point;\n\n if (!spring.active && (value.x !== target.x || value.y !== target.y)) {\n spring.active = true;\n loop.add(spring);\n }\n },\n subscribe: updates.subscribe,\n dispose() {\n spring.active = false;\n loop.remove(spring);\n updates.dispose();\n },\n };\n}\n\nfunction PresenceCursor({\n connectionId,\n presenceKey,\n sizeRef,\n sizeEvents,\n}: {\n connectionId: number;\n presenceKey: string;\n sizeRef: MutableRefObject<Size | null>;\n sizeEvents: EventSource<void>;\n}) {\n const room = useRoom();\n const cursorRef = useRef<HTMLDivElement>(null);\n const userId = useOther(connectionId, (other) => $string(other.id));\n const { user, isLoading } = useUser(userId ?? \"\");\n const hasUserInfo = userId !== undefined && !isLoading;\n const color = $string(user?.color);\n const name = $string(user?.name);\n\n useLayoutEffect(() => {\n const spring = makeCoordinatesSpring();\n\n function update() {\n const element = cursorRef.current;\n const coordinates = spring.get();\n\n if (!element) {\n return;\n }\n\n if (!hasUserInfo || coordinates === null) {\n element.style.transform = \"translate3d(0, 0, 0)\";\n element.style.display = \"none\";\n return;\n }\n\n if (sizeRef.current) {\n element.style.transform = `translate3d(${coordinates.x * sizeRef.current.width}px, ${coordinates.y * sizeRef.current.height}px, 0)`;\n }\n\n element.style.display = \"\";\n }\n\n const unsubscribeSpring = spring.subscribe(update);\n const unsubscribeSize = sizeEvents.subscribe(update);\n update();\n\n const unsubscribeOther = room.events.others.subscribe(({ others }) => {\n const other = others.find((other) => other.connectionId === connectionId);\n const cursor = $coordinates(other?.presence[presenceKey]);\n\n spring.set(cursor ?? null);\n });\n\n return () => {\n spring.dispose();\n unsubscribeSpring();\n unsubscribeSize();\n unsubscribeOther();\n };\n }, [room, connectionId, presenceKey, sizeRef, sizeEvents, hasUserInfo]);\n\n return (\n <Cursor\n color={color}\n label={name}\n ref={cursorRef}\n style={{ display: \"none\" }}\n />\n );\n}\n\n/**\n * Displays multiplayer cursors.\n */\nexport const Cursors = forwardRef<HTMLDivElement, CursorsProps>(\n (\n { className, children, presenceKey = DEFAULT_PRESENCE_KEY, ...props },\n forwardedRef\n ) => {\n const ref = useRef<HTMLDivElement>(null);\n const updateMyPresence = useUpdateMyPresence();\n const othersConnectionIds = useOthersConnectionIds();\n const sizeRef = useRef<Size | null>(null);\n const [sizeEvents] = useState(() => makeEventSource<void>());\n const isWindowFocused = useWindowFocus();\n\n useEffect(() => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n function setSize(size: Size) {\n sizeRef.current = size;\n sizeEvents.notify();\n }\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n if (entry.target === element) {\n setSize({\n width: entry.contentRect.width,\n height: entry.contentRect.height,\n });\n }\n }\n });\n\n setSize({\n width: element.clientWidth,\n height: element.clientHeight,\n });\n\n observer.observe(element);\n\n return () => {\n observer.disconnect();\n };\n }, [sizeEvents]);\n\n const handlePointerMove = useCallback(\n (event: PointerEvent) => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n const bounds = element.getBoundingClientRect();\n\n if (bounds.width === 0 || bounds.height === 0) {\n return;\n }\n\n updateMyPresence({\n [presenceKey]: {\n x: (event.clientX - bounds.left) / bounds.width,\n y: (event.clientY - bounds.top) / bounds.height,\n },\n });\n },\n [updateMyPresence, presenceKey]\n );\n\n const handlePointerLeave = useCallback(() => {\n updateMyPresence({\n [presenceKey]: null,\n });\n }, [updateMyPresence, presenceKey]);\n\n useEffect(() => {\n if (!isWindowFocused) {\n updateMyPresence({\n [presenceKey]: null,\n });\n }\n }, [isWindowFocused, updateMyPresence, presenceKey]);\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => ref.current,\n []\n );\n\n return (\n <div\n className={cn(\"lb-root lb-cursors\", className)}\n {...props}\n onPointerMove={handlePointerMove}\n onPointerLeave={handlePointerLeave}\n ref={ref}\n >\n <div className=\"lb-cursors-container\">\n {othersConnectionIds.map((connectionId) => (\n <PresenceCursor\n key={connectionId}\n connectionId={connectionId}\n presenceKey={presenceKey}\n sizeRef={sizeRef}\n sizeEvents={sizeEvents}\n />\n ))}\n </div>\n\n {children}\n </div>\n );\n }\n);\n"],"names":["other"],"mappings":";;;;;;;;;;AA6BA,MAAM,SAAY,GAAA,GAAA,CAAA;AAClB,MAAM,OAAU,GAAA,EAAA,CAAA;AAChB,MAAM,OAAU,GAAA,IAAA,CAAA;AAEhB,MAAM,oBAAuB,GAAA,QAAA,CAAA;AAoB7B,SAAS,QAAQ,KAAoC,EAAA;AACnD,EAAO,OAAA,OAAO,KAAU,KAAA,QAAA,GAAW,KAAQ,GAAA,KAAA,CAAA,CAAA;AAC7C,CAAA;AAEA,SAAS,aAAa,KAAyC,EAAA;AAC7D,EACE,IAAA,aAAA,CAAc,KAAK,CAAA,IACnB,OAAO,KAAA,CAAM,MAAM,QACnB,IAAA,OAAO,KAAM,CAAA,CAAA,KAAM,QACnB,EAAA;AACA,IAAO,OAAA,KAAA,CAAA;AAAA,GACT;AAEA,EAAO,OAAA,KAAA,CAAA,CAAA;AACT,CAAA;AAGA,MAAM,OAAO,iBAAkB,EAAA,CAAA;AAE/B,SAAS,qBAAwB,GAAA;AAC/B,EAAA,MAAM,UAAU,eAAoC,EAAA,CAAA;AACpD,EAAA,IAAI,KAA4B,GAAA,IAAA,CAAA;AAChC,EAAA,IAAI,MAA6B,GAAA,IAAA,CAAA;AACjC,EAAA,MAAM,QAAW,GAAA,EAAE,CAAG,EAAA,CAAA,EAAG,GAAG,CAAE,EAAA,CAAA;AAE9B,EAAA,MAAM,MAAqB,GAAA;AAAA,IACzB,MAAQ,EAAA,KAAA;AAAA,IACR,KAAK,EAAY,EAAA;AACf,MAAI,IAAA,KAAA,KAAU,IAAQ,IAAA,MAAA,KAAW,IAAM,EAAA;AACrC,QAAA,MAAA,CAAO,MAAS,GAAA,KAAA,CAAA;AAChB,QAAA,OAAA;AAAA,OACF;AAEA,MAAA,MAAM,CAAI,GAAA,SAAA,CAAA;AACV,MAAA,MAAM,CAAI,GAAA,OAAA,CAAA;AACV,MAAM,MAAA,EAAA,GAAK,KAAM,CAAA,CAAA,GAAI,MAAO,CAAA,CAAA,CAAA;AAC5B,MAAM,MAAA,EAAA,GAAK,KAAM,CAAA,CAAA,GAAI,MAAO,CAAA,CAAA,CAAA;AAE5B,MAAA,QAAA,CAAS,MAAM,CAAC,CAAA,GAAI,EAAK,GAAA,CAAA,GAAI,SAAS,CAAK,IAAA,EAAA,CAAA;AAC3C,MAAA,QAAA,CAAS,MAAM,CAAC,CAAA,GAAI,EAAK,GAAA,CAAA,GAAI,SAAS,CAAK,IAAA,EAAA,CAAA;AAE3C,MAAA,MAAM,EAAK,GAAA,KAAA,CAAM,CAAI,GAAA,QAAA,CAAS,CAAI,GAAA,EAAA,CAAA;AAClC,MAAA,MAAM,EAAK,GAAA,KAAA,CAAM,CAAI,GAAA,QAAA,CAAS,CAAI,GAAA,EAAA,CAAA;AAElC,MAAA,IAAI,EAAO,KAAA,KAAA,CAAM,CAAK,IAAA,EAAA,KAAO,MAAM,CAAG,EAAA;AACpC,QAAA,KAAA,CAAM,CAAI,GAAA,EAAA,CAAA;AACV,QAAA,KAAA,CAAM,CAAI,GAAA,EAAA,CAAA;AACV,QAAA,OAAA,CAAQ,OAAO,KAAK,CAAA,CAAA;AAAA,OACtB;AAEA,MACE,IAAA,IAAA,CAAK,GAAI,CAAA,QAAA,CAAS,CAAC,CAAA,GAAI,OACvB,IAAA,IAAA,CAAK,GAAI,CAAA,QAAA,CAAS,CAAC,CAAA,GAAI,OACvB,IAAA,IAAA,CAAK,GAAI,CAAA,MAAA,CAAO,CAAI,GAAA,KAAA,CAAM,CAAC,CAAA,GAAI,OAC/B,IAAA,IAAA,CAAK,GAAI,CAAA,MAAA,CAAO,CAAI,GAAA,KAAA,CAAM,CAAC,CAAA,GAAI,OAC/B,EAAA;AACA,QAAA,IAAI,MAAM,CAAM,KAAA,MAAA,CAAO,KAAK,KAAM,CAAA,CAAA,KAAM,OAAO,CAAG,EAAA;AAChD,UAAA,KAAA,CAAM,IAAI,MAAO,CAAA,CAAA,CAAA;AACjB,UAAA,KAAA,CAAM,IAAI,MAAO,CAAA,CAAA,CAAA;AACjB,UAAA,OAAA,CAAQ,OAAO,KAAK,CAAA,CAAA;AAAA,SACtB;AAEA,QAAA,QAAA,CAAS,CAAI,GAAA,CAAA,CAAA;AACb,QAAA,QAAA,CAAS,CAAI,GAAA,CAAA,CAAA;AACb,QAAA,MAAA,CAAO,MAAS,GAAA,KAAA,CAAA;AAAA,OAClB;AAAA,KACF;AAAA,GACF,CAAA;AAEA,EAAO,OAAA;AAAA,IACL,GAAM,GAAA;AACJ,MAAO,OAAA,KAAA,CAAA;AAAA,KACT;AAAA,IACA,IAAI,KAA2B,EAAA;AAC7B,MAAA,IAAI,UAAU,IAAM,EAAA;AAClB,QAAQ,KAAA,GAAA,IAAA,CAAA;AACR,QAAS,MAAA,GAAA,IAAA,CAAA;AACT,QAAA,QAAA,CAAS,CAAI,GAAA,CAAA,CAAA;AACb,QAAA,QAAA,CAAS,CAAI,GAAA,CAAA,CAAA;AACb,QAAA,MAAA,CAAO,MAAS,GAAA,KAAA,CAAA;AAChB,QAAA,IAAA,CAAK,OAAO,MAAM,CAAA,CAAA;AAClB,QAAA,OAAA,CAAQ,OAAO,IAAI,CAAA,CAAA;AACnB,QAAA,OAAA;AAAA,OACF;AAEA,MAAA,IAAI,UAAU,IAAM,EAAA;AAClB,QAAA,KAAA,GAAQ,EAAE,CAAG,EAAA,KAAA,CAAM,CAAG,EAAA,CAAA,EAAG,MAAM,CAAE,EAAA,CAAA;AACjC,QAAS,MAAA,GAAA,KAAA,CAAA;AACT,QAAA,QAAA,CAAS,CAAI,GAAA,CAAA,CAAA;AACb,QAAA,QAAA,CAAS,CAAI,GAAA,CAAA,CAAA;AACb,QAAA,OAAA,CAAQ,OAAO,KAAK,CAAA,CAAA;AACpB,QAAA,OAAA;AAAA,OACF;AAEA,MAAS,MAAA,GAAA,KAAA,CAAA;AAET,MAAI,IAAA,CAAC,MAAO,CAAA,MAAA,KAAW,KAAM,CAAA,CAAA,KAAM,OAAO,CAAK,IAAA,KAAA,CAAM,CAAM,KAAA,MAAA,CAAO,CAAI,CAAA,EAAA;AACpE,QAAA,MAAA,CAAO,MAAS,GAAA,IAAA,CAAA;AAChB,QAAA,IAAA,CAAK,IAAI,MAAM,CAAA,CAAA;AAAA,OACjB;AAAA,KACF;AAAA,IACA,WAAW,OAAQ,CAAA,SAAA;AAAA,IACnB,OAAU,GAAA;AACR,MAAA,MAAA,CAAO,MAAS,GAAA,KAAA,CAAA;AAChB,MAAA,IAAA,CAAK,OAAO,MAAM,CAAA,CAAA;AAClB,MAAA,OAAA,CAAQ,OAAQ,EAAA,CAAA;AAAA,KAClB;AAAA,GACF,CAAA;AACF,CAAA;AAEA,SAAS,cAAe,CAAA;AAAA,EACtB,YAAA;AAAA,EACA,WAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AACF,CAKG,EAAA;AACD,EAAA,MAAM,OAAO,OAAQ,EAAA,CAAA;AACrB,EAAM,MAAA,SAAA,GAAY,OAAuB,IAAI,CAAA,CAAA;AAC7C,EAAM,MAAA,MAAA,GAAS,SAAS,YAAc,EAAA,CAAC,UAAU,OAAQ,CAAA,KAAA,CAAM,EAAE,CAAC,CAAA,CAAA;AAClE,EAAA,MAAM,EAAE,IAAM,EAAA,SAAA,EAAc,GAAA,OAAA,CAAQ,UAAU,EAAE,CAAA,CAAA;AAChD,EAAM,MAAA,WAAA,GAAc,MAAW,KAAA,KAAA,CAAA,IAAa,CAAC,SAAA,CAAA;AAC7C,EAAM,MAAA,KAAA,GAAQ,OAAQ,CAAA,IAAA,EAAM,KAAK,CAAA,CAAA;AACjC,EAAM,MAAA,IAAA,GAAO,OAAQ,CAAA,IAAA,EAAM,IAAI,CAAA,CAAA;AAE/B,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,MAAM,SAAS,qBAAsB,EAAA,CAAA;AAErC,IAAA,SAAS,MAAS,GAAA;AAChB,MAAA,MAAM,UAAU,SAAU,CAAA,OAAA,CAAA;AAC1B,MAAM,MAAA,WAAA,GAAc,OAAO,GAAI,EAAA,CAAA;AAE/B,MAAA,IAAI,CAAC,OAAS,EAAA;AACZ,QAAA,OAAA;AAAA,OACF;AAEA,MAAI,IAAA,CAAC,WAAe,IAAA,WAAA,KAAgB,IAAM,EAAA;AACxC,QAAA,OAAA,CAAQ,MAAM,SAAY,GAAA,sBAAA,CAAA;AAC1B,QAAA,OAAA,CAAQ,MAAM,OAAU,GAAA,MAAA,CAAA;AACxB,QAAA,OAAA;AAAA,OACF;AAEA,MAAA,IAAI,QAAQ,OAAS,EAAA;AACnB,QAAA,OAAA,CAAQ,KAAM,CAAA,SAAA,GAAY,CAAe,YAAA,EAAA,WAAA,CAAY,CAAI,GAAA,OAAA,CAAQ,OAAQ,CAAA,KAAK,CAAO,IAAA,EAAA,WAAA,CAAY,CAAI,GAAA,OAAA,CAAQ,QAAQ,MAAM,CAAA,MAAA,CAAA,CAAA;AAAA,OAC7H;AAEA,MAAA,OAAA,CAAQ,MAAM,OAAU,GAAA,EAAA,CAAA;AAAA,KAC1B;AAEA,IAAM,MAAA,iBAAA,GAAoB,MAAO,CAAA,SAAA,CAAU,MAAM,CAAA,CAAA;AACjD,IAAM,MAAA,eAAA,GAAkB,UAAW,CAAA,SAAA,CAAU,MAAM,CAAA,CAAA;AACnD,IAAO,MAAA,EAAA,CAAA;AAEP,IAAM,MAAA,gBAAA,GAAmB,KAAK,MAAO,CAAA,MAAA,CAAO,UAAU,CAAC,EAAE,QAAa,KAAA;AACpE,MAAA,MAAM,QAAQ,MAAO,CAAA,IAAA,CAAK,CAACA,MAAUA,KAAAA,MAAAA,CAAM,iBAAiB,YAAY,CAAA,CAAA;AACxE,MAAA,MAAM,MAAS,GAAA,YAAA,CAAa,KAAO,EAAA,QAAA,CAAS,WAAW,CAAC,CAAA,CAAA;AAExD,MAAO,MAAA,CAAA,GAAA,CAAI,UAAU,IAAI,CAAA,CAAA;AAAA,KAC1B,CAAA,CAAA;AAED,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,OAAQ,EAAA,CAAA;AACf,MAAkB,iBAAA,EAAA,CAAA;AAClB,MAAgB,eAAA,EAAA,CAAA;AAChB,MAAiB,gBAAA,EAAA,CAAA;AAAA,KACnB,CAAA;AAAA,GACF,EAAG,CAAC,IAAM,EAAA,YAAA,EAAc,aAAa,OAAS,EAAA,UAAA,EAAY,WAAW,CAAC,CAAA,CAAA;AAEtE,EACE,uBAAA,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,KAAA;AAAA,MACA,KAAO,EAAA,IAAA;AAAA,MACP,GAAK,EAAA,SAAA;AAAA,MACL,KAAA,EAAO,EAAE,OAAA,EAAS,MAAO,EAAA;AAAA,KAAA;AAAA,GAC3B,CAAA;AAEJ,CAAA;AAKO,MAAM,OAAU,GAAA,UAAA;AAAA,EACrB,CACE,EAAE,SAAW,EAAA,QAAA,EAAU,cAAc,oBAAsB,EAAA,GAAG,KAAM,EAAA,EACpE,YACG,KAAA;AACH,IAAM,MAAA,GAAA,GAAM,OAAuB,IAAI,CAAA,CAAA;AACvC,IAAA,MAAM,mBAAmB,mBAAoB,EAAA,CAAA;AAC7C,IAAA,MAAM,sBAAsB,sBAAuB,EAAA,CAAA;AACnD,IAAM,MAAA,OAAA,GAAU,OAAoB,IAAI,CAAA,CAAA;AACxC,IAAA,MAAM,CAAC,UAAU,CAAA,GAAI,QAAS,CAAA,MAAM,iBAAuB,CAAA,CAAA;AAC3D,IAAA,MAAM,kBAAkB,cAAe,EAAA,CAAA;AAEvC,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,MAAM,UAAU,GAAI,CAAA,OAAA,CAAA;AAEpB,MAAA,IAAI,CAAC,OAAS,EAAA;AACZ,QAAA,OAAA;AAAA,OACF;AAEA,MAAA,SAAS,QAAQ,IAAY,EAAA;AAC3B,QAAA,OAAA,CAAQ,OAAU,GAAA,IAAA,CAAA;AAClB,QAAA,UAAA,CAAW,MAAO,EAAA,CAAA;AAAA,OACpB;AAEA,MAAA,MAAM,QAAW,GAAA,IAAI,cAAe,CAAA,CAAC,OAAY,KAAA;AAC/C,QAAA,KAAA,MAAW,SAAS,OAAS,EAAA;AAC3B,UAAI,IAAA,KAAA,CAAM,WAAW,OAAS,EAAA;AAC5B,YAAQ,OAAA,CAAA;AAAA,cACN,KAAA,EAAO,MAAM,WAAY,CAAA,KAAA;AAAA,cACzB,MAAA,EAAQ,MAAM,WAAY,CAAA,MAAA;AAAA,aAC3B,CAAA,CAAA;AAAA,WACH;AAAA,SACF;AAAA,OACD,CAAA,CAAA;AAED,MAAQ,OAAA,CAAA;AAAA,QACN,OAAO,OAAQ,CAAA,WAAA;AAAA,QACf,QAAQ,OAAQ,CAAA,YAAA;AAAA,OACjB,CAAA,CAAA;AAED,MAAA,QAAA,CAAS,QAAQ,OAAO,CAAA,CAAA;AAExB,MAAA,OAAO,MAAM;AACX,QAAA,QAAA,CAAS,UAAW,EAAA,CAAA;AAAA,OACtB,CAAA;AAAA,KACF,EAAG,CAAC,UAAU,CAAC,CAAA,CAAA;AAEf,IAAA,MAAM,iBAAoB,GAAA,WAAA;AAAA,MACxB,CAAC,KAAwB,KAAA;AACvB,QAAA,MAAM,UAAU,GAAI,CAAA,OAAA,CAAA;AAEpB,QAAA,IAAI,CAAC,OAAS,EAAA;AACZ,UAAA,OAAA;AAAA,SACF;AAEA,QAAM,MAAA,MAAA,GAAS,QAAQ,qBAAsB,EAAA,CAAA;AAE7C,QAAA,IAAI,MAAO,CAAA,KAAA,KAAU,CAAK,IAAA,MAAA,CAAO,WAAW,CAAG,EAAA;AAC7C,UAAA,OAAA;AAAA,SACF;AAEA,QAAiB,gBAAA,CAAA;AAAA,UACf,CAAC,WAAW,GAAG;AAAA,YACb,CAAI,EAAA,CAAA,KAAA,CAAM,OAAU,GAAA,MAAA,CAAO,QAAQ,MAAO,CAAA,KAAA;AAAA,YAC1C,CAAI,EAAA,CAAA,KAAA,CAAM,OAAU,GAAA,MAAA,CAAO,OAAO,MAAO,CAAA,MAAA;AAAA,WAC3C;AAAA,SACD,CAAA,CAAA;AAAA,OACH;AAAA,MACA,CAAC,kBAAkB,WAAW,CAAA;AAAA,KAChC,CAAA;AAEA,IAAM,MAAA,kBAAA,GAAqB,YAAY,MAAM;AAC3C,MAAiB,gBAAA,CAAA;AAAA,QACf,CAAC,WAAW,GAAG,IAAA;AAAA,OAChB,CAAA,CAAA;AAAA,KACA,EAAA,CAAC,gBAAkB,EAAA,WAAW,CAAC,CAAA,CAAA;AAElC,IAAA,SAAA,CAAU,MAAM;AACd,MAAA,IAAI,CAAC,eAAiB,EAAA;AACpB,QAAiB,gBAAA,CAAA;AAAA,UACf,CAAC,WAAW,GAAG,IAAA;AAAA,SAChB,CAAA,CAAA;AAAA,OACH;AAAA,KACC,EAAA,CAAC,eAAiB,EAAA,gBAAA,EAAkB,WAAW,CAAC,CAAA,CAAA;AAEnD,IAAA,mBAAA;AAAA,MACE,YAAA;AAAA,MACA,MAAM,GAAI,CAAA,OAAA;AAAA,MACV,EAAC;AAAA,KACH,CAAA;AAEA,IACE,uBAAA,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAW,EAAG,CAAA,oBAAA,EAAsB,SAAS,CAAA;AAAA,QAC5C,GAAG,KAAA;AAAA,QACJ,aAAe,EAAA,iBAAA;AAAA,QACf,cAAgB,EAAA,kBAAA;AAAA,QAChB,GAAA;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,SAAI,SAAU,EAAA,sBAAA,EACZ,QAAoB,EAAA,mBAAA,CAAA,GAAA,CAAI,CAAC,YACxB,qBAAA,GAAA;AAAA,YAAC,cAAA;AAAA,YAAA;AAAA,cAEC,YAAA;AAAA,cACA,WAAA;AAAA,cACA,OAAA;AAAA,cACA,UAAA;AAAA,aAAA;AAAA,YAJK,YAAA;AAAA,WAMR,CACH,EAAA,CAAA;AAAA,UAEC,QAAA;AAAA,SAAA;AAAA,OAAA;AAAA,KACH,CAAA;AAAA,GAEJ;AACF;;;;"}
@@ -0,0 +1,97 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var radixUi = require('radix-ui');
6
+ var react = require('react');
7
+ var config = require('../config.cjs');
8
+ var constants = require('../constants.cjs');
9
+ var overrides = require('../overrides.cjs');
10
+ var cn = require('../utils/cn.cjs');
11
+ var useControllableState = require('../utils/use-controllable-state.cjs');
12
+ var Composer = require('./Composer.cjs');
13
+
14
+
15
+ const FloatingComposer = react.forwardRef(
16
+ ({
17
+ children,
18
+ defaultOpen,
19
+ open,
20
+ onOpenChange,
21
+ side = "right",
22
+ sideOffset = constants.FLOATING_ELEMENT_SIDE_OFFSET,
23
+ align = "start",
24
+ alignOffset,
25
+ autoFocus = true,
26
+ overrides: overrides$1,
27
+ className,
28
+ ...props
29
+ }, forwardedRef) => {
30
+ const ref = react.useRef(null);
31
+ const $ = overrides.useOverrides(overrides$1);
32
+ const { portalContainer } = config.useLiveblocksUiConfig();
33
+ const [isOpen, setIsOpen] = useControllableState.useControllableState(
34
+ defaultOpen ?? false,
35
+ open,
36
+ onOpenChange
37
+ );
38
+ react.useImperativeHandle(
39
+ forwardedRef,
40
+ () => ref.current,
41
+ []
42
+ );
43
+ return /* @__PURE__ */ jsxRuntime.jsxs(radixUi.Popover.Root, { open: isOpen, onOpenChange: setIsOpen, children: [
44
+ /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Trigger, { asChild: true, children }),
45
+ /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Portal, { container: portalContainer, children: /* @__PURE__ */ jsxRuntime.jsx(
46
+ radixUi.Popover.Content,
47
+ {
48
+ className: cn.cn(
49
+ "lb-root lb-portal lb-elevation lb-floating-composer",
50
+ className
51
+ ),
52
+ dir: $.dir,
53
+ side,
54
+ sideOffset,
55
+ updatePositionStrategy: "always",
56
+ align,
57
+ alignOffset,
58
+ collisionPadding: constants.FLOATING_ELEMENT_COLLISION_PADDING,
59
+ onEscapeKeyDown: (event) => {
60
+ if (event.defaultPrevented) {
61
+ return;
62
+ }
63
+ setIsOpen(false);
64
+ event.preventDefault();
65
+ },
66
+ onInteractOutside: (event) => {
67
+ const target = event.target;
68
+ if (target.closest(".lb-portal")) {
69
+ event.preventDefault();
70
+ }
71
+ },
72
+ onOpenAutoFocus: (event) => {
73
+ if (!autoFocus) {
74
+ event.preventDefault();
75
+ ref.current?.focus();
76
+ }
77
+ },
78
+ asChild: true,
79
+ children: /* @__PURE__ */ jsxRuntime.jsx(
80
+ Composer.Composer,
81
+ {
82
+ ref,
83
+ overrides: overrides$1,
84
+ autoFocus,
85
+ collapsed: false,
86
+ tabIndex: -1,
87
+ ...props
88
+ }
89
+ )
90
+ }
91
+ ) })
92
+ ] });
93
+ }
94
+ );
95
+
96
+ exports.FloatingComposer = FloatingComposer;
97
+ //# sourceMappingURL=FloatingComposer.cjs.map