@liveblocks/react-flow 3.16.0-flow1 → 3.16.0-flow2

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/README.md CHANGED
@@ -4,3 +4,54 @@
4
4
  </p>
5
5
 
6
6
  # `@liveblocks/react-flow`
7
+
8
+ <p>
9
+ <a href="https://npmjs.org/package/@liveblocks/react-flow"><img src="https://img.shields.io/npm/v/@liveblocks/react-flow?style=flat&label=npm&color=c33" alt="NPM" /></a>
10
+ <a href="https://bundlephobia.com/package/@liveblocks/react-flow"><img src="https://img.shields.io/bundlephobia/minzip/@liveblocks/react-flow?style=flat&label=size&color=09f" alt="Size" /></a>
11
+ <a href="https://github.com/liveblocks/liveblocks/blob/main/licenses/LICENSE-APACHE-2.0"><img src="https://img.shields.io/badge/license-Apache--2.0-green" alt="License" /></a>
12
+ </p>
13
+
14
+ `@liveblocks/react-flow` provides [React](https://reactjs.org/) APIs to
15
+ integrate [React Flow](https://reactflow.dev/) diagrams with Liveblocks—a
16
+ platform to build, host, and scale collaborative applications with zero
17
+ configuration, no maintenance required.
18
+
19
+ ## Installation
20
+
21
+ ```
22
+ npm install @liveblocks/client @liveblocks/react @liveblocks/react-ui @liveblocks/react-flow
23
+ ```
24
+
25
+ ## Documentation
26
+
27
+ Read the
28
+ [documentation](https://liveblocks.io/docs/api-reference/liveblocks-react-flow)
29
+ for guides and API references.
30
+
31
+ ## Examples
32
+
33
+ Explore our [collaborative examples](https://liveblocks.io/examples) to help you
34
+ get started.
35
+
36
+ > All examples are open-source and live in this repository, within
37
+ > [`/examples`](../../examples).
38
+
39
+ ## Releases
40
+
41
+ See the [latest changes](https://github.com/liveblocks/liveblocks/releases) or
42
+ learn more about
43
+ [upcoming releases](https://github.com/liveblocks/liveblocks/milestones).
44
+
45
+ ## Community
46
+
47
+ - [Discord](https://liveblocks.io/discord) - To get involved with the Liveblocks
48
+ community, ask questions and share tips.
49
+ - [X](https://x.com/liveblocks) - To receive updates, announcements, blog posts,
50
+ and general Liveblocks tips.
51
+
52
+ ## License
53
+
54
+ Licensed under the Apache License 2.0, Copyright © 2021-present
55
+ [Liveblocks](https://liveblocks.io).
56
+
57
+ See [LICENSE](../../licenses/LICENSE-APACHE-2.0) for more information.
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ const DEFAULT_STORAGE_KEY = "flow";
4
+ const NODE_BASE_CONFIG = {
5
+ // Local-only (not synced)
6
+ selected: false,
7
+ dragging: false,
8
+ measured: false,
9
+ resizing: false,
10
+ // Atomic (synced as plain Json)
11
+ position: "atomic",
12
+ sourcePosition: "atomic",
13
+ targetPosition: "atomic",
14
+ extent: "atomic",
15
+ origin: "atomic",
16
+ handles: "atomic"
17
+ // Note: the `data` key is intentionally left out of this base config, as it
18
+ // is expected to be provided by the end user
19
+ };
20
+ const EDGE_BASE_CONFIG = {
21
+ // Local-only (not synced)
22
+ selected: false,
23
+ // Atomic (synced as plain Json)
24
+ markerStart: "atomic",
25
+ markerEnd: "atomic"
26
+ // Note: the `data` key is intentionally left out of this base config, as it
27
+ // is expected to be provided by the end user
28
+ };
29
+
30
+ exports.DEFAULT_STORAGE_KEY = DEFAULT_STORAGE_KEY;
31
+ exports.EDGE_BASE_CONFIG = EDGE_BASE_CONFIG;
32
+ exports.NODE_BASE_CONFIG = NODE_BASE_CONFIG;
33
+ //# sourceMappingURL=constants.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.cjs","sources":["../src/constants.ts"],"sourcesContent":["import type { SyncConfig } from \"@liveblocks/core\";\n\nexport const DEFAULT_STORAGE_KEY = \"flow\";\n\nexport const NODE_BASE_CONFIG = {\n // Local-only (not synced)\n selected: false,\n dragging: false,\n measured: false,\n resizing: false,\n\n // Atomic (synced as plain Json)\n position: \"atomic\",\n sourcePosition: \"atomic\",\n targetPosition: \"atomic\",\n extent: \"atomic\",\n origin: \"atomic\",\n handles: \"atomic\",\n\n // Note: the `data` key is intentionally left out of this base config, as it\n // is expected to be provided by the end user\n} as const satisfies SyncConfig;\n\nexport const EDGE_BASE_CONFIG = {\n // Local-only (not synced)\n selected: false,\n\n // Atomic (synced as plain Json)\n markerStart: \"atomic\",\n markerEnd: \"atomic\",\n\n // Note: the `data` key is intentionally left out of this base config, as it\n // is expected to be provided by the end user\n} as const satisfies SyncConfig;\n"],"names":[],"mappings":";;AAEO,MAAM,mBAAsB,GAAA,OAAA;AAE5B,MAAM,gBAAmB,GAAA;AAAA;AAAA,EAE9B,QAAU,EAAA,KAAA;AAAA,EACV,QAAU,EAAA,KAAA;AAAA,EACV,QAAU,EAAA,KAAA;AAAA,EACV,QAAU,EAAA,KAAA;AAAA;AAAA,EAGV,QAAU,EAAA,QAAA;AAAA,EACV,cAAgB,EAAA,QAAA;AAAA,EAChB,cAAgB,EAAA,QAAA;AAAA,EAChB,MAAQ,EAAA,QAAA;AAAA,EACR,MAAQ,EAAA,QAAA;AAAA,EACR,OAAS,EAAA,QAAA;AAAA;AAAA;AAIX,EAAA;AAEO,MAAM,gBAAmB,GAAA;AAAA;AAAA,EAE9B,QAAU,EAAA,KAAA;AAAA;AAAA,EAGV,WAAa,EAAA,QAAA;AAAA,EACb,SAAW,EAAA,QAAA;AAAA;AAAA;AAIb;;;;;;"}
@@ -0,0 +1,29 @@
1
+ const DEFAULT_STORAGE_KEY = "flow";
2
+ const NODE_BASE_CONFIG = {
3
+ // Local-only (not synced)
4
+ selected: false,
5
+ dragging: false,
6
+ measured: false,
7
+ resizing: false,
8
+ // Atomic (synced as plain Json)
9
+ position: "atomic",
10
+ sourcePosition: "atomic",
11
+ targetPosition: "atomic",
12
+ extent: "atomic",
13
+ origin: "atomic",
14
+ handles: "atomic"
15
+ // Note: the `data` key is intentionally left out of this base config, as it
16
+ // is expected to be provided by the end user
17
+ };
18
+ const EDGE_BASE_CONFIG = {
19
+ // Local-only (not synced)
20
+ selected: false,
21
+ // Atomic (synced as plain Json)
22
+ markerStart: "atomic",
23
+ markerEnd: "atomic"
24
+ // Note: the `data` key is intentionally left out of this base config, as it
25
+ // is expected to be provided by the end user
26
+ };
27
+
28
+ export { DEFAULT_STORAGE_KEY, EDGE_BASE_CONFIG, NODE_BASE_CONFIG };
29
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sources":["../src/constants.ts"],"sourcesContent":["import type { SyncConfig } from \"@liveblocks/core\";\n\nexport const DEFAULT_STORAGE_KEY = \"flow\";\n\nexport const NODE_BASE_CONFIG = {\n // Local-only (not synced)\n selected: false,\n dragging: false,\n measured: false,\n resizing: false,\n\n // Atomic (synced as plain Json)\n position: \"atomic\",\n sourcePosition: \"atomic\",\n targetPosition: \"atomic\",\n extent: \"atomic\",\n origin: \"atomic\",\n handles: \"atomic\",\n\n // Note: the `data` key is intentionally left out of this base config, as it\n // is expected to be provided by the end user\n} as const satisfies SyncConfig;\n\nexport const EDGE_BASE_CONFIG = {\n // Local-only (not synced)\n selected: false,\n\n // Atomic (synced as plain Json)\n markerStart: \"atomic\",\n markerEnd: \"atomic\",\n\n // Note: the `data` key is intentionally left out of this base config, as it\n // is expected to be provided by the end user\n} as const satisfies SyncConfig;\n"],"names":[],"mappings":"AAEO,MAAM,mBAAsB,GAAA,OAAA;AAE5B,MAAM,gBAAmB,GAAA;AAAA;AAAA,EAE9B,QAAU,EAAA,KAAA;AAAA,EACV,QAAU,EAAA,KAAA;AAAA,EACV,QAAU,EAAA,KAAA;AAAA,EACV,QAAU,EAAA,KAAA;AAAA;AAAA,EAGV,QAAU,EAAA,QAAA;AAAA,EACV,cAAgB,EAAA,QAAA;AAAA,EAChB,cAAgB,EAAA,QAAA;AAAA,EAChB,MAAQ,EAAA,QAAA;AAAA,EACR,MAAQ,EAAA,QAAA;AAAA,EACR,OAAS,EAAA,QAAA;AAAA;AAAA;AAIX,EAAA;AAEO,MAAM,gBAAmB,GAAA;AAAA;AAAA,EAE9B,QAAU,EAAA,KAAA;AAAA;AAAA,EAGV,WAAa,EAAA,QAAA;AAAA,EACb,SAAW,EAAA,QAAA;AAAA;AAAA;AAIb;;;;"}
package/dist/cursors.cjs CHANGED
@@ -19,18 +19,24 @@ function $coordinates(value) {
19
19
  }
20
20
  return void 0;
21
21
  }
22
+ function DefaultCursorWithUserInfo({ userId }) {
23
+ const { user, isLoading } = react.useUser(userId);
24
+ const color = $string(user?.color);
25
+ const name = $string(user?.name);
26
+ if (isLoading) {
27
+ return null;
28
+ }
29
+ return /* @__PURE__ */ jsxRuntime.jsx(reactUi.Cursor, { color, label: name });
30
+ }
22
31
  function PresenceCursor({
23
32
  connectionId,
24
- presenceKey
33
+ presenceKey,
34
+ Cursor
25
35
  }) {
26
36
  const room = react.useRoom();
27
37
  const cursorRef = react$1.useRef(null);
28
38
  const reactFlowStoreApi = react$2.useStoreApi();
29
39
  const userId = react.useOther(connectionId, (other) => $string(other.id));
30
- const { user, isLoading } = react.useUser(userId ?? "");
31
- const hasUserInfo = userId !== void 0 && !isLoading;
32
- const color = $string(user?.color);
33
- const name = $string(user?.name);
34
40
  _private.useLayoutEffect(() => {
35
41
  const spring = _private$1.makeCursorSpring();
36
42
  function update() {
@@ -40,7 +46,7 @@ function PresenceCursor({
40
46
  if (!element) {
41
47
  return;
42
48
  }
43
- if (!hasUserInfo || coordinates === null) {
49
+ if (coordinates === null) {
44
50
  element.style.transform = "translate3d(0, 0, 0)";
45
51
  element.style.display = "none";
46
52
  return;
@@ -68,19 +74,21 @@ function PresenceCursor({
68
74
  unsubscribeTransform();
69
75
  unsubscribeOther();
70
76
  };
71
- }, [room, connectionId, presenceKey, hasUserInfo, reactFlowStoreApi]);
72
- return /* @__PURE__ */ jsxRuntime.jsx(
73
- reactUi.Cursor,
74
- {
75
- color,
76
- label: name,
77
- ref: cursorRef,
78
- style: { display: "none" }
79
- }
80
- );
77
+ }, [room, connectionId, presenceKey, reactFlowStoreApi]);
78
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: cursorRef, style: { display: "none" }, children: userId ? /* @__PURE__ */ jsxRuntime.jsx(Cursor, { userId, connectionId }) : /* @__PURE__ */ jsxRuntime.jsx(reactUi.Cursor, {}) });
81
79
  }
82
80
  const Cursors = react$1.forwardRef(
83
- ({ className, children, presenceKey = DEFAULT_PRESENCE_KEY, ...props }, forwardedRef) => {
81
+ ({
82
+ className,
83
+ children,
84
+ presenceKey = DEFAULT_PRESENCE_KEY,
85
+ components,
86
+ ...props
87
+ }, forwardedRef) => {
88
+ const Cursor = _private$1.useStableComponent(
89
+ components?.Cursor,
90
+ DefaultCursorWithUserInfo
91
+ );
84
92
  const reactFlow = react$2.useReactFlow();
85
93
  const updateMyPresence = react.useUpdateMyPresence();
86
94
  const othersConnectionIds = react.useOthersConnectionIds();
@@ -133,7 +141,8 @@ const Cursors = react$1.forwardRef(
133
141
  PresenceCursor,
134
142
  {
135
143
  connectionId,
136
- presenceKey
144
+ presenceKey,
145
+ Cursor
137
146
  },
138
147
  connectionId
139
148
  )),
@@ -1 +1 @@
1
- {"version":3,"file":"cursors.cjs","sources":["../src/cursors.tsx"],"sourcesContent":["import { isPlainObject } 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 { Cursor } from \"@liveblocks/react-ui\";\nimport { cn, makeCursorSpring } from \"@liveblocks/react-ui/_private\";\nimport { useReactFlow, useStore, useStoreApi } from \"@xyflow/react\";\nimport type { ComponentPropsWithoutRef } from \"react\";\nimport { forwardRef, useCallback, useEffect, useRef } from \"react\";\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 *\n * Defaults to `\"cursor\"`.\n */\n presenceKey?: string;\n}\n\ntype Coordinates = {\n x: number;\n y: 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\nfunction PresenceCursor({\n connectionId,\n presenceKey,\n}: {\n connectionId: number;\n presenceKey: string;\n}) {\n const room = useRoom();\n const cursorRef = useRef<HTMLDivElement>(null);\n const reactFlowStoreApi = useStoreApi();\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 = makeCursorSpring();\n\n function update() {\n const element = cursorRef.current;\n const coordinates = spring.get();\n const [panX, panY, zoom] = reactFlowStoreApi.getState().transform;\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 element.style.transform = `translate3d(${coordinates.x * zoom + panX}px, ${coordinates.y * zoom + panY}px, 0)`;\n element.style.display = \"\";\n }\n\n update();\n\n const unsubscribeSpring = spring.subscribe(update);\n const unsubscribeTransform = reactFlowStoreApi.subscribe(\n (state, previousState) => {\n // Update positions whenever the canvas is panned or zoomed.\n if (state.transform !== previousState.transform) {\n update();\n }\n }\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 unsubscribeTransform();\n unsubscribeOther();\n };\n }, [room, connectionId, presenceKey, hasUserInfo, reactFlowStoreApi]);\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 other users' cursors inside a React Flow canvas and stores the\n * current user's cursor in Presence as `{ cursor: { x, y } }`.\n *\n * Cursor coordinates are kept in React Flow canvas space, so panning moves\n * them correctly while zooming only affects their position, not their size.\n */\nexport const Cursors = forwardRef<HTMLDivElement, CursorsProps>(\n (\n { className, children, presenceKey = DEFAULT_PRESENCE_KEY, ...props },\n forwardedRef\n ) => {\n const reactFlow = useReactFlow();\n const updateMyPresence = useUpdateMyPresence();\n const othersConnectionIds = useOthersConnectionIds();\n const reactFlowDomNode = useStore((state) => state.domNode);\n const reactFlowStoreApi = useStoreApi();\n\n const handlePointerMove = useCallback(\n (event: PointerEvent) => {\n const isPanning = reactFlowStoreApi.getState().paneDragging;\n\n if (isPanning) {\n return;\n }\n\n updateMyPresence({\n [presenceKey]: reactFlow.screenToFlowPosition({\n x: event.clientX,\n y: event.clientY,\n }),\n });\n },\n [updateMyPresence, presenceKey, reactFlow, reactFlowStoreApi]\n );\n\n const handlePointerLeave = useCallback(() => {\n updateMyPresence({ [presenceKey]: null });\n }, [updateMyPresence, presenceKey]);\n\n useEffect(() => {\n if (!reactFlowDomNode) {\n return;\n }\n\n reactFlowDomNode.addEventListener(\"pointermove\", handlePointerMove);\n reactFlowDomNode.addEventListener(\"pointerleave\", handlePointerLeave);\n window.addEventListener(\"blur\", handlePointerLeave);\n\n return () => {\n reactFlowDomNode.removeEventListener(\"pointermove\", handlePointerMove);\n reactFlowDomNode.removeEventListener(\n \"pointerleave\",\n handlePointerLeave\n );\n window.removeEventListener(\"blur\", handlePointerLeave);\n handlePointerLeave();\n };\n }, [reactFlowDomNode, handlePointerMove, handlePointerLeave]);\n\n return (\n <div\n aria-hidden\n className={cn(\"lb-root lb-react-flow-cursors\", className)}\n {...props}\n ref={forwardedRef}\n >\n {othersConnectionIds.map((connectionId) => (\n <PresenceCursor\n key={connectionId}\n connectionId={connectionId}\n presenceKey={presenceKey}\n />\n ))}\n\n {children}\n </div>\n );\n }\n);\n"],"names":["isPlainObject","useRoom","useRef","useStoreApi","useOther","useUser","useLayoutEffect","makeCursorSpring","other","jsx","Cursor","forwardRef","useReactFlow","useUpdateMyPresence","useOthersConnectionIds","useStore","useCallback","useEffect","jsxs","cn"],"mappings":"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"}
1
+ {"version":3,"file":"cursors.cjs","sources":["../src/cursors.tsx"],"sourcesContent":["import { isPlainObject } 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 { Cursor as DefaultCursor } from \"@liveblocks/react-ui\";\nimport {\n cn,\n makeCursorSpring,\n useStableComponent,\n} from \"@liveblocks/react-ui/_private\";\nimport { useReactFlow, useStore, useStoreApi } from \"@xyflow/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport { forwardRef, useCallback, useEffect, useRef } from \"react\";\n\nconst DEFAULT_PRESENCE_KEY = \"cursor\";\n\nexport interface CursorsCursorProps {\n /**\n * The user ID for this cursor.\n */\n userId: string;\n\n /**\n * The connection ID for this cursor.\n */\n connectionId: number;\n}\n\ninterface CursorsComponents {\n /**\n * The component used to display each cursor.\n */\n Cursor: ComponentType<CursorsCursorProps>;\n}\n\nexport interface CursorsProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The key used to store the cursors in users' Presence.\n *\n * Defaults to `\"cursor\"`.\n */\n presenceKey?: string;\n\n /**\n * Override the component's components.\n */\n components?: Partial<CursorsComponents>;\n}\n\ntype Coordinates = {\n x: number;\n y: 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\nfunction DefaultCursorWithUserInfo({ userId }: CursorsCursorProps) {\n const { user, isLoading } = useUser(userId);\n const color = $string(user?.color);\n const name = $string(user?.name);\n\n if (isLoading) {\n return null;\n }\n\n return <DefaultCursor color={color} label={name} />;\n}\n\nfunction PresenceCursor({\n connectionId,\n presenceKey,\n Cursor,\n}: {\n connectionId: number;\n presenceKey: string;\n Cursor: ComponentType<CursorsCursorProps>;\n}) {\n const room = useRoom();\n const cursorRef = useRef<HTMLDivElement>(null);\n const reactFlowStoreApi = useStoreApi();\n const userId = useOther(connectionId, (other) => $string(other.id));\n\n useLayoutEffect(() => {\n const spring = makeCursorSpring();\n\n function update() {\n const element = cursorRef.current;\n const coordinates = spring.get();\n const [panX, panY, zoom] = reactFlowStoreApi.getState().transform;\n\n if (!element) {\n return;\n }\n\n if (coordinates === null) {\n element.style.transform = \"translate3d(0, 0, 0)\";\n element.style.display = \"none\";\n return;\n }\n\n element.style.transform = `translate3d(${coordinates.x * zoom + panX}px, ${coordinates.y * zoom + panY}px, 0)`;\n element.style.display = \"\";\n }\n\n update();\n\n const unsubscribeSpring = spring.subscribe(update);\n const unsubscribeTransform = reactFlowStoreApi.subscribe(\n (state, previousState) => {\n // Update positions whenever the canvas is panned or zoomed.\n if (state.transform !== previousState.transform) {\n update();\n }\n }\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 unsubscribeTransform();\n unsubscribeOther();\n };\n }, [room, connectionId, presenceKey, reactFlowStoreApi]);\n\n return (\n <div ref={cursorRef} style={{ display: \"none\" }}>\n {userId ? (\n <Cursor userId={userId} connectionId={connectionId} />\n ) : (\n <DefaultCursor />\n )}\n </div>\n );\n}\n\n/**\n * Displays other users' cursors inside a React Flow canvas and stores the\n * current user's cursor in Presence as `{ cursor: { x, y } }`.\n *\n * Cursor coordinates are kept in React Flow canvas space, so panning moves\n * them correctly while zooming only affects their position, not their size.\n */\nexport const Cursors = forwardRef<HTMLDivElement, CursorsProps>(\n (\n {\n className,\n children,\n presenceKey = DEFAULT_PRESENCE_KEY,\n components,\n ...props\n },\n forwardedRef\n ) => {\n const Cursor = useStableComponent(\n components?.Cursor,\n DefaultCursorWithUserInfo\n );\n const reactFlow = useReactFlow();\n const updateMyPresence = useUpdateMyPresence();\n const othersConnectionIds = useOthersConnectionIds();\n const reactFlowDomNode = useStore((state) => state.domNode);\n const reactFlowStoreApi = useStoreApi();\n\n const handlePointerMove = useCallback(\n (event: PointerEvent) => {\n const isPanning = reactFlowStoreApi.getState().paneDragging;\n\n if (isPanning) {\n return;\n }\n\n updateMyPresence({\n [presenceKey]: reactFlow.screenToFlowPosition({\n x: event.clientX,\n y: event.clientY,\n }),\n });\n },\n [updateMyPresence, presenceKey, reactFlow, reactFlowStoreApi]\n );\n\n const handlePointerLeave = useCallback(() => {\n updateMyPresence({ [presenceKey]: null });\n }, [updateMyPresence, presenceKey]);\n\n useEffect(() => {\n if (!reactFlowDomNode) {\n return;\n }\n\n reactFlowDomNode.addEventListener(\"pointermove\", handlePointerMove);\n reactFlowDomNode.addEventListener(\"pointerleave\", handlePointerLeave);\n window.addEventListener(\"blur\", handlePointerLeave);\n\n return () => {\n reactFlowDomNode.removeEventListener(\"pointermove\", handlePointerMove);\n reactFlowDomNode.removeEventListener(\n \"pointerleave\",\n handlePointerLeave\n );\n window.removeEventListener(\"blur\", handlePointerLeave);\n handlePointerLeave();\n };\n }, [reactFlowDomNode, handlePointerMove, handlePointerLeave]);\n\n return (\n <div\n aria-hidden\n className={cn(\"lb-root lb-react-flow-cursors\", className)}\n {...props}\n ref={forwardedRef}\n >\n {othersConnectionIds.map((connectionId) => (\n <PresenceCursor\n key={connectionId}\n connectionId={connectionId}\n presenceKey={presenceKey}\n Cursor={Cursor}\n />\n ))}\n\n {children}\n </div>\n );\n }\n);\n"],"names":["isPlainObject","useUser","jsx","DefaultCursor","useRoom","useRef","useStoreApi","useOther","useLayoutEffect","makeCursorSpring","other","forwardRef","useStableComponent","useReactFlow","useUpdateMyPresence","useOthersConnectionIds","useStore","useCallback","useEffect","jsxs","cn"],"mappings":";;;;;;;;;;;AAmBA,MAAM,oBAAuB,GAAA,QAAA,CAAA;AAwC7B,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,IAAAA,kBAAA,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;AAEA,SAAS,yBAAA,CAA0B,EAAE,MAAA,EAA8B,EAAA;AACjE,EAAA,MAAM,EAAE,IAAA,EAAM,SAAU,EAAA,GAAIC,cAAQ,MAAM,CAAA,CAAA;AAC1C,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,IAAI,SAAW,EAAA;AACb,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AAEA,EAAA,uBAAQC,cAAA,CAAAC,cAAA,EAAA,EAAc,KAAc,EAAA,KAAA,EAAO,IAAM,EAAA,CAAA,CAAA;AACnD,CAAA;AAEA,SAAS,cAAe,CAAA;AAAA,EACtB,YAAA;AAAA,EACA,WAAA;AAAA,EACA,MAAA;AACF,CAIG,EAAA;AACD,EAAA,MAAM,OAAOC,aAAQ,EAAA,CAAA;AACrB,EAAM,MAAA,SAAA,GAAYC,eAAuB,IAAI,CAAA,CAAA;AAC7C,EAAA,MAAM,oBAAoBC,mBAAY,EAAA,CAAA;AACtC,EAAM,MAAA,MAAA,GAASC,eAAS,YAAc,EAAA,CAAC,UAAU,OAAQ,CAAA,KAAA,CAAM,EAAE,CAAC,CAAA,CAAA;AAElE,EAAAC,wBAAA,CAAgB,MAAM;AACpB,IAAA,MAAM,SAASC,2BAAiB,EAAA,CAAA;AAEhC,IAAA,SAAS,MAAS,GAAA;AAChB,MAAA,MAAM,UAAU,SAAU,CAAA,OAAA,CAAA;AAC1B,MAAM,MAAA,WAAA,GAAc,OAAO,GAAI,EAAA,CAAA;AAC/B,MAAA,MAAM,CAAC,IAAM,EAAA,IAAA,EAAM,IAAI,CAAI,GAAA,iBAAA,CAAkB,UAAW,CAAA,SAAA,CAAA;AAExD,MAAA,IAAI,CAAC,OAAS,EAAA;AACZ,QAAA,OAAA;AAAA,OACF;AAEA,MAAA,IAAI,gBAAgB,IAAM,EAAA;AACxB,QAAA,OAAA,CAAQ,MAAM,SAAY,GAAA,sBAAA,CAAA;AAC1B,QAAA,OAAA,CAAQ,MAAM,OAAU,GAAA,MAAA,CAAA;AACxB,QAAA,OAAA;AAAA,OACF;AAEA,MAAQ,OAAA,CAAA,KAAA,CAAM,SAAY,GAAA,CAAA,YAAA,EAAe,WAAY,CAAA,CAAA,GAAI,IAAO,GAAA,IAAI,CAAO,IAAA,EAAA,WAAA,CAAY,CAAI,GAAA,IAAA,GAAO,IAAI,CAAA,MAAA,CAAA,CAAA;AACtG,MAAA,OAAA,CAAQ,MAAM,OAAU,GAAA,EAAA,CAAA;AAAA,KAC1B;AAEA,IAAO,MAAA,EAAA,CAAA;AAEP,IAAM,MAAA,iBAAA,GAAoB,MAAO,CAAA,SAAA,CAAU,MAAM,CAAA,CAAA;AACjD,IAAA,MAAM,uBAAuB,iBAAkB,CAAA,SAAA;AAAA,MAC7C,CAAC,OAAO,aAAkB,KAAA;AAExB,QAAI,IAAA,KAAA,CAAM,SAAc,KAAA,aAAA,CAAc,SAAW,EAAA;AAC/C,UAAO,MAAA,EAAA,CAAA;AAAA,SACT;AAAA,OACF;AAAA,KACF,CAAA;AACA,IAAM,MAAA,gBAAA,GAAmB,KAAK,MAAO,CAAA,MAAA,CAAO,UAAU,CAAC,EAAE,QAAa,KAAA;AACpE,MAAA,MAAM,QAAQ,MAAO,CAAA,IAAA,CAAK,CAACC,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,MAAqB,oBAAA,EAAA,CAAA;AACrB,MAAiB,gBAAA,EAAA,CAAA;AAAA,KACnB,CAAA;AAAA,KACC,CAAC,IAAA,EAAM,YAAc,EAAA,WAAA,EAAa,iBAAiB,CAAC,CAAA,CAAA;AAEvD,EAAA,sCACG,KAAI,EAAA,EAAA,GAAA,EAAK,SAAW,EAAA,KAAA,EAAO,EAAE,OAAS,EAAA,MAAA,EACpC,EAAA,QAAA,EAAA,MAAA,kCACE,MAAO,EAAA,EAAA,MAAA,EAAgB,cAA4B,CAEpD,mBAAAR,cAAA,CAACC,kBAAc,CAEnB,EAAA,CAAA,CAAA;AAEJ,CAAA;AASO,MAAM,OAAU,GAAAQ,kBAAA;AAAA,EACrB,CACE;AAAA,IACE,SAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAc,GAAA,oBAAA;AAAA,IACd,UAAA;AAAA,IACA,GAAG,KAAA;AAAA,KAEL,YACG,KAAA;AACH,IAAA,MAAM,MAAS,GAAAC,6BAAA;AAAA,MACb,UAAY,EAAA,MAAA;AAAA,MACZ,yBAAA;AAAA,KACF,CAAA;AACA,IAAA,MAAM,YAAYC,oBAAa,EAAA,CAAA;AAC/B,IAAA,MAAM,mBAAmBC,yBAAoB,EAAA,CAAA;AAC7C,IAAA,MAAM,sBAAsBC,4BAAuB,EAAA,CAAA;AACnD,IAAA,MAAM,gBAAmB,GAAAC,gBAAA,CAAS,CAAC,KAAA,KAAU,MAAM,OAAO,CAAA,CAAA;AAC1D,IAAA,MAAM,oBAAoBV,mBAAY,EAAA,CAAA;AAEtC,IAAA,MAAM,iBAAoB,GAAAW,mBAAA;AAAA,MACxB,CAAC,KAAwB,KAAA;AACvB,QAAM,MAAA,SAAA,GAAY,iBAAkB,CAAA,QAAA,EAAW,CAAA,YAAA,CAAA;AAE/C,QAAA,IAAI,SAAW,EAAA;AACb,UAAA,OAAA;AAAA,SACF;AAEA,QAAiB,gBAAA,CAAA;AAAA,UACf,CAAC,WAAW,GAAG,SAAA,CAAU,oBAAqB,CAAA;AAAA,YAC5C,GAAG,KAAM,CAAA,OAAA;AAAA,YACT,GAAG,KAAM,CAAA,OAAA;AAAA,WACV,CAAA;AAAA,SACF,CAAA,CAAA;AAAA,OACH;AAAA,MACA,CAAC,gBAAA,EAAkB,WAAa,EAAA,SAAA,EAAW,iBAAiB,CAAA;AAAA,KAC9D,CAAA;AAEA,IAAM,MAAA,kBAAA,GAAqBA,oBAAY,MAAM;AAC3C,MAAA,gBAAA,CAAiB,EAAE,CAAC,WAAW,GAAG,MAAM,CAAA,CAAA;AAAA,KACvC,EAAA,CAAC,gBAAkB,EAAA,WAAW,CAAC,CAAA,CAAA;AAElC,IAAAC,iBAAA,CAAU,MAAM;AACd,MAAA,IAAI,CAAC,gBAAkB,EAAA;AACrB,QAAA,OAAA;AAAA,OACF;AAEA,MAAiB,gBAAA,CAAA,gBAAA,CAAiB,eAAe,iBAAiB,CAAA,CAAA;AAClE,MAAiB,gBAAA,CAAA,gBAAA,CAAiB,gBAAgB,kBAAkB,CAAA,CAAA;AACpE,MAAO,MAAA,CAAA,gBAAA,CAAiB,QAAQ,kBAAkB,CAAA,CAAA;AAElD,MAAA,OAAO,MAAM;AACX,QAAiB,gBAAA,CAAA,mBAAA,CAAoB,eAAe,iBAAiB,CAAA,CAAA;AACrE,QAAiB,gBAAA,CAAA,mBAAA;AAAA,UACf,cAAA;AAAA,UACA,kBAAA;AAAA,SACF,CAAA;AACA,QAAO,MAAA,CAAA,mBAAA,CAAoB,QAAQ,kBAAkB,CAAA,CAAA;AACrD,QAAmB,kBAAA,EAAA,CAAA;AAAA,OACrB,CAAA;AAAA,KACC,EAAA,CAAC,gBAAkB,EAAA,iBAAA,EAAmB,kBAAkB,CAAC,CAAA,CAAA;AAE5D,IACE,uBAAAC,eAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,aAAW,EAAA,IAAA;AAAA,QACX,SAAA,EAAWC,aAAG,CAAA,+BAAA,EAAiC,SAAS,CAAA;AAAA,QACvD,GAAG,KAAA;AAAA,QACJ,GAAK,EAAA,YAAA;AAAA,QAEJ,QAAA,EAAA;AAAA,UAAoB,mBAAA,CAAA,GAAA,CAAI,CAAC,YACxB,qBAAAlB,cAAA;AAAA,YAAC,cAAA;AAAA,YAAA;AAAA,cAEC,YAAA;AAAA,cACA,WAAA;AAAA,cACA,MAAA;AAAA,aAAA;AAAA,YAHK,YAAA;AAAA,WAKR,CAAA;AAAA,UAEA,QAAA;AAAA,SAAA;AAAA,OAAA;AAAA,KACH,CAAA;AAAA,GAEJ;AACF;;;;"}
package/dist/cursors.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { isPlainObject } from '@liveblocks/core';
3
- import { useRoom, useOther, useUser, useUpdateMyPresence, useOthersConnectionIds } from '@liveblocks/react';
3
+ import { useUser, useRoom, useOther, useUpdateMyPresence, useOthersConnectionIds } from '@liveblocks/react';
4
4
  import { useLayoutEffect } from '@liveblocks/react/_private';
5
5
  import { Cursor } from '@liveblocks/react-ui';
6
- import { makeCursorSpring, cn } from '@liveblocks/react-ui/_private';
6
+ import { makeCursorSpring, useStableComponent, cn } from '@liveblocks/react-ui/_private';
7
7
  import { useStoreApi, useReactFlow, useStore } from '@xyflow/react';
8
8
  import { useRef, forwardRef, useCallback, useEffect } from 'react';
9
9
 
@@ -17,18 +17,24 @@ function $coordinates(value) {
17
17
  }
18
18
  return void 0;
19
19
  }
20
+ function DefaultCursorWithUserInfo({ userId }) {
21
+ const { user, isLoading } = useUser(userId);
22
+ const color = $string(user?.color);
23
+ const name = $string(user?.name);
24
+ if (isLoading) {
25
+ return null;
26
+ }
27
+ return /* @__PURE__ */ jsx(Cursor, { color, label: name });
28
+ }
20
29
  function PresenceCursor({
21
30
  connectionId,
22
- presenceKey
31
+ presenceKey,
32
+ Cursor: Cursor$1
23
33
  }) {
24
34
  const room = useRoom();
25
35
  const cursorRef = useRef(null);
26
36
  const reactFlowStoreApi = useStoreApi();
27
37
  const userId = useOther(connectionId, (other) => $string(other.id));
28
- const { user, isLoading } = useUser(userId ?? "");
29
- const hasUserInfo = userId !== void 0 && !isLoading;
30
- const color = $string(user?.color);
31
- const name = $string(user?.name);
32
38
  useLayoutEffect(() => {
33
39
  const spring = makeCursorSpring();
34
40
  function update() {
@@ -38,7 +44,7 @@ function PresenceCursor({
38
44
  if (!element) {
39
45
  return;
40
46
  }
41
- if (!hasUserInfo || coordinates === null) {
47
+ if (coordinates === null) {
42
48
  element.style.transform = "translate3d(0, 0, 0)";
43
49
  element.style.display = "none";
44
50
  return;
@@ -66,19 +72,21 @@ function PresenceCursor({
66
72
  unsubscribeTransform();
67
73
  unsubscribeOther();
68
74
  };
69
- }, [room, connectionId, presenceKey, hasUserInfo, reactFlowStoreApi]);
70
- return /* @__PURE__ */ jsx(
71
- Cursor,
72
- {
73
- color,
74
- label: name,
75
- ref: cursorRef,
76
- style: { display: "none" }
77
- }
78
- );
75
+ }, [room, connectionId, presenceKey, reactFlowStoreApi]);
76
+ return /* @__PURE__ */ jsx("div", { ref: cursorRef, style: { display: "none" }, children: userId ? /* @__PURE__ */ jsx(Cursor$1, { userId, connectionId }) : /* @__PURE__ */ jsx(Cursor, {}) });
79
77
  }
80
78
  const Cursors = forwardRef(
81
- ({ className, children, presenceKey = DEFAULT_PRESENCE_KEY, ...props }, forwardedRef) => {
79
+ ({
80
+ className,
81
+ children,
82
+ presenceKey = DEFAULT_PRESENCE_KEY,
83
+ components,
84
+ ...props
85
+ }, forwardedRef) => {
86
+ const Cursor = useStableComponent(
87
+ components?.Cursor,
88
+ DefaultCursorWithUserInfo
89
+ );
82
90
  const reactFlow = useReactFlow();
83
91
  const updateMyPresence = useUpdateMyPresence();
84
92
  const othersConnectionIds = useOthersConnectionIds();
@@ -131,7 +139,8 @@ const Cursors = forwardRef(
131
139
  PresenceCursor,
132
140
  {
133
141
  connectionId,
134
- presenceKey
142
+ presenceKey,
143
+ Cursor
135
144
  },
136
145
  connectionId
137
146
  )),
@@ -1 +1 @@
1
- {"version":3,"file":"cursors.js","sources":["../src/cursors.tsx"],"sourcesContent":["import { isPlainObject } 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 { Cursor } from \"@liveblocks/react-ui\";\nimport { cn, makeCursorSpring } from \"@liveblocks/react-ui/_private\";\nimport { useReactFlow, useStore, useStoreApi } from \"@xyflow/react\";\nimport type { ComponentPropsWithoutRef } from \"react\";\nimport { forwardRef, useCallback, useEffect, useRef } from \"react\";\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 *\n * Defaults to `\"cursor\"`.\n */\n presenceKey?: string;\n}\n\ntype Coordinates = {\n x: number;\n y: 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\nfunction PresenceCursor({\n connectionId,\n presenceKey,\n}: {\n connectionId: number;\n presenceKey: string;\n}) {\n const room = useRoom();\n const cursorRef = useRef<HTMLDivElement>(null);\n const reactFlowStoreApi = useStoreApi();\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 = makeCursorSpring();\n\n function update() {\n const element = cursorRef.current;\n const coordinates = spring.get();\n const [panX, panY, zoom] = reactFlowStoreApi.getState().transform;\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 element.style.transform = `translate3d(${coordinates.x * zoom + panX}px, ${coordinates.y * zoom + panY}px, 0)`;\n element.style.display = \"\";\n }\n\n update();\n\n const unsubscribeSpring = spring.subscribe(update);\n const unsubscribeTransform = reactFlowStoreApi.subscribe(\n (state, previousState) => {\n // Update positions whenever the canvas is panned or zoomed.\n if (state.transform !== previousState.transform) {\n update();\n }\n }\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 unsubscribeTransform();\n unsubscribeOther();\n };\n }, [room, connectionId, presenceKey, hasUserInfo, reactFlowStoreApi]);\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 other users' cursors inside a React Flow canvas and stores the\n * current user's cursor in Presence as `{ cursor: { x, y } }`.\n *\n * Cursor coordinates are kept in React Flow canvas space, so panning moves\n * them correctly while zooming only affects their position, not their size.\n */\nexport const Cursors = forwardRef<HTMLDivElement, CursorsProps>(\n (\n { className, children, presenceKey = DEFAULT_PRESENCE_KEY, ...props },\n forwardedRef\n ) => {\n const reactFlow = useReactFlow();\n const updateMyPresence = useUpdateMyPresence();\n const othersConnectionIds = useOthersConnectionIds();\n const reactFlowDomNode = useStore((state) => state.domNode);\n const reactFlowStoreApi = useStoreApi();\n\n const handlePointerMove = useCallback(\n (event: PointerEvent) => {\n const isPanning = reactFlowStoreApi.getState().paneDragging;\n\n if (isPanning) {\n return;\n }\n\n updateMyPresence({\n [presenceKey]: reactFlow.screenToFlowPosition({\n x: event.clientX,\n y: event.clientY,\n }),\n });\n },\n [updateMyPresence, presenceKey, reactFlow, reactFlowStoreApi]\n );\n\n const handlePointerLeave = useCallback(() => {\n updateMyPresence({ [presenceKey]: null });\n }, [updateMyPresence, presenceKey]);\n\n useEffect(() => {\n if (!reactFlowDomNode) {\n return;\n }\n\n reactFlowDomNode.addEventListener(\"pointermove\", handlePointerMove);\n reactFlowDomNode.addEventListener(\"pointerleave\", handlePointerLeave);\n window.addEventListener(\"blur\", handlePointerLeave);\n\n return () => {\n reactFlowDomNode.removeEventListener(\"pointermove\", handlePointerMove);\n reactFlowDomNode.removeEventListener(\n \"pointerleave\",\n handlePointerLeave\n );\n window.removeEventListener(\"blur\", handlePointerLeave);\n handlePointerLeave();\n };\n }, [reactFlowDomNode, handlePointerMove, handlePointerLeave]);\n\n return (\n <div\n aria-hidden\n className={cn(\"lb-root lb-react-flow-cursors\", className)}\n {...props}\n ref={forwardedRef}\n >\n {othersConnectionIds.map((connectionId) => (\n <PresenceCursor\n key={connectionId}\n connectionId={connectionId}\n presenceKey={presenceKey}\n />\n ))}\n\n {children}\n </div>\n );\n }\n);\n"],"names":["other"],"mappings":"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"}
1
+ {"version":3,"file":"cursors.js","sources":["../src/cursors.tsx"],"sourcesContent":["import { isPlainObject } 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 { Cursor as DefaultCursor } from \"@liveblocks/react-ui\";\nimport {\n cn,\n makeCursorSpring,\n useStableComponent,\n} from \"@liveblocks/react-ui/_private\";\nimport { useReactFlow, useStore, useStoreApi } from \"@xyflow/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport { forwardRef, useCallback, useEffect, useRef } from \"react\";\n\nconst DEFAULT_PRESENCE_KEY = \"cursor\";\n\nexport interface CursorsCursorProps {\n /**\n * The user ID for this cursor.\n */\n userId: string;\n\n /**\n * The connection ID for this cursor.\n */\n connectionId: number;\n}\n\ninterface CursorsComponents {\n /**\n * The component used to display each cursor.\n */\n Cursor: ComponentType<CursorsCursorProps>;\n}\n\nexport interface CursorsProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The key used to store the cursors in users' Presence.\n *\n * Defaults to `\"cursor\"`.\n */\n presenceKey?: string;\n\n /**\n * Override the component's components.\n */\n components?: Partial<CursorsComponents>;\n}\n\ntype Coordinates = {\n x: number;\n y: 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\nfunction DefaultCursorWithUserInfo({ userId }: CursorsCursorProps) {\n const { user, isLoading } = useUser(userId);\n const color = $string(user?.color);\n const name = $string(user?.name);\n\n if (isLoading) {\n return null;\n }\n\n return <DefaultCursor color={color} label={name} />;\n}\n\nfunction PresenceCursor({\n connectionId,\n presenceKey,\n Cursor,\n}: {\n connectionId: number;\n presenceKey: string;\n Cursor: ComponentType<CursorsCursorProps>;\n}) {\n const room = useRoom();\n const cursorRef = useRef<HTMLDivElement>(null);\n const reactFlowStoreApi = useStoreApi();\n const userId = useOther(connectionId, (other) => $string(other.id));\n\n useLayoutEffect(() => {\n const spring = makeCursorSpring();\n\n function update() {\n const element = cursorRef.current;\n const coordinates = spring.get();\n const [panX, panY, zoom] = reactFlowStoreApi.getState().transform;\n\n if (!element) {\n return;\n }\n\n if (coordinates === null) {\n element.style.transform = \"translate3d(0, 0, 0)\";\n element.style.display = \"none\";\n return;\n }\n\n element.style.transform = `translate3d(${coordinates.x * zoom + panX}px, ${coordinates.y * zoom + panY}px, 0)`;\n element.style.display = \"\";\n }\n\n update();\n\n const unsubscribeSpring = spring.subscribe(update);\n const unsubscribeTransform = reactFlowStoreApi.subscribe(\n (state, previousState) => {\n // Update positions whenever the canvas is panned or zoomed.\n if (state.transform !== previousState.transform) {\n update();\n }\n }\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 unsubscribeTransform();\n unsubscribeOther();\n };\n }, [room, connectionId, presenceKey, reactFlowStoreApi]);\n\n return (\n <div ref={cursorRef} style={{ display: \"none\" }}>\n {userId ? (\n <Cursor userId={userId} connectionId={connectionId} />\n ) : (\n <DefaultCursor />\n )}\n </div>\n );\n}\n\n/**\n * Displays other users' cursors inside a React Flow canvas and stores the\n * current user's cursor in Presence as `{ cursor: { x, y } }`.\n *\n * Cursor coordinates are kept in React Flow canvas space, so panning moves\n * them correctly while zooming only affects their position, not their size.\n */\nexport const Cursors = forwardRef<HTMLDivElement, CursorsProps>(\n (\n {\n className,\n children,\n presenceKey = DEFAULT_PRESENCE_KEY,\n components,\n ...props\n },\n forwardedRef\n ) => {\n const Cursor = useStableComponent(\n components?.Cursor,\n DefaultCursorWithUserInfo\n );\n const reactFlow = useReactFlow();\n const updateMyPresence = useUpdateMyPresence();\n const othersConnectionIds = useOthersConnectionIds();\n const reactFlowDomNode = useStore((state) => state.domNode);\n const reactFlowStoreApi = useStoreApi();\n\n const handlePointerMove = useCallback(\n (event: PointerEvent) => {\n const isPanning = reactFlowStoreApi.getState().paneDragging;\n\n if (isPanning) {\n return;\n }\n\n updateMyPresence({\n [presenceKey]: reactFlow.screenToFlowPosition({\n x: event.clientX,\n y: event.clientY,\n }),\n });\n },\n [updateMyPresence, presenceKey, reactFlow, reactFlowStoreApi]\n );\n\n const handlePointerLeave = useCallback(() => {\n updateMyPresence({ [presenceKey]: null });\n }, [updateMyPresence, presenceKey]);\n\n useEffect(() => {\n if (!reactFlowDomNode) {\n return;\n }\n\n reactFlowDomNode.addEventListener(\"pointermove\", handlePointerMove);\n reactFlowDomNode.addEventListener(\"pointerleave\", handlePointerLeave);\n window.addEventListener(\"blur\", handlePointerLeave);\n\n return () => {\n reactFlowDomNode.removeEventListener(\"pointermove\", handlePointerMove);\n reactFlowDomNode.removeEventListener(\n \"pointerleave\",\n handlePointerLeave\n );\n window.removeEventListener(\"blur\", handlePointerLeave);\n handlePointerLeave();\n };\n }, [reactFlowDomNode, handlePointerMove, handlePointerLeave]);\n\n return (\n <div\n aria-hidden\n className={cn(\"lb-root lb-react-flow-cursors\", className)}\n {...props}\n ref={forwardedRef}\n >\n {othersConnectionIds.map((connectionId) => (\n <PresenceCursor\n key={connectionId}\n connectionId={connectionId}\n presenceKey={presenceKey}\n Cursor={Cursor}\n />\n ))}\n\n {children}\n </div>\n );\n }\n);\n"],"names":["DefaultCursor","Cursor","other"],"mappings":"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"}