@liveblocks/react-lexical 2.2.2 → 2.2.3-alpha2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/comments/anchored-threads.js +245 -0
- package/dist/comments/anchored-threads.js.map +1 -0
- package/dist/comments/anchored-threads.mjs +242 -0
- package/dist/comments/anchored-threads.mjs.map +1 -0
- package/dist/comments/comment-plugin-provider.js +11 -6
- package/dist/comments/comment-plugin-provider.js.map +1 -1
- package/dist/comments/comment-plugin-provider.mjs +12 -8
- package/dist/comments/comment-plugin-provider.mjs.map +1 -1
- package/dist/comments/floating-composer.js +2 -2
- package/dist/comments/floating-composer.js.map +1 -1
- package/dist/comments/floating-composer.mjs +2 -2
- package/dist/comments/floating-composer.mjs.map +1 -1
- package/dist/comments/floating-threads.js +219 -0
- package/dist/comments/floating-threads.js.map +1 -0
- package/dist/comments/floating-threads.mjs +216 -0
- package/dist/comments/floating-threads.mjs.map +1 -0
- package/dist/index.d.mts +34 -4
- package/dist/index.d.ts +34 -4
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -1
- package/dist/liveblocks-plugin-provider.js +1 -0
- package/dist/liveblocks-plugin-provider.js.map +1 -1
- package/dist/liveblocks-plugin-provider.mjs +1 -1
- package/dist/liveblocks-plugin-provider.mjs.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/version.mjs +1 -1
- package/dist/version.mjs.map +1 -1
- package/package.json +6 -6
- package/src/styles/constants.css +8 -0
- package/src/styles/index.css +76 -13
- package/styles.css +1 -1
- package/styles.css.map +1 -1
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var LexicalComposerContext = require('@lexical/react/LexicalComposerContext');
|
|
4
|
+
var reactUi = require('@liveblocks/react-ui');
|
|
5
|
+
var lexical = require('lexical');
|
|
6
|
+
var React = require('react');
|
|
7
|
+
var classnames = require('../classnames.js');
|
|
8
|
+
var liveblocksPluginProvider = require('../liveblocks-plugin-provider.js');
|
|
9
|
+
var commentPluginProvider = require('./comment-plugin-provider.js');
|
|
10
|
+
var threadMarkNode = require('./thread-mark-node.js');
|
|
11
|
+
|
|
12
|
+
const DEFAULT_GAP = 20;
|
|
13
|
+
const DEFAULT_ACTIVE_THREAD_OFFSET = -12;
|
|
14
|
+
const GAP = `var(--lb-lexical-anchored-threads-gap, ${DEFAULT_GAP}px)`;
|
|
15
|
+
const ACTIVE_THREAD_OFFSET = `var(--lb-lexical-anchored-threads-active-thread-offset, ${DEFAULT_ACTIVE_THREAD_OFFSET}px)`;
|
|
16
|
+
function compareNodes(a, b) {
|
|
17
|
+
const position = a.compareDocumentPosition(b);
|
|
18
|
+
if (position & Node.DOCUMENT_POSITION_FOLLOWING)
|
|
19
|
+
return -1;
|
|
20
|
+
if (position & Node.DOCUMENT_POSITION_PRECEDING)
|
|
21
|
+
return 1;
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
function AnchoredThreads({
|
|
25
|
+
threads,
|
|
26
|
+
components,
|
|
27
|
+
className,
|
|
28
|
+
style,
|
|
29
|
+
...divProps
|
|
30
|
+
}) {
|
|
31
|
+
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
32
|
+
const Thread = components?.Thread ?? reactUi.Thread;
|
|
33
|
+
const containerRef = React.useRef(null);
|
|
34
|
+
const activeThreads = useActiveThreads();
|
|
35
|
+
const nodes = useThreadToNodes();
|
|
36
|
+
const getOrderedThreads = React.useCallback(() => {
|
|
37
|
+
return threads.map((thread) => {
|
|
38
|
+
const keys = nodes.get(thread.id);
|
|
39
|
+
if (keys === void 0 || keys.size === 0)
|
|
40
|
+
return null;
|
|
41
|
+
const elements2 = Array.from(keys.values()).map((key) => editor.getElementByKey(key)).filter(Boolean);
|
|
42
|
+
if (elements2.length === 0)
|
|
43
|
+
return null;
|
|
44
|
+
const element = elements2.sort(compareNodes)[0];
|
|
45
|
+
return {
|
|
46
|
+
thread,
|
|
47
|
+
element
|
|
48
|
+
};
|
|
49
|
+
}).filter(
|
|
50
|
+
(entry) => entry !== null
|
|
51
|
+
).sort((a, b) => {
|
|
52
|
+
return compareNodes(a.element, b.element);
|
|
53
|
+
});
|
|
54
|
+
}, [editor, threads, nodes]);
|
|
55
|
+
const orderedThreads = React.useMemo(getOrderedThreads, [getOrderedThreads]);
|
|
56
|
+
const [elements, setElements] = React.useState(/* @__PURE__ */ new Map());
|
|
57
|
+
const [positions, setPositions] = React.useState(/* @__PURE__ */ new Map());
|
|
58
|
+
const onItemAdd = React.useCallback((id, el) => {
|
|
59
|
+
setElements((prev) => new Map(prev).set(id, el));
|
|
60
|
+
}, []);
|
|
61
|
+
const onItemRemove = React.useCallback((id) => {
|
|
62
|
+
setElements((prev) => {
|
|
63
|
+
const items = new Map(prev);
|
|
64
|
+
items.delete(id);
|
|
65
|
+
return items;
|
|
66
|
+
});
|
|
67
|
+
}, []);
|
|
68
|
+
const handlePositionThreads = React.useCallback(() => {
|
|
69
|
+
const container = containerRef.current;
|
|
70
|
+
if (container === null)
|
|
71
|
+
return;
|
|
72
|
+
const orderedThreads2 = getOrderedThreads();
|
|
73
|
+
function getAscendingThreads() {
|
|
74
|
+
if (activeThreads.length === 0)
|
|
75
|
+
return orderedThreads2;
|
|
76
|
+
const active = orderedThreads2.filter(
|
|
77
|
+
({ thread }) => activeThreads.includes(thread.id)
|
|
78
|
+
);
|
|
79
|
+
const after = orderedThreads2.filter(({ thread, element }) => {
|
|
80
|
+
if (activeThreads.includes(thread.id))
|
|
81
|
+
return false;
|
|
82
|
+
const isAfter = active.some(({ element: activeElement }) => {
|
|
83
|
+
return compareNodes(activeElement, element) === -1;
|
|
84
|
+
});
|
|
85
|
+
return isAfter;
|
|
86
|
+
});
|
|
87
|
+
return active.concat(after);
|
|
88
|
+
}
|
|
89
|
+
const ascending = getAscendingThreads();
|
|
90
|
+
const descending = orderedThreads2.filter(
|
|
91
|
+
(entry) => !ascending.includes(entry)
|
|
92
|
+
);
|
|
93
|
+
const newPositions = /* @__PURE__ */ new Map();
|
|
94
|
+
for (const { thread, element } of ascending) {
|
|
95
|
+
const rect = element.getBoundingClientRect();
|
|
96
|
+
let top = rect.top - container.getBoundingClientRect().top;
|
|
97
|
+
for (const [id, position] of newPositions) {
|
|
98
|
+
const el = elements.get(id);
|
|
99
|
+
if (el === void 0)
|
|
100
|
+
continue;
|
|
101
|
+
if (top >= position && top <= position + el.getBoundingClientRect().height) {
|
|
102
|
+
top = position + el.getBoundingClientRect().height;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
newPositions.set(thread.id, top);
|
|
106
|
+
}
|
|
107
|
+
for (const { thread, element } of descending.reverse()) {
|
|
108
|
+
const rect = element.getBoundingClientRect();
|
|
109
|
+
const el = elements.get(thread.id);
|
|
110
|
+
if (el === void 0)
|
|
111
|
+
continue;
|
|
112
|
+
let top = rect.top - container.getBoundingClientRect().top;
|
|
113
|
+
for (const [, position] of newPositions) {
|
|
114
|
+
if (top >= position - el.getBoundingClientRect().height) {
|
|
115
|
+
top = position - el.getBoundingClientRect().height;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
newPositions.set(thread.id, top);
|
|
119
|
+
}
|
|
120
|
+
setPositions(newPositions);
|
|
121
|
+
}, [getOrderedThreads, activeThreads, elements]);
|
|
122
|
+
React.useLayoutEffect(() => {
|
|
123
|
+
handlePositionThreads();
|
|
124
|
+
}, [handlePositionThreads]);
|
|
125
|
+
React.useEffect(() => {
|
|
126
|
+
return editor.registerUpdateListener(() => {
|
|
127
|
+
handlePositionThreads();
|
|
128
|
+
});
|
|
129
|
+
}, [editor, handlePositionThreads]);
|
|
130
|
+
React.useEffect(() => {
|
|
131
|
+
const observer = new ResizeObserver(handlePositionThreads);
|
|
132
|
+
for (const element of elements.values()) {
|
|
133
|
+
observer.observe(element);
|
|
134
|
+
}
|
|
135
|
+
return () => observer.disconnect();
|
|
136
|
+
}, [elements, handlePositionThreads]);
|
|
137
|
+
const root = liveblocksPluginProvider.useRootElement();
|
|
138
|
+
React.useEffect(() => {
|
|
139
|
+
if (root === null)
|
|
140
|
+
return;
|
|
141
|
+
const observer = new ResizeObserver(handlePositionThreads);
|
|
142
|
+
observer.observe(root);
|
|
143
|
+
return () => observer.disconnect();
|
|
144
|
+
}, [root, handlePositionThreads]);
|
|
145
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
146
|
+
...divProps,
|
|
147
|
+
className: classnames.classNames(className, "lb-root lb-lexical-anchored-threads"),
|
|
148
|
+
ref: containerRef,
|
|
149
|
+
style: {
|
|
150
|
+
position: "relative",
|
|
151
|
+
...style
|
|
152
|
+
}
|
|
153
|
+
}, orderedThreads.map(({ thread, element }) => {
|
|
154
|
+
const rect = element.getBoundingClientRect();
|
|
155
|
+
let top = rect.top;
|
|
156
|
+
if (positions.has(thread.id)) {
|
|
157
|
+
top = positions.get(thread.id);
|
|
158
|
+
}
|
|
159
|
+
const isActive = activeThreads.includes(thread.id);
|
|
160
|
+
return /* @__PURE__ */ React.createElement(ThreadWrapper, {
|
|
161
|
+
key: thread.id,
|
|
162
|
+
Thread,
|
|
163
|
+
thread,
|
|
164
|
+
onItemAdd,
|
|
165
|
+
onItemRemove,
|
|
166
|
+
style: {
|
|
167
|
+
position: "absolute",
|
|
168
|
+
transform: `translate3d(${isActive ? ACTIVE_THREAD_OFFSET : 0}, ${top}px, 0)`,
|
|
169
|
+
insetInlineStart: 0,
|
|
170
|
+
inlineSize: "100%",
|
|
171
|
+
paddingBlockEnd: GAP
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
function ThreadWrapper({
|
|
177
|
+
onItemAdd,
|
|
178
|
+
onItemRemove,
|
|
179
|
+
thread,
|
|
180
|
+
Thread,
|
|
181
|
+
className,
|
|
182
|
+
...divProps
|
|
183
|
+
}) {
|
|
184
|
+
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
185
|
+
const nodes = useThreadToNodes();
|
|
186
|
+
const activeThreads = useActiveThreads();
|
|
187
|
+
const isActive = activeThreads.includes(thread.id);
|
|
188
|
+
function handleThreadClick() {
|
|
189
|
+
const keys = nodes.get(thread.id);
|
|
190
|
+
if (keys === void 0 || keys.size === 0)
|
|
191
|
+
return;
|
|
192
|
+
if (activeThreads.includes(thread.id))
|
|
193
|
+
return;
|
|
194
|
+
editor.update(() => {
|
|
195
|
+
const [key] = keys;
|
|
196
|
+
const node = lexical.$getNodeByKey(key);
|
|
197
|
+
if (!threadMarkNode.$isThreadMarkNode(node))
|
|
198
|
+
return;
|
|
199
|
+
node.selectStart();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
const handleRef = React.useCallback(
|
|
203
|
+
(el) => {
|
|
204
|
+
onItemAdd(thread.id, el);
|
|
205
|
+
return () => onItemRemove(thread.id);
|
|
206
|
+
},
|
|
207
|
+
[thread.id, onItemAdd, onItemRemove]
|
|
208
|
+
);
|
|
209
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
210
|
+
ref: handleRef,
|
|
211
|
+
className: classnames.classNames(
|
|
212
|
+
"lb-lexical-anchored-threads-thread-container",
|
|
213
|
+
className
|
|
214
|
+
),
|
|
215
|
+
...divProps
|
|
216
|
+
}, /* @__PURE__ */ React.createElement(Thread, {
|
|
217
|
+
thread,
|
|
218
|
+
"data-state": isActive ? "active" : "inactive",
|
|
219
|
+
onClick: handleThreadClick,
|
|
220
|
+
className: "lb-lexical-anchored-threads-thread",
|
|
221
|
+
showComposer: isActive ? true : false
|
|
222
|
+
}));
|
|
223
|
+
}
|
|
224
|
+
function useThreadToNodes() {
|
|
225
|
+
const threadToNodes = React.useContext(commentPluginProvider.ThreadToNodesContext);
|
|
226
|
+
if (threadToNodes === null) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
"AnchoredThreads component must be used within a LiveblocksPlugin component."
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
return threadToNodes;
|
|
232
|
+
}
|
|
233
|
+
function useActiveThreads() {
|
|
234
|
+
const activeThreads = React.useContext(commentPluginProvider.ActiveThreadsContext);
|
|
235
|
+
if (activeThreads === null) {
|
|
236
|
+
throw new Error(
|
|
237
|
+
"AnchoredThreads component must be used within LiveblocksPlugin."
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
return activeThreads;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
exports.AnchoredThreads = AnchoredThreads;
|
|
244
|
+
exports.compareNodes = compareNodes;
|
|
245
|
+
//# sourceMappingURL=anchored-threads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchored-threads.js","sources":["../../src/comments/anchored-threads.tsx"],"sourcesContent":["import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport type { BaseMetadata, DM, ThreadData } from \"@liveblocks/core\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport { $getNodeByKey } from \"lexical\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport React, {\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { classNames } from \"../classnames\";\nimport { useRootElement } from \"../liveblocks-plugin-provider\";\nimport {\n ActiveThreadsContext,\n type ThreadToNodesMap,\n} from \"./comment-plugin-provider\";\nimport { ThreadToNodesContext } from \"./comment-plugin-provider\";\nimport { $isThreadMarkNode } from \"./thread-mark-node\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\nconst GAP = `var(--lb-lexical-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-lexical-anchored-threads-active-thread-offset, ${DEFAULT_ACTIVE_THREAD_OFFSET}px)`;\n\ntype AnchoredThreadsComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface AnchoredThreadsProps<M extends BaseMetadata = DM>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<M>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<AnchoredThreadsComponents>;\n}\n\n/**\n * Compares two nodes based on their position in the DOM.\n * Returns -1 if a comes before b, 1 if a comes after b, and 0 if they are the same node.\n * @param a The first node to compare\n * @param b The second node to compare\n * @returns -1 if a comes before b, 1 if a comes after b, and 0 if they are the same node.\n */\nexport function compareNodes(a: Node, b: Node): number {\n // Calculate the position of node 'b' relative to node 'a'\n const position = a.compareDocumentPosition(b);\n if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;\n if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;\n return 0;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n ...divProps\n}: AnchoredThreadsProps) {\n const [editor] = useLexicalComposerContext();\n const Thread = components?.Thread ?? DefaultThread;\n const containerRef = useRef<HTMLDivElement>(null);\n\n const activeThreads = useActiveThreads();\n\n const nodes = useThreadToNodes(); // A map of thread ids to a set of thread mark nodes associated with the thread\n\n const getOrderedThreads = useCallback(() => {\n return threads\n .map((thread) => {\n const keys = nodes.get(thread.id);\n if (keys === undefined || keys.size === 0) return null;\n\n const elements = Array.from(keys.values())\n .map((key) => editor.getElementByKey(key))\n .filter(Boolean) as HTMLElement[];\n if (elements.length === 0) return null;\n\n const element = elements.sort(compareNodes)[0];\n return {\n thread,\n element,\n };\n })\n .filter(\n (entry): entry is { thread: ThreadData; element: HTMLElement } =>\n entry !== null\n )\n .sort((a, b) => {\n return compareNodes(a.element, b.element);\n });\n }, [editor, threads, nodes]);\n\n // Sort threads by the position of the first element associated with the thread in the document (top to bottom, left to right)\n const orderedThreads = useMemo(getOrderedThreads, [getOrderedThreads]);\n\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\n\n const [positions, setPositions] = useState<Map<string, number>>(new Map()); // A map of thread ids to their 'top' position in the document\n\n const onItemAdd = useCallback((id: string, el: HTMLElement) => {\n setElements((prev) => new Map(prev).set(id, el));\n }, []);\n\n const onItemRemove = useCallback((id: string) => {\n setElements((prev) => {\n const items = new Map(prev);\n items.delete(id);\n return items;\n });\n }, []);\n\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (container === null) return;\n\n const orderedThreads = getOrderedThreads();\n\n // Returns an array of threads that should be positioned in ascending order - this includes threads that are active and threads that should come after the active threads\n function getAscendingThreads() {\n // If there are no active threads, all threads are ordered in ascending manner.\n if (activeThreads.length === 0) return orderedThreads;\n\n // Filter threads that are active\n const active = orderedThreads.filter(({ thread }) =>\n activeThreads.includes(thread.id)\n );\n\n // Filter threads that should come after the active threads\n const after = orderedThreads.filter(({ thread, element }) => {\n if (activeThreads.includes(thread.id)) return false;\n\n // Check if the current thread comes after any of the active threads\n const isAfter = active.some(({ element: activeElement }) => {\n return compareNodes(activeElement, element) === -1;\n });\n\n return isAfter;\n });\n\n return active.concat(after);\n }\n\n const ascending = getAscendingThreads();\n\n // Filter threads that are neither active nor come after active threads (i.e. 'other' threads)\n const descending = orderedThreads.filter(\n (entry) => !ascending.includes(entry)\n );\n\n const newPositions = new Map<string, number>();\n\n // Iterate over each thread and calculate its new position by taking into account the position of the previously positioned threads\n for (const { thread, element } of ascending) {\n const rect = element.getBoundingClientRect();\n let top = rect.top - container.getBoundingClientRect().top;\n\n for (const [id, position] of newPositions) {\n // Retrieve the element associated with the thread\n const el = elements.get(id);\n if (el === undefined) continue;\n\n if (\n top >= position &&\n top <= position + el.getBoundingClientRect().height\n ) {\n top = position + el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n for (const { thread, element } of descending.reverse()) {\n const rect = element.getBoundingClientRect();\n // Retrieve the element associated with the current thread\n const el = elements.get(thread.id);\n if (el === undefined) continue;\n\n let top = rect.top - container.getBoundingClientRect().top;\n for (const [, position] of newPositions) {\n if (top >= position - el.getBoundingClientRect().height) {\n top = position - el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n setPositions(newPositions);\n }, [getOrderedThreads, activeThreads, elements]);\n\n useLayoutEffect(() => {\n handlePositionThreads();\n }, [handlePositionThreads]);\n\n useEffect(() => {\n return editor.registerUpdateListener(() => {\n handlePositionThreads();\n });\n }, [editor, handlePositionThreads]);\n\n useEffect(() => {\n const observer = new ResizeObserver(handlePositionThreads);\n for (const element of elements.values()) {\n observer.observe(element);\n }\n\n return () => observer.disconnect();\n }, [elements, handlePositionThreads]);\n\n const root = useRootElement();\n\n useEffect(() => {\n if (root === null) return;\n const observer = new ResizeObserver(handlePositionThreads);\n\n observer.observe(root);\n return () => observer.disconnect();\n }, [root, handlePositionThreads]);\n\n return (\n <div\n {...divProps}\n className={classNames(className, \"lb-root lb-lexical-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, element }) => {\n const rect = element.getBoundingClientRect();\n let top = rect.top;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = activeThreads.includes(thread.id);\n\n return (\n <ThreadWrapper\n key={thread.id}\n Thread={Thread}\n thread={thread}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n style={{\n position: \"absolute\",\n transform: `translate3d(${isActive ? ACTIVE_THREAD_OFFSET : 0}, ${top}px, 0)`,\n insetInlineStart: 0,\n inlineSize: \"100%\",\n paddingBlockEnd: GAP,\n }}\n />\n );\n })}\n </div>\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n Thread: ComponentType<ThreadProps>;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n}\n\nfunction ThreadWrapper({\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n ...divProps\n}: ThreadWrapperProps) {\n const [editor] = useLexicalComposerContext();\n const nodes = useThreadToNodes();\n\n const activeThreads = useActiveThreads();\n\n const isActive = activeThreads.includes(thread.id);\n\n function handleThreadClick() {\n const keys = nodes.get(thread.id);\n if (keys === undefined || keys.size === 0) return;\n\n if (activeThreads.includes(thread.id)) return;\n\n editor.update(() => {\n const [key] = keys;\n const node = $getNodeByKey(key);\n if (!$isThreadMarkNode(node)) return;\n node.selectStart();\n });\n }\n\n const handleRef = useCallback(\n (el: HTMLDivElement) => {\n onItemAdd(thread.id, el);\n return () => onItemRemove(thread.id);\n },\n [thread.id, onItemAdd, onItemRemove]\n );\n\n return (\n <div\n ref={handleRef}\n className={classNames(\n \"lb-lexical-anchored-threads-thread-container\",\n className\n )}\n {...divProps}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-lexical-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n\nfunction useThreadToNodes(): ThreadToNodesMap {\n const threadToNodes = useContext(ThreadToNodesContext);\n if (threadToNodes === null) {\n throw new Error(\n \"AnchoredThreads component must be used within a LiveblocksPlugin component.\"\n );\n }\n return threadToNodes;\n}\n\nfunction useActiveThreads() {\n const activeThreads = useContext(ActiveThreadsContext);\n if (activeThreads === null) {\n throw new Error(\n \"AnchoredThreads component must be used within LiveblocksPlugin.\"\n );\n }\n\n return activeThreads;\n}\n"],"names":["useLexicalComposerContext","DefaultThread","useRef","useCallback","elements","useMemo","useState","orderedThreads","useLayoutEffect","useEffect","useRootElement","classNames","$getNodeByKey","$isThreadMarkNode","useContext","ThreadToNodesContext","ActiveThreadsContext"],"mappings":"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"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
2
|
+
import { Thread } from '@liveblocks/react-ui';
|
|
3
|
+
import { $getNodeByKey } from 'lexical';
|
|
4
|
+
import React__default, { useRef, useCallback, useMemo, useState, useLayoutEffect, useEffect, useContext } from 'react';
|
|
5
|
+
import { classNames } from '../classnames.mjs';
|
|
6
|
+
import { useRootElement } from '../liveblocks-plugin-provider.mjs';
|
|
7
|
+
import { ThreadToNodesContext, ActiveThreadsContext } from './comment-plugin-provider.mjs';
|
|
8
|
+
import { $isThreadMarkNode } from './thread-mark-node.mjs';
|
|
9
|
+
|
|
10
|
+
const DEFAULT_GAP = 20;
|
|
11
|
+
const DEFAULT_ACTIVE_THREAD_OFFSET = -12;
|
|
12
|
+
const GAP = `var(--lb-lexical-anchored-threads-gap, ${DEFAULT_GAP}px)`;
|
|
13
|
+
const ACTIVE_THREAD_OFFSET = `var(--lb-lexical-anchored-threads-active-thread-offset, ${DEFAULT_ACTIVE_THREAD_OFFSET}px)`;
|
|
14
|
+
function compareNodes(a, b) {
|
|
15
|
+
const position = a.compareDocumentPosition(b);
|
|
16
|
+
if (position & Node.DOCUMENT_POSITION_FOLLOWING)
|
|
17
|
+
return -1;
|
|
18
|
+
if (position & Node.DOCUMENT_POSITION_PRECEDING)
|
|
19
|
+
return 1;
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
function AnchoredThreads({
|
|
23
|
+
threads,
|
|
24
|
+
components,
|
|
25
|
+
className,
|
|
26
|
+
style,
|
|
27
|
+
...divProps
|
|
28
|
+
}) {
|
|
29
|
+
const [editor] = useLexicalComposerContext();
|
|
30
|
+
const Thread$1 = components?.Thread ?? Thread;
|
|
31
|
+
const containerRef = useRef(null);
|
|
32
|
+
const activeThreads = useActiveThreads();
|
|
33
|
+
const nodes = useThreadToNodes();
|
|
34
|
+
const getOrderedThreads = useCallback(() => {
|
|
35
|
+
return threads.map((thread) => {
|
|
36
|
+
const keys = nodes.get(thread.id);
|
|
37
|
+
if (keys === void 0 || keys.size === 0)
|
|
38
|
+
return null;
|
|
39
|
+
const elements2 = Array.from(keys.values()).map((key) => editor.getElementByKey(key)).filter(Boolean);
|
|
40
|
+
if (elements2.length === 0)
|
|
41
|
+
return null;
|
|
42
|
+
const element = elements2.sort(compareNodes)[0];
|
|
43
|
+
return {
|
|
44
|
+
thread,
|
|
45
|
+
element
|
|
46
|
+
};
|
|
47
|
+
}).filter(
|
|
48
|
+
(entry) => entry !== null
|
|
49
|
+
).sort((a, b) => {
|
|
50
|
+
return compareNodes(a.element, b.element);
|
|
51
|
+
});
|
|
52
|
+
}, [editor, threads, nodes]);
|
|
53
|
+
const orderedThreads = useMemo(getOrderedThreads, [getOrderedThreads]);
|
|
54
|
+
const [elements, setElements] = useState(/* @__PURE__ */ new Map());
|
|
55
|
+
const [positions, setPositions] = useState(/* @__PURE__ */ new Map());
|
|
56
|
+
const onItemAdd = useCallback((id, el) => {
|
|
57
|
+
setElements((prev) => new Map(prev).set(id, el));
|
|
58
|
+
}, []);
|
|
59
|
+
const onItemRemove = useCallback((id) => {
|
|
60
|
+
setElements((prev) => {
|
|
61
|
+
const items = new Map(prev);
|
|
62
|
+
items.delete(id);
|
|
63
|
+
return items;
|
|
64
|
+
});
|
|
65
|
+
}, []);
|
|
66
|
+
const handlePositionThreads = useCallback(() => {
|
|
67
|
+
const container = containerRef.current;
|
|
68
|
+
if (container === null)
|
|
69
|
+
return;
|
|
70
|
+
const orderedThreads2 = getOrderedThreads();
|
|
71
|
+
function getAscendingThreads() {
|
|
72
|
+
if (activeThreads.length === 0)
|
|
73
|
+
return orderedThreads2;
|
|
74
|
+
const active = orderedThreads2.filter(
|
|
75
|
+
({ thread }) => activeThreads.includes(thread.id)
|
|
76
|
+
);
|
|
77
|
+
const after = orderedThreads2.filter(({ thread, element }) => {
|
|
78
|
+
if (activeThreads.includes(thread.id))
|
|
79
|
+
return false;
|
|
80
|
+
const isAfter = active.some(({ element: activeElement }) => {
|
|
81
|
+
return compareNodes(activeElement, element) === -1;
|
|
82
|
+
});
|
|
83
|
+
return isAfter;
|
|
84
|
+
});
|
|
85
|
+
return active.concat(after);
|
|
86
|
+
}
|
|
87
|
+
const ascending = getAscendingThreads();
|
|
88
|
+
const descending = orderedThreads2.filter(
|
|
89
|
+
(entry) => !ascending.includes(entry)
|
|
90
|
+
);
|
|
91
|
+
const newPositions = /* @__PURE__ */ new Map();
|
|
92
|
+
for (const { thread, element } of ascending) {
|
|
93
|
+
const rect = element.getBoundingClientRect();
|
|
94
|
+
let top = rect.top - container.getBoundingClientRect().top;
|
|
95
|
+
for (const [id, position] of newPositions) {
|
|
96
|
+
const el = elements.get(id);
|
|
97
|
+
if (el === void 0)
|
|
98
|
+
continue;
|
|
99
|
+
if (top >= position && top <= position + el.getBoundingClientRect().height) {
|
|
100
|
+
top = position + el.getBoundingClientRect().height;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
newPositions.set(thread.id, top);
|
|
104
|
+
}
|
|
105
|
+
for (const { thread, element } of descending.reverse()) {
|
|
106
|
+
const rect = element.getBoundingClientRect();
|
|
107
|
+
const el = elements.get(thread.id);
|
|
108
|
+
if (el === void 0)
|
|
109
|
+
continue;
|
|
110
|
+
let top = rect.top - container.getBoundingClientRect().top;
|
|
111
|
+
for (const [, position] of newPositions) {
|
|
112
|
+
if (top >= position - el.getBoundingClientRect().height) {
|
|
113
|
+
top = position - el.getBoundingClientRect().height;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
newPositions.set(thread.id, top);
|
|
117
|
+
}
|
|
118
|
+
setPositions(newPositions);
|
|
119
|
+
}, [getOrderedThreads, activeThreads, elements]);
|
|
120
|
+
useLayoutEffect(() => {
|
|
121
|
+
handlePositionThreads();
|
|
122
|
+
}, [handlePositionThreads]);
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
return editor.registerUpdateListener(() => {
|
|
125
|
+
handlePositionThreads();
|
|
126
|
+
});
|
|
127
|
+
}, [editor, handlePositionThreads]);
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
const observer = new ResizeObserver(handlePositionThreads);
|
|
130
|
+
for (const element of elements.values()) {
|
|
131
|
+
observer.observe(element);
|
|
132
|
+
}
|
|
133
|
+
return () => observer.disconnect();
|
|
134
|
+
}, [elements, handlePositionThreads]);
|
|
135
|
+
const root = useRootElement();
|
|
136
|
+
useEffect(() => {
|
|
137
|
+
if (root === null)
|
|
138
|
+
return;
|
|
139
|
+
const observer = new ResizeObserver(handlePositionThreads);
|
|
140
|
+
observer.observe(root);
|
|
141
|
+
return () => observer.disconnect();
|
|
142
|
+
}, [root, handlePositionThreads]);
|
|
143
|
+
return /* @__PURE__ */ React__default.createElement("div", {
|
|
144
|
+
...divProps,
|
|
145
|
+
className: classNames(className, "lb-root lb-lexical-anchored-threads"),
|
|
146
|
+
ref: containerRef,
|
|
147
|
+
style: {
|
|
148
|
+
position: "relative",
|
|
149
|
+
...style
|
|
150
|
+
}
|
|
151
|
+
}, orderedThreads.map(({ thread, element }) => {
|
|
152
|
+
const rect = element.getBoundingClientRect();
|
|
153
|
+
let top = rect.top;
|
|
154
|
+
if (positions.has(thread.id)) {
|
|
155
|
+
top = positions.get(thread.id);
|
|
156
|
+
}
|
|
157
|
+
const isActive = activeThreads.includes(thread.id);
|
|
158
|
+
return /* @__PURE__ */ React__default.createElement(ThreadWrapper, {
|
|
159
|
+
key: thread.id,
|
|
160
|
+
Thread: Thread$1,
|
|
161
|
+
thread,
|
|
162
|
+
onItemAdd,
|
|
163
|
+
onItemRemove,
|
|
164
|
+
style: {
|
|
165
|
+
position: "absolute",
|
|
166
|
+
transform: `translate3d(${isActive ? ACTIVE_THREAD_OFFSET : 0}, ${top}px, 0)`,
|
|
167
|
+
insetInlineStart: 0,
|
|
168
|
+
inlineSize: "100%",
|
|
169
|
+
paddingBlockEnd: GAP
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}));
|
|
173
|
+
}
|
|
174
|
+
function ThreadWrapper({
|
|
175
|
+
onItemAdd,
|
|
176
|
+
onItemRemove,
|
|
177
|
+
thread,
|
|
178
|
+
Thread,
|
|
179
|
+
className,
|
|
180
|
+
...divProps
|
|
181
|
+
}) {
|
|
182
|
+
const [editor] = useLexicalComposerContext();
|
|
183
|
+
const nodes = useThreadToNodes();
|
|
184
|
+
const activeThreads = useActiveThreads();
|
|
185
|
+
const isActive = activeThreads.includes(thread.id);
|
|
186
|
+
function handleThreadClick() {
|
|
187
|
+
const keys = nodes.get(thread.id);
|
|
188
|
+
if (keys === void 0 || keys.size === 0)
|
|
189
|
+
return;
|
|
190
|
+
if (activeThreads.includes(thread.id))
|
|
191
|
+
return;
|
|
192
|
+
editor.update(() => {
|
|
193
|
+
const [key] = keys;
|
|
194
|
+
const node = $getNodeByKey(key);
|
|
195
|
+
if (!$isThreadMarkNode(node))
|
|
196
|
+
return;
|
|
197
|
+
node.selectStart();
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
const handleRef = useCallback(
|
|
201
|
+
(el) => {
|
|
202
|
+
onItemAdd(thread.id, el);
|
|
203
|
+
return () => onItemRemove(thread.id);
|
|
204
|
+
},
|
|
205
|
+
[thread.id, onItemAdd, onItemRemove]
|
|
206
|
+
);
|
|
207
|
+
return /* @__PURE__ */ React__default.createElement("div", {
|
|
208
|
+
ref: handleRef,
|
|
209
|
+
className: classNames(
|
|
210
|
+
"lb-lexical-anchored-threads-thread-container",
|
|
211
|
+
className
|
|
212
|
+
),
|
|
213
|
+
...divProps
|
|
214
|
+
}, /* @__PURE__ */ React__default.createElement(Thread, {
|
|
215
|
+
thread,
|
|
216
|
+
"data-state": isActive ? "active" : "inactive",
|
|
217
|
+
onClick: handleThreadClick,
|
|
218
|
+
className: "lb-lexical-anchored-threads-thread",
|
|
219
|
+
showComposer: isActive ? true : false
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
222
|
+
function useThreadToNodes() {
|
|
223
|
+
const threadToNodes = useContext(ThreadToNodesContext);
|
|
224
|
+
if (threadToNodes === null) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
"AnchoredThreads component must be used within a LiveblocksPlugin component."
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
return threadToNodes;
|
|
230
|
+
}
|
|
231
|
+
function useActiveThreads() {
|
|
232
|
+
const activeThreads = useContext(ActiveThreadsContext);
|
|
233
|
+
if (activeThreads === null) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
"AnchoredThreads component must be used within LiveblocksPlugin."
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
return activeThreads;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export { AnchoredThreads, compareNodes };
|
|
242
|
+
//# sourceMappingURL=anchored-threads.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchored-threads.mjs","sources":["../../src/comments/anchored-threads.tsx"],"sourcesContent":["import { useLexicalComposerContext } from \"@lexical/react/LexicalComposerContext\";\nimport type { BaseMetadata, DM, ThreadData } from \"@liveblocks/core\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport { $getNodeByKey } from \"lexical\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport React, {\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { classNames } from \"../classnames\";\nimport { useRootElement } from \"../liveblocks-plugin-provider\";\nimport {\n ActiveThreadsContext,\n type ThreadToNodesMap,\n} from \"./comment-plugin-provider\";\nimport { ThreadToNodesContext } from \"./comment-plugin-provider\";\nimport { $isThreadMarkNode } from \"./thread-mark-node\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\nconst GAP = `var(--lb-lexical-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-lexical-anchored-threads-active-thread-offset, ${DEFAULT_ACTIVE_THREAD_OFFSET}px)`;\n\ntype AnchoredThreadsComponents = {\n Thread: ComponentType<ThreadProps>;\n};\n\nexport interface AnchoredThreadsProps<M extends BaseMetadata = DM>\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /**\n * The threads to display.\n */\n threads: ThreadData<M>[];\n\n /**\n * Override the component's components.\n */\n components?: Partial<AnchoredThreadsComponents>;\n}\n\n/**\n * Compares two nodes based on their position in the DOM.\n * Returns -1 if a comes before b, 1 if a comes after b, and 0 if they are the same node.\n * @param a The first node to compare\n * @param b The second node to compare\n * @returns -1 if a comes before b, 1 if a comes after b, and 0 if they are the same node.\n */\nexport function compareNodes(a: Node, b: Node): number {\n // Calculate the position of node 'b' relative to node 'a'\n const position = a.compareDocumentPosition(b);\n if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;\n if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;\n return 0;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n ...divProps\n}: AnchoredThreadsProps) {\n const [editor] = useLexicalComposerContext();\n const Thread = components?.Thread ?? DefaultThread;\n const containerRef = useRef<HTMLDivElement>(null);\n\n const activeThreads = useActiveThreads();\n\n const nodes = useThreadToNodes(); // A map of thread ids to a set of thread mark nodes associated with the thread\n\n const getOrderedThreads = useCallback(() => {\n return threads\n .map((thread) => {\n const keys = nodes.get(thread.id);\n if (keys === undefined || keys.size === 0) return null;\n\n const elements = Array.from(keys.values())\n .map((key) => editor.getElementByKey(key))\n .filter(Boolean) as HTMLElement[];\n if (elements.length === 0) return null;\n\n const element = elements.sort(compareNodes)[0];\n return {\n thread,\n element,\n };\n })\n .filter(\n (entry): entry is { thread: ThreadData; element: HTMLElement } =>\n entry !== null\n )\n .sort((a, b) => {\n return compareNodes(a.element, b.element);\n });\n }, [editor, threads, nodes]);\n\n // Sort threads by the position of the first element associated with the thread in the document (top to bottom, left to right)\n const orderedThreads = useMemo(getOrderedThreads, [getOrderedThreads]);\n\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\n\n const [positions, setPositions] = useState<Map<string, number>>(new Map()); // A map of thread ids to their 'top' position in the document\n\n const onItemAdd = useCallback((id: string, el: HTMLElement) => {\n setElements((prev) => new Map(prev).set(id, el));\n }, []);\n\n const onItemRemove = useCallback((id: string) => {\n setElements((prev) => {\n const items = new Map(prev);\n items.delete(id);\n return items;\n });\n }, []);\n\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (container === null) return;\n\n const orderedThreads = getOrderedThreads();\n\n // Returns an array of threads that should be positioned in ascending order - this includes threads that are active and threads that should come after the active threads\n function getAscendingThreads() {\n // If there are no active threads, all threads are ordered in ascending manner.\n if (activeThreads.length === 0) return orderedThreads;\n\n // Filter threads that are active\n const active = orderedThreads.filter(({ thread }) =>\n activeThreads.includes(thread.id)\n );\n\n // Filter threads that should come after the active threads\n const after = orderedThreads.filter(({ thread, element }) => {\n if (activeThreads.includes(thread.id)) return false;\n\n // Check if the current thread comes after any of the active threads\n const isAfter = active.some(({ element: activeElement }) => {\n return compareNodes(activeElement, element) === -1;\n });\n\n return isAfter;\n });\n\n return active.concat(after);\n }\n\n const ascending = getAscendingThreads();\n\n // Filter threads that are neither active nor come after active threads (i.e. 'other' threads)\n const descending = orderedThreads.filter(\n (entry) => !ascending.includes(entry)\n );\n\n const newPositions = new Map<string, number>();\n\n // Iterate over each thread and calculate its new position by taking into account the position of the previously positioned threads\n for (const { thread, element } of ascending) {\n const rect = element.getBoundingClientRect();\n let top = rect.top - container.getBoundingClientRect().top;\n\n for (const [id, position] of newPositions) {\n // Retrieve the element associated with the thread\n const el = elements.get(id);\n if (el === undefined) continue;\n\n if (\n top >= position &&\n top <= position + el.getBoundingClientRect().height\n ) {\n top = position + el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n for (const { thread, element } of descending.reverse()) {\n const rect = element.getBoundingClientRect();\n // Retrieve the element associated with the current thread\n const el = elements.get(thread.id);\n if (el === undefined) continue;\n\n let top = rect.top - container.getBoundingClientRect().top;\n for (const [, position] of newPositions) {\n if (top >= position - el.getBoundingClientRect().height) {\n top = position - el.getBoundingClientRect().height;\n }\n }\n\n newPositions.set(thread.id, top);\n }\n\n setPositions(newPositions);\n }, [getOrderedThreads, activeThreads, elements]);\n\n useLayoutEffect(() => {\n handlePositionThreads();\n }, [handlePositionThreads]);\n\n useEffect(() => {\n return editor.registerUpdateListener(() => {\n handlePositionThreads();\n });\n }, [editor, handlePositionThreads]);\n\n useEffect(() => {\n const observer = new ResizeObserver(handlePositionThreads);\n for (const element of elements.values()) {\n observer.observe(element);\n }\n\n return () => observer.disconnect();\n }, [elements, handlePositionThreads]);\n\n const root = useRootElement();\n\n useEffect(() => {\n if (root === null) return;\n const observer = new ResizeObserver(handlePositionThreads);\n\n observer.observe(root);\n return () => observer.disconnect();\n }, [root, handlePositionThreads]);\n\n return (\n <div\n {...divProps}\n className={classNames(className, \"lb-root lb-lexical-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, element }) => {\n const rect = element.getBoundingClientRect();\n let top = rect.top;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = activeThreads.includes(thread.id);\n\n return (\n <ThreadWrapper\n key={thread.id}\n Thread={Thread}\n thread={thread}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n style={{\n position: \"absolute\",\n transform: `translate3d(${isActive ? ACTIVE_THREAD_OFFSET : 0}, ${top}px, 0)`,\n insetInlineStart: 0,\n inlineSize: \"100%\",\n paddingBlockEnd: GAP,\n }}\n />\n );\n })}\n </div>\n );\n}\n\ninterface ThreadWrapperProps extends ThreadProps {\n Thread: ComponentType<ThreadProps>;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n}\n\nfunction ThreadWrapper({\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n ...divProps\n}: ThreadWrapperProps) {\n const [editor] = useLexicalComposerContext();\n const nodes = useThreadToNodes();\n\n const activeThreads = useActiveThreads();\n\n const isActive = activeThreads.includes(thread.id);\n\n function handleThreadClick() {\n const keys = nodes.get(thread.id);\n if (keys === undefined || keys.size === 0) return;\n\n if (activeThreads.includes(thread.id)) return;\n\n editor.update(() => {\n const [key] = keys;\n const node = $getNodeByKey(key);\n if (!$isThreadMarkNode(node)) return;\n node.selectStart();\n });\n }\n\n const handleRef = useCallback(\n (el: HTMLDivElement) => {\n onItemAdd(thread.id, el);\n return () => onItemRemove(thread.id);\n },\n [thread.id, onItemAdd, onItemRemove]\n );\n\n return (\n <div\n ref={handleRef}\n className={classNames(\n \"lb-lexical-anchored-threads-thread-container\",\n className\n )}\n {...divProps}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-lexical-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n\nfunction useThreadToNodes(): ThreadToNodesMap {\n const threadToNodes = useContext(ThreadToNodesContext);\n if (threadToNodes === null) {\n throw new Error(\n \"AnchoredThreads component must be used within a LiveblocksPlugin component.\"\n );\n }\n return threadToNodes;\n}\n\nfunction useActiveThreads() {\n const activeThreads = useContext(ActiveThreadsContext);\n if (activeThreads === null) {\n throw new Error(\n \"AnchoredThreads component must be used within LiveblocksPlugin.\"\n );\n }\n\n return activeThreads;\n}\n"],"names":["Thread","DefaultThread","elements","orderedThreads","React"],"mappings":"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"}
|
|
@@ -31,6 +31,7 @@ function _interopNamespaceDefault(e) {
|
|
|
31
31
|
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
32
32
|
|
|
33
33
|
const OnDeleteThreadCallback = React.createContext(null);
|
|
34
|
+
const ActiveThreadsContext = React.createContext(null);
|
|
34
35
|
const IsActiveThreadContext = React.createContext(null);
|
|
35
36
|
const ThreadToNodesContext = React.createContext(
|
|
36
37
|
null
|
|
@@ -79,15 +80,16 @@ function CommentPluginProvider({ children }) {
|
|
|
79
80
|
store.get,
|
|
80
81
|
store.get,
|
|
81
82
|
React.useCallback(
|
|
82
|
-
() => react.selectedThreads(room.id, store.get(), {}),
|
|
83
|
+
() => react.selectedThreads(room.id, store.get(), {}).map((thread) => thread.id),
|
|
83
84
|
[room.id, store]
|
|
84
|
-
)
|
|
85
|
+
),
|
|
86
|
+
core.shallow
|
|
85
87
|
);
|
|
86
88
|
React.useEffect(() => {
|
|
87
89
|
function getThreadMarkElements() {
|
|
88
90
|
const activeElements = /* @__PURE__ */ new Set();
|
|
89
|
-
for (const
|
|
90
|
-
const keys = threadToNodes.get(
|
|
91
|
+
for (const id of threads) {
|
|
92
|
+
const keys = threadToNodes.get(id);
|
|
91
93
|
if (keys === void 0)
|
|
92
94
|
continue;
|
|
93
95
|
for (const key of keys) {
|
|
@@ -101,7 +103,7 @@ function CommentPluginProvider({ children }) {
|
|
|
101
103
|
}
|
|
102
104
|
const elements = getThreadMarkElements();
|
|
103
105
|
const theme = context.getTheme();
|
|
104
|
-
const classNames = ["lb-root", "lb-thread-mark"];
|
|
106
|
+
const classNames = ["lb-root", "lb-lexical-thread-mark"];
|
|
105
107
|
if (theme && theme.liveblocks && "threadMark" in theme.liveblocks) {
|
|
106
108
|
classNames.push(theme.liveblocks.threadMark);
|
|
107
109
|
}
|
|
@@ -219,11 +221,13 @@ function CommentPluginProvider({ children }) {
|
|
|
219
221
|
}, [editor]);
|
|
220
222
|
return /* @__PURE__ */ React__namespace.createElement(OnDeleteThreadCallback.Provider, {
|
|
221
223
|
value: handleThreadDelete
|
|
224
|
+
}, /* @__PURE__ */ React__namespace.createElement(ActiveThreadsContext.Provider, {
|
|
225
|
+
value: activeThreads
|
|
222
226
|
}, /* @__PURE__ */ React__namespace.createElement(IsActiveThreadContext.Provider, {
|
|
223
227
|
value: isThreadActive
|
|
224
228
|
}, /* @__PURE__ */ React__namespace.createElement(ThreadToNodesContext.Provider, {
|
|
225
229
|
value: threadToNodes
|
|
226
|
-
}, children)));
|
|
230
|
+
}, children))));
|
|
227
231
|
}
|
|
228
232
|
function useIsThreadActive(threadId) {
|
|
229
233
|
const isActive = React__namespace.useContext(IsActiveThreadContext);
|
|
@@ -235,6 +239,7 @@ function useIsThreadActive(threadId) {
|
|
|
235
239
|
return isActive(threadId);
|
|
236
240
|
}
|
|
237
241
|
|
|
242
|
+
exports.ActiveThreadsContext = ActiveThreadsContext;
|
|
238
243
|
exports.CommentPluginProvider = CommentPluginProvider;
|
|
239
244
|
exports.IsActiveThreadContext = IsActiveThreadContext;
|
|
240
245
|
exports.OnDeleteThreadCallback = OnDeleteThreadCallback;
|