@liveblocks/react-flow 0.0.0 → 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 ADDED
@@ -0,0 +1,57 @@
1
+ <p>
2
+ <a href="https://liveblocks.io#gh-light-mode-only"><img src="https://raw.githubusercontent.com/liveblocks/liveblocks/main/.github/assets/header-light.svg" alt="Liveblocks" /></a>
3
+ <a href="https://liveblocks.io#gh-dark-mode-only"><img src="https://raw.githubusercontent.com/liveblocks/liveblocks/main/.github/assets/header-dark.svg" alt="Liveblocks" /></a>
4
+ </p>
5
+
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;;;;"}
@@ -0,0 +1,157 @@
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 reactUi = require('@liveblocks/react-ui');
8
+ var _private$1 = require('@liveblocks/react-ui/_private');
9
+ var react$2 = require('@xyflow/react');
10
+ var react$1 = require('react');
11
+
12
+ const DEFAULT_PRESENCE_KEY = "cursor";
13
+ function $string(value) {
14
+ return typeof value === "string" ? value : void 0;
15
+ }
16
+ function $coordinates(value) {
17
+ if (core.isPlainObject(value) && typeof value.x === "number" && typeof value.y === "number") {
18
+ return value;
19
+ }
20
+ return void 0;
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
+ }
31
+ function PresenceCursor({
32
+ connectionId,
33
+ presenceKey,
34
+ Cursor
35
+ }) {
36
+ const room = react.useRoom();
37
+ const cursorRef = react$1.useRef(null);
38
+ const reactFlowStoreApi = react$2.useStoreApi();
39
+ const userId = react.useOther(connectionId, (other) => $string(other.id));
40
+ _private.useLayoutEffect(() => {
41
+ const spring = _private$1.makeCursorSpring();
42
+ function update() {
43
+ const element = cursorRef.current;
44
+ const coordinates = spring.get();
45
+ const [panX, panY, zoom] = reactFlowStoreApi.getState().transform;
46
+ if (!element) {
47
+ return;
48
+ }
49
+ if (coordinates === null) {
50
+ element.style.transform = "translate3d(0, 0, 0)";
51
+ element.style.display = "none";
52
+ return;
53
+ }
54
+ element.style.transform = `translate3d(${coordinates.x * zoom + panX}px, ${coordinates.y * zoom + panY}px, 0)`;
55
+ element.style.display = "";
56
+ }
57
+ update();
58
+ const unsubscribeSpring = spring.subscribe(update);
59
+ const unsubscribeTransform = reactFlowStoreApi.subscribe(
60
+ (state, previousState) => {
61
+ if (state.transform !== previousState.transform) {
62
+ update();
63
+ }
64
+ }
65
+ );
66
+ const unsubscribeOther = room.events.others.subscribe(({ others }) => {
67
+ const other = others.find((other2) => other2.connectionId === connectionId);
68
+ const cursor = $coordinates(other?.presence[presenceKey]);
69
+ spring.set(cursor ?? null);
70
+ });
71
+ return () => {
72
+ spring.dispose();
73
+ unsubscribeSpring();
74
+ unsubscribeTransform();
75
+ unsubscribeOther();
76
+ };
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, {}) });
79
+ }
80
+ const Cursors = react$1.forwardRef(
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
+ );
92
+ const reactFlow = react$2.useReactFlow();
93
+ const updateMyPresence = react.useUpdateMyPresence();
94
+ const othersConnectionIds = react.useOthersConnectionIds();
95
+ const reactFlowDomNode = react$2.useStore((state) => state.domNode);
96
+ const reactFlowStoreApi = react$2.useStoreApi();
97
+ const handlePointerMove = react$1.useCallback(
98
+ (event) => {
99
+ const isPanning = reactFlowStoreApi.getState().paneDragging;
100
+ if (isPanning) {
101
+ return;
102
+ }
103
+ updateMyPresence({
104
+ [presenceKey]: reactFlow.screenToFlowPosition({
105
+ x: event.clientX,
106
+ y: event.clientY
107
+ })
108
+ });
109
+ },
110
+ [updateMyPresence, presenceKey, reactFlow, reactFlowStoreApi]
111
+ );
112
+ const handlePointerLeave = react$1.useCallback(() => {
113
+ updateMyPresence({ [presenceKey]: null });
114
+ }, [updateMyPresence, presenceKey]);
115
+ react$1.useEffect(() => {
116
+ if (!reactFlowDomNode) {
117
+ return;
118
+ }
119
+ reactFlowDomNode.addEventListener("pointermove", handlePointerMove);
120
+ reactFlowDomNode.addEventListener("pointerleave", handlePointerLeave);
121
+ window.addEventListener("blur", handlePointerLeave);
122
+ return () => {
123
+ reactFlowDomNode.removeEventListener("pointermove", handlePointerMove);
124
+ reactFlowDomNode.removeEventListener(
125
+ "pointerleave",
126
+ handlePointerLeave
127
+ );
128
+ window.removeEventListener("blur", handlePointerLeave);
129
+ handlePointerLeave();
130
+ };
131
+ }, [reactFlowDomNode, handlePointerMove, handlePointerLeave]);
132
+ return /* @__PURE__ */ jsxRuntime.jsxs(
133
+ "div",
134
+ {
135
+ "aria-hidden": true,
136
+ className: _private$1.cn("lb-root lb-react-flow-cursors", className),
137
+ ...props,
138
+ ref: forwardedRef,
139
+ children: [
140
+ othersConnectionIds.map((connectionId) => /* @__PURE__ */ jsxRuntime.jsx(
141
+ PresenceCursor,
142
+ {
143
+ connectionId,
144
+ presenceKey,
145
+ Cursor
146
+ },
147
+ connectionId
148
+ )),
149
+ children
150
+ ]
151
+ }
152
+ );
153
+ }
154
+ );
155
+
156
+ exports.Cursors = Cursors;
157
+ //# sourceMappingURL=cursors.cjs.map
@@ -0,0 +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 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":"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"}
@@ -0,0 +1,155 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { isPlainObject } from '@liveblocks/core';
3
+ import { useUser, useRoom, useOther, useUpdateMyPresence, useOthersConnectionIds } from '@liveblocks/react';
4
+ import { useLayoutEffect } from '@liveblocks/react/_private';
5
+ import { Cursor } from '@liveblocks/react-ui';
6
+ import { makeCursorSpring, useStableComponent, cn } from '@liveblocks/react-ui/_private';
7
+ import { useStoreApi, useReactFlow, useStore } from '@xyflow/react';
8
+ import { useRef, forwardRef, useCallback, useEffect } from 'react';
9
+
10
+ const DEFAULT_PRESENCE_KEY = "cursor";
11
+ function $string(value) {
12
+ return typeof value === "string" ? value : void 0;
13
+ }
14
+ function $coordinates(value) {
15
+ if (isPlainObject(value) && typeof value.x === "number" && typeof value.y === "number") {
16
+ return value;
17
+ }
18
+ return void 0;
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
+ }
29
+ function PresenceCursor({
30
+ connectionId,
31
+ presenceKey,
32
+ Cursor: Cursor$1
33
+ }) {
34
+ const room = useRoom();
35
+ const cursorRef = useRef(null);
36
+ const reactFlowStoreApi = useStoreApi();
37
+ const userId = useOther(connectionId, (other) => $string(other.id));
38
+ useLayoutEffect(() => {
39
+ const spring = makeCursorSpring();
40
+ function update() {
41
+ const element = cursorRef.current;
42
+ const coordinates = spring.get();
43
+ const [panX, panY, zoom] = reactFlowStoreApi.getState().transform;
44
+ if (!element) {
45
+ return;
46
+ }
47
+ if (coordinates === null) {
48
+ element.style.transform = "translate3d(0, 0, 0)";
49
+ element.style.display = "none";
50
+ return;
51
+ }
52
+ element.style.transform = `translate3d(${coordinates.x * zoom + panX}px, ${coordinates.y * zoom + panY}px, 0)`;
53
+ element.style.display = "";
54
+ }
55
+ update();
56
+ const unsubscribeSpring = spring.subscribe(update);
57
+ const unsubscribeTransform = reactFlowStoreApi.subscribe(
58
+ (state, previousState) => {
59
+ if (state.transform !== previousState.transform) {
60
+ update();
61
+ }
62
+ }
63
+ );
64
+ const unsubscribeOther = room.events.others.subscribe(({ others }) => {
65
+ const other = others.find((other2) => other2.connectionId === connectionId);
66
+ const cursor = $coordinates(other?.presence[presenceKey]);
67
+ spring.set(cursor ?? null);
68
+ });
69
+ return () => {
70
+ spring.dispose();
71
+ unsubscribeSpring();
72
+ unsubscribeTransform();
73
+ unsubscribeOther();
74
+ };
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, {}) });
77
+ }
78
+ const Cursors = forwardRef(
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
+ );
90
+ const reactFlow = useReactFlow();
91
+ const updateMyPresence = useUpdateMyPresence();
92
+ const othersConnectionIds = useOthersConnectionIds();
93
+ const reactFlowDomNode = useStore((state) => state.domNode);
94
+ const reactFlowStoreApi = useStoreApi();
95
+ const handlePointerMove = useCallback(
96
+ (event) => {
97
+ const isPanning = reactFlowStoreApi.getState().paneDragging;
98
+ if (isPanning) {
99
+ return;
100
+ }
101
+ updateMyPresence({
102
+ [presenceKey]: reactFlow.screenToFlowPosition({
103
+ x: event.clientX,
104
+ y: event.clientY
105
+ })
106
+ });
107
+ },
108
+ [updateMyPresence, presenceKey, reactFlow, reactFlowStoreApi]
109
+ );
110
+ const handlePointerLeave = useCallback(() => {
111
+ updateMyPresence({ [presenceKey]: null });
112
+ }, [updateMyPresence, presenceKey]);
113
+ useEffect(() => {
114
+ if (!reactFlowDomNode) {
115
+ return;
116
+ }
117
+ reactFlowDomNode.addEventListener("pointermove", handlePointerMove);
118
+ reactFlowDomNode.addEventListener("pointerleave", handlePointerLeave);
119
+ window.addEventListener("blur", handlePointerLeave);
120
+ return () => {
121
+ reactFlowDomNode.removeEventListener("pointermove", handlePointerMove);
122
+ reactFlowDomNode.removeEventListener(
123
+ "pointerleave",
124
+ handlePointerLeave
125
+ );
126
+ window.removeEventListener("blur", handlePointerLeave);
127
+ handlePointerLeave();
128
+ };
129
+ }, [reactFlowDomNode, handlePointerMove, handlePointerLeave]);
130
+ return /* @__PURE__ */ jsxs(
131
+ "div",
132
+ {
133
+ "aria-hidden": true,
134
+ className: cn("lb-root lb-react-flow-cursors", className),
135
+ ...props,
136
+ ref: forwardedRef,
137
+ children: [
138
+ othersConnectionIds.map((connectionId) => /* @__PURE__ */ jsx(
139
+ PresenceCursor,
140
+ {
141
+ connectionId,
142
+ presenceKey,
143
+ Cursor
144
+ },
145
+ connectionId
146
+ )),
147
+ children
148
+ ]
149
+ }
150
+ );
151
+ }
152
+ );
153
+
154
+ export { Cursors };
155
+ //# sourceMappingURL=cursors.js.map
@@ -0,0 +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 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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"}