@liveblocks/react-tiptap 2.8.3-tiptap1 → 2.8.3-tiptap2
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/LiveblocksExtension.js +77 -15
- package/dist/LiveblocksExtension.js.map +1 -1
- package/dist/LiveblocksExtension.mjs +81 -19
- package/dist/LiveblocksExtension.mjs.map +1 -1
- package/dist/comments/AnchoredThreads.js +1 -1
- package/dist/comments/AnchoredThreads.js.map +1 -1
- package/dist/comments/AnchoredThreads.mjs +1 -1
- package/dist/comments/AnchoredThreads.mjs.map +1 -1
- package/dist/comments/CommentsExtension.js +18 -1
- package/dist/comments/CommentsExtension.js.map +1 -1
- package/dist/comments/CommentsExtension.mjs +18 -1
- package/dist/comments/CommentsExtension.mjs.map +1 -1
- package/dist/comments/FloatingComposer.js +36 -39
- package/dist/comments/FloatingComposer.js.map +1 -1
- package/dist/comments/FloatingComposer.mjs +37 -40
- package/dist/comments/FloatingComposer.mjs.map +1 -1
- package/dist/comments/FloatingThreads.js +29 -24
- package/dist/comments/FloatingThreads.js.map +1 -1
- package/dist/comments/FloatingThreads.mjs +29 -24
- package/dist/comments/FloatingThreads.mjs.map +1 -1
- package/dist/index.d.mts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/mentions/MentionExtension.js +1 -0
- package/dist/mentions/MentionExtension.js.map +1 -1
- package/dist/mentions/MentionExtension.mjs +1 -0
- package/dist/mentions/MentionExtension.mjs.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.mjs +1 -1
- package/package.json +6 -6
- package/src/styles/index.css +5 -2
- package/styles.css +1 -1
- package/styles.css.map +1 -1
|
@@ -10,6 +10,7 @@ var React = require('react');
|
|
|
10
10
|
var yjs = require('yjs');
|
|
11
11
|
var CommentsExtension = require('./comments/CommentsExtension.js');
|
|
12
12
|
var MentionExtension = require('./mentions/MentionExtension.js');
|
|
13
|
+
var types = require('./types.js');
|
|
13
14
|
|
|
14
15
|
const providersMap = /* @__PURE__ */ new Map();
|
|
15
16
|
const docMap = /* @__PURE__ */ new Map();
|
|
@@ -41,8 +42,19 @@ const LiveblocksCollab = Collaboration.extend({
|
|
|
41
42
|
});
|
|
42
43
|
const useLiveblocksExtension = () => {
|
|
43
44
|
const room = react.useRoom();
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
react.useCommentsErrorListener((error) => {
|
|
46
|
+
if (error instanceof react.CreateThreadError) {
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
const client = react.useClient();
|
|
50
|
+
const store = react.getUmbrellaStoreForClient(client);
|
|
51
|
+
const roomId = room.id;
|
|
52
|
+
const reportTextEditorType = React.useCallback(
|
|
53
|
+
(field) => {
|
|
54
|
+
room[core.kInternal].reportTextEditor(core.TextEditorType.TipTap, field);
|
|
55
|
+
},
|
|
56
|
+
[room]
|
|
57
|
+
);
|
|
46
58
|
const onCreateMention = React.useCallback(
|
|
47
59
|
(userId, notificationId) => {
|
|
48
60
|
try {
|
|
@@ -80,19 +92,70 @@ const useLiveblocksExtension = () => {
|
|
|
80
92
|
color: self.info.color
|
|
81
93
|
});
|
|
82
94
|
}
|
|
83
|
-
this.storage.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
this.storage.unsubs.push(
|
|
96
|
+
room.events.self.subscribe(({ info }) => {
|
|
97
|
+
if (!info) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const { name, color } = info;
|
|
101
|
+
const { user } = this.storage.provider.awareness.getLocalState();
|
|
102
|
+
if (name !== user?.name || color !== user?.color) {
|
|
103
|
+
this.editor.commands.updateUser({ name, color });
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
);
|
|
107
|
+
if (this.options.comments) {
|
|
108
|
+
const commentMarkType = core$1.getMarkType(
|
|
109
|
+
types.LIVEBLOCKS_COMMENT_MARK_TYPE,
|
|
110
|
+
this.editor.schema
|
|
111
|
+
);
|
|
112
|
+
this.storage.unsubs.push(
|
|
113
|
+
store.subscribeThreads(() => {
|
|
114
|
+
const threadMap = new Map(
|
|
115
|
+
react.selectThreads(store.getFullState(), {
|
|
116
|
+
roomId,
|
|
117
|
+
orderBy: "age",
|
|
118
|
+
query: {
|
|
119
|
+
resolved: false
|
|
120
|
+
}
|
|
121
|
+
}).map((thread) => [thread.id, true])
|
|
122
|
+
);
|
|
123
|
+
function isComment(mark) {
|
|
124
|
+
return mark.type.name === types.LIVEBLOCKS_COMMENT_MARK_TYPE;
|
|
125
|
+
}
|
|
126
|
+
this.editor.state.doc.descendants((node, pos) => {
|
|
127
|
+
node.marks.forEach((mark) => {
|
|
128
|
+
if (isComment(mark)) {
|
|
129
|
+
const markThreadId = mark.attrs.threadId;
|
|
130
|
+
const isOrphan = !threadMap.has(markThreadId);
|
|
131
|
+
if (isOrphan !== mark.attrs.orphan) {
|
|
132
|
+
const { tr } = this.editor.state;
|
|
133
|
+
const trimmedFrom = Math.max(pos, 0);
|
|
134
|
+
const trimmedTo = Math.min(
|
|
135
|
+
pos + node.nodeSize,
|
|
136
|
+
this.editor.state.doc.content.size - 1
|
|
137
|
+
);
|
|
138
|
+
tr.removeMark(trimmedFrom, trimmedTo, commentMarkType);
|
|
139
|
+
tr.addMark(
|
|
140
|
+
trimmedFrom,
|
|
141
|
+
trimmedTo,
|
|
142
|
+
commentMarkType.create({
|
|
143
|
+
...mark.attrs,
|
|
144
|
+
orphan: isOrphan
|
|
145
|
+
})
|
|
146
|
+
);
|
|
147
|
+
this.editor.view.dispatch(tr);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
})
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
reportTextEditorType(this.options.field ?? "default");
|
|
93
156
|
},
|
|
94
157
|
onDestroy() {
|
|
95
|
-
this.storage.unsub();
|
|
158
|
+
this.storage.unsubs.forEach((unsub) => unsub());
|
|
96
159
|
},
|
|
97
160
|
addStorage() {
|
|
98
161
|
if (!providersMap.has(room.id)) {
|
|
@@ -103,8 +166,7 @@ const useLiveblocksExtension = () => {
|
|
|
103
166
|
return {
|
|
104
167
|
doc: docMap.get(room.id),
|
|
105
168
|
provider: providersMap.get(room.id),
|
|
106
|
-
|
|
107
|
-
}
|
|
169
|
+
unsubs: []
|
|
108
170
|
};
|
|
109
171
|
},
|
|
110
172
|
addOptions() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveblocksExtension.js","sources":["../src/LiveblocksExtension.ts"],"sourcesContent":["import { type IUserInfo, kInternal } from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport { LiveblocksYjsProvider } from \"@liveblocks/yjs\";\nimport type { AnyExtension } from \"@tiptap/core\";\nimport { Extension } from \"@tiptap/core\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCursor from \"@tiptap/extension-collaboration-cursor\";\nimport { useCallback, useEffect } from \"react\";\nimport { Doc } from \"yjs\";\n\nimport { CommentsExtension } from \"./comments/CommentsExtension\";\nimport { MentionExtension } from \"./mentions/MentionExtension\";\n\nconst providersMap = new Map<\n string,\n LiveblocksYjsProvider<any, any, any, any, any>\n>();\n\nconst docMap = new Map<string, Doc>();\n\ntype LiveblocksExtensionOptions = {\n field?: string;\n comments: boolean; // | CommentsConfiguration\n mentions: boolean; // | MentionsConfiguration\n};\n\nconst LiveblocksCollab = Collaboration.extend({\n // Override the onCreate method to warn users about potential misconfigurations\n onCreate() {\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"doc\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap document extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n !this.editor.extensionManager.extensions.find(\n (e) => e.name === \"paragraph\"\n )\n ) {\n console.warn(\n \"[Liveblocks] The tiptap paragraph extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"text\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap text extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n this.editor.extensionManager.extensions.find((e) => e.name === \"history\")\n ) {\n console.warn(\n \"[Liveblocks] The history extension is enabled, Liveblocks extension provides its own. Please remove or disable the History plugin to prevent unwanted conflicts.\"\n );\n }\n },\n});\n\nexport const useLiveblocksExtension = (): Extension => {\n const room = useRoom();\n useEffect(() => {\n // Report that this is lexical and root is the rootKey\n // TODO: put this back in\n // room[kInternal].reportTextEditor(TextEditorType.TipTap, \"root\");\n }, [room]);\n\n const onCreateMention = useCallback(\n (userId: string, notificationId: string) => {\n try {\n room[kInternal].createTextMention(userId, notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n const onDeleteMention = useCallback(\n (notificationId: string) => {\n try {\n room[kInternal].deleteTextMention(notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n return Extension.create<\n LiveblocksExtensionOptions,\n {\n unsub: () => void;\n doc: Doc;\n provider: LiveblocksYjsProvider<any, any, any, any, any>;\n }\n >({\n name: \"liveblocksExtension\",\n\n onCreate() {\n if (\n this.options.mentions &&\n this.editor.extensionManager.extensions.find(\n (e) => e.name.toLowerCase() === \"mention\"\n )\n ) {\n console.warn(\n \"[Liveblocks] Liveblocks own mention plugin is enabled, using another mention plugin may cause a conflict.\"\n );\n }\n const self = room.getSelf();\n if (self?.info) {\n this.editor.commands.updateUser({\n name: self.info.name,\n color: self.info.color,\n });\n }\n this.storage.unsub = room.events.self.subscribe(({ info }) => {\n if (!info) {\n return;\n }\n const { name, color } = info;\n const { user } = this.storage.provider.awareness.getLocalState() as {\n user: IUserInfo;\n };\n // TODO: maybe we need a deep compare here so other info can be provided\n if (name !== user?.name || color !== user?.color) {\n this.editor.commands.updateUser({ name, color });\n }\n });\n },\n onDestroy() {\n this.storage.unsub();\n },\n addStorage() {\n if (!providersMap.has(room.id)) {\n const doc = new Doc();\n docMap.set(room.id, doc);\n providersMap.set(room.id, new LiveblocksYjsProvider(room, doc));\n }\n return {\n doc: docMap.get(room.id)!,\n provider: providersMap.get(room.id)!,\n unsub: () => {},\n };\n },\n\n addOptions() {\n return {\n field: \"default\",\n mentions: true,\n comments: true,\n };\n },\n addExtensions() {\n const extensions: AnyExtension[] = [\n LiveblocksCollab.configure({\n document: this.storage.doc,\n field: this.options.field,\n }),\n CollaborationCursor.configure({\n provider: this.storage.provider, //todo change the ! to an assert\n }),\n ];\n\n if (this.options.comments) {\n extensions.push(CommentsExtension);\n }\n if (this.options.mentions) {\n extensions.push(\n MentionExtension.configure({\n onCreateMention,\n onDeleteMention,\n })\n );\n }\n\n return extensions;\n },\n });\n};\n"],"names":["useRoom","useEffect","useCallback","kInternal","Extension","Doc","LiveblocksYjsProvider","CommentsExtension","MentionExtension"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LiveblocksExtension.js","sources":["../src/LiveblocksExtension.ts"],"sourcesContent":["import { type IUserInfo, kInternal, TextEditorType } from \"@liveblocks/core\";\nimport {\n CreateThreadError,\n getUmbrellaStoreForClient,\n selectThreads,\n useClient,\n useCommentsErrorListener,\n useRoom,\n} from \"@liveblocks/react\";\nimport { LiveblocksYjsProvider } from \"@liveblocks/yjs\";\nimport type { AnyExtension } from \"@tiptap/core\";\nimport { Extension, getMarkType } from \"@tiptap/core\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCursor from \"@tiptap/extension-collaboration-cursor\";\nimport type { Mark as PMMark } from \"@tiptap/pm/model\";\nimport { useCallback } from \"react\";\nimport { Doc } from \"yjs\";\n\nimport { CommentsExtension } from \"./comments/CommentsExtension\";\nimport { MentionExtension } from \"./mentions/MentionExtension\";\nimport { LIVEBLOCKS_COMMENT_MARK_TYPE } from \"./types\";\n\nconst providersMap = new Map<\n string,\n LiveblocksYjsProvider<any, any, any, any, any>\n>();\n\nconst docMap = new Map<string, Doc>();\n\ntype LiveblocksExtensionOptions = {\n field?: string;\n comments: boolean; // | CommentsConfiguration\n mentions: boolean; // | MentionsConfiguration\n};\n\nconst LiveblocksCollab = Collaboration.extend({\n // Override the onCreate method to warn users about potential misconfigurations\n onCreate() {\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"doc\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap document extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n !this.editor.extensionManager.extensions.find(\n (e) => e.name === \"paragraph\"\n )\n ) {\n console.warn(\n \"[Liveblocks] The tiptap paragraph extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"text\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap text extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n this.editor.extensionManager.extensions.find((e) => e.name === \"history\")\n ) {\n console.warn(\n \"[Liveblocks] The history extension is enabled, Liveblocks extension provides its own. Please remove or disable the History plugin to prevent unwanted conflicts.\"\n );\n }\n },\n});\n\nexport const useLiveblocksExtension = (): Extension => {\n const room = useRoom();\n\n // TODO: we don't need these things if comments isn't turned on...\n // TODO: we don't have a reference to the editor here, need to figure this out\n useCommentsErrorListener((error) => {\n // If thread creation fails, we remove the thread id from the associated nodes and unwrap the nodes if they are no longer associated with any threads\n if (error instanceof CreateThreadError) {\n // handleThreadDelete(error.context.threadId);\n }\n });\n const client = useClient();\n const store = getUmbrellaStoreForClient(client);\n const roomId = room.id;\n\n const reportTextEditorType = useCallback(\n (field: string) => {\n room[kInternal].reportTextEditor(TextEditorType.TipTap, field);\n },\n [room]\n );\n const onCreateMention = useCallback(\n (userId: string, notificationId: string) => {\n try {\n room[kInternal].createTextMention(userId, notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n const onDeleteMention = useCallback(\n (notificationId: string) => {\n try {\n room[kInternal].deleteTextMention(notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n return Extension.create<\n LiveblocksExtensionOptions,\n {\n unsubs: (() => void)[];\n doc: Doc;\n provider: LiveblocksYjsProvider<any, any, any, any, any>;\n }\n >({\n name: \"liveblocksExtension\",\n\n onCreate() {\n if (\n this.options.mentions &&\n this.editor.extensionManager.extensions.find(\n (e) => e.name.toLowerCase() === \"mention\"\n )\n ) {\n console.warn(\n \"[Liveblocks] Liveblocks own mention plugin is enabled, using another mention plugin may cause a conflict.\"\n );\n }\n const self = room.getSelf();\n if (self?.info) {\n this.editor.commands.updateUser({\n name: self.info.name,\n color: self.info.color,\n });\n }\n this.storage.unsubs.push(\n room.events.self.subscribe(({ info }) => {\n if (!info) {\n return;\n }\n const { name, color } = info;\n const { user } = this.storage.provider.awareness.getLocalState() as {\n user: IUserInfo;\n };\n // TODO: maybe we need a deep compare here so other info can be provided\n if (name !== user?.name || color !== user?.color) {\n this.editor.commands.updateUser({ name, color });\n }\n })\n );\n if (this.options.comments) {\n const commentMarkType = getMarkType(\n LIVEBLOCKS_COMMENT_MARK_TYPE,\n this.editor.schema\n );\n this.storage.unsubs.push(\n // Subscribe to threads so we can update comment marks if they become resolved/deleted\n store.subscribeThreads(() => {\n const threadMap = new Map(\n selectThreads(store.getFullState(), {\n roomId,\n orderBy: \"age\",\n query: {\n resolved: false,\n },\n }).map((thread) => [thread.id, true])\n );\n function isComment(mark: PMMark): mark is PMMark & {\n attrs: { orphan: boolean; threadId: string };\n } {\n return mark.type.name === LIVEBLOCKS_COMMENT_MARK_TYPE;\n }\n // when threads change, find marks and update them if needed\n this.editor.state.doc.descendants((node, pos) => {\n node.marks.forEach((mark) => {\n if (isComment(mark)) {\n const markThreadId = mark.attrs.threadId;\n const isOrphan = !threadMap.has(markThreadId);\n if (isOrphan !== mark.attrs.orphan) {\n const { tr } = this.editor.state;\n const trimmedFrom = Math.max(pos, 0);\n const trimmedTo = Math.min(\n pos + node.nodeSize,\n this.editor.state.doc.content.size - 1\n );\n tr.removeMark(trimmedFrom, trimmedTo, commentMarkType);\n tr.addMark(\n trimmedFrom,\n trimmedTo,\n commentMarkType.create({\n ...mark.attrs,\n orphan: isOrphan,\n })\n );\n this.editor.view.dispatch(tr);\n }\n }\n });\n });\n })\n );\n }\n\n reportTextEditorType(this.options.field ?? \"default\");\n },\n onDestroy() {\n this.storage.unsubs.forEach((unsub) => unsub());\n },\n addStorage() {\n if (!providersMap.has(room.id)) {\n const doc = new Doc();\n docMap.set(room.id, doc);\n providersMap.set(room.id, new LiveblocksYjsProvider(room, doc));\n }\n return {\n doc: docMap.get(room.id)!,\n provider: providersMap.get(room.id)!,\n unsubs: [],\n };\n },\n\n addOptions() {\n return {\n field: \"default\",\n mentions: true,\n comments: true,\n };\n },\n addExtensions() {\n const extensions: AnyExtension[] = [\n LiveblocksCollab.configure({\n document: this.storage.doc,\n field: this.options.field,\n }),\n CollaborationCursor.configure({\n provider: this.storage.provider, //todo change the ! to an assert\n }),\n ];\n\n if (this.options.comments) {\n extensions.push(CommentsExtension);\n }\n if (this.options.mentions) {\n extensions.push(\n MentionExtension.configure({\n onCreateMention,\n onDeleteMention,\n })\n );\n }\n\n return extensions;\n },\n });\n};\n"],"names":["useRoom","useCommentsErrorListener","CreateThreadError","useClient","getUmbrellaStoreForClient","useCallback","kInternal","TextEditorType","Extension","getMarkType","LIVEBLOCKS_COMMENT_MARK_TYPE","selectThreads","Doc","LiveblocksYjsProvider","CommentsExtension","MentionExtension"],"mappings":"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"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { kInternal } from '@liveblocks/core';
|
|
2
|
-
import { useRoom } from '@liveblocks/react';
|
|
1
|
+
import { kInternal, TextEditorType } from '@liveblocks/core';
|
|
2
|
+
import { useRoom, useCommentsErrorListener, CreateThreadError, useClient, getUmbrellaStoreForClient, selectThreads } from '@liveblocks/react';
|
|
3
3
|
import { LiveblocksYjsProvider } from '@liveblocks/yjs';
|
|
4
|
-
import { Extension } from '@tiptap/core';
|
|
4
|
+
import { Extension, getMarkType } from '@tiptap/core';
|
|
5
5
|
import Collaboration from '@tiptap/extension-collaboration';
|
|
6
6
|
import CollaborationCursor from '@tiptap/extension-collaboration-cursor';
|
|
7
|
-
import {
|
|
7
|
+
import { useCallback } from 'react';
|
|
8
8
|
import { Doc } from 'yjs';
|
|
9
9
|
import { CommentsExtension } from './comments/CommentsExtension.mjs';
|
|
10
10
|
import { MentionExtension } from './mentions/MentionExtension.mjs';
|
|
11
|
+
import { LIVEBLOCKS_COMMENT_MARK_TYPE } from './types.mjs';
|
|
11
12
|
|
|
12
13
|
const providersMap = /* @__PURE__ */ new Map();
|
|
13
14
|
const docMap = /* @__PURE__ */ new Map();
|
|
@@ -39,8 +40,19 @@ const LiveblocksCollab = Collaboration.extend({
|
|
|
39
40
|
});
|
|
40
41
|
const useLiveblocksExtension = () => {
|
|
41
42
|
const room = useRoom();
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
useCommentsErrorListener((error) => {
|
|
44
|
+
if (error instanceof CreateThreadError) {
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
const client = useClient();
|
|
48
|
+
const store = getUmbrellaStoreForClient(client);
|
|
49
|
+
const roomId = room.id;
|
|
50
|
+
const reportTextEditorType = useCallback(
|
|
51
|
+
(field) => {
|
|
52
|
+
room[kInternal].reportTextEditor(TextEditorType.TipTap, field);
|
|
53
|
+
},
|
|
54
|
+
[room]
|
|
55
|
+
);
|
|
44
56
|
const onCreateMention = useCallback(
|
|
45
57
|
(userId, notificationId) => {
|
|
46
58
|
try {
|
|
@@ -78,19 +90,70 @@ const useLiveblocksExtension = () => {
|
|
|
78
90
|
color: self.info.color
|
|
79
91
|
});
|
|
80
92
|
}
|
|
81
|
-
this.storage.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
this.storage.unsubs.push(
|
|
94
|
+
room.events.self.subscribe(({ info }) => {
|
|
95
|
+
if (!info) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const { name, color } = info;
|
|
99
|
+
const { user } = this.storage.provider.awareness.getLocalState();
|
|
100
|
+
if (name !== user?.name || color !== user?.color) {
|
|
101
|
+
this.editor.commands.updateUser({ name, color });
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
);
|
|
105
|
+
if (this.options.comments) {
|
|
106
|
+
const commentMarkType = getMarkType(
|
|
107
|
+
LIVEBLOCKS_COMMENT_MARK_TYPE,
|
|
108
|
+
this.editor.schema
|
|
109
|
+
);
|
|
110
|
+
this.storage.unsubs.push(
|
|
111
|
+
store.subscribeThreads(() => {
|
|
112
|
+
const threadMap = new Map(
|
|
113
|
+
selectThreads(store.getFullState(), {
|
|
114
|
+
roomId,
|
|
115
|
+
orderBy: "age",
|
|
116
|
+
query: {
|
|
117
|
+
resolved: false
|
|
118
|
+
}
|
|
119
|
+
}).map((thread) => [thread.id, true])
|
|
120
|
+
);
|
|
121
|
+
function isComment(mark) {
|
|
122
|
+
return mark.type.name === LIVEBLOCKS_COMMENT_MARK_TYPE;
|
|
123
|
+
}
|
|
124
|
+
this.editor.state.doc.descendants((node, pos) => {
|
|
125
|
+
node.marks.forEach((mark) => {
|
|
126
|
+
if (isComment(mark)) {
|
|
127
|
+
const markThreadId = mark.attrs.threadId;
|
|
128
|
+
const isOrphan = !threadMap.has(markThreadId);
|
|
129
|
+
if (isOrphan !== mark.attrs.orphan) {
|
|
130
|
+
const { tr } = this.editor.state;
|
|
131
|
+
const trimmedFrom = Math.max(pos, 0);
|
|
132
|
+
const trimmedTo = Math.min(
|
|
133
|
+
pos + node.nodeSize,
|
|
134
|
+
this.editor.state.doc.content.size - 1
|
|
135
|
+
);
|
|
136
|
+
tr.removeMark(trimmedFrom, trimmedTo, commentMarkType);
|
|
137
|
+
tr.addMark(
|
|
138
|
+
trimmedFrom,
|
|
139
|
+
trimmedTo,
|
|
140
|
+
commentMarkType.create({
|
|
141
|
+
...mark.attrs,
|
|
142
|
+
orphan: isOrphan
|
|
143
|
+
})
|
|
144
|
+
);
|
|
145
|
+
this.editor.view.dispatch(tr);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
})
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
reportTextEditorType(this.options.field ?? "default");
|
|
91
154
|
},
|
|
92
155
|
onDestroy() {
|
|
93
|
-
this.storage.unsub();
|
|
156
|
+
this.storage.unsubs.forEach((unsub) => unsub());
|
|
94
157
|
},
|
|
95
158
|
addStorage() {
|
|
96
159
|
if (!providersMap.has(room.id)) {
|
|
@@ -101,8 +164,7 @@ const useLiveblocksExtension = () => {
|
|
|
101
164
|
return {
|
|
102
165
|
doc: docMap.get(room.id),
|
|
103
166
|
provider: providersMap.get(room.id),
|
|
104
|
-
|
|
105
|
-
}
|
|
167
|
+
unsubs: []
|
|
106
168
|
};
|
|
107
169
|
},
|
|
108
170
|
addOptions() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveblocksExtension.mjs","sources":["../src/LiveblocksExtension.ts"],"sourcesContent":["import { type IUserInfo, kInternal } from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport { LiveblocksYjsProvider } from \"@liveblocks/yjs\";\nimport type { AnyExtension } from \"@tiptap/core\";\nimport { Extension } from \"@tiptap/core\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCursor from \"@tiptap/extension-collaboration-cursor\";\nimport { useCallback, useEffect } from \"react\";\nimport { Doc } from \"yjs\";\n\nimport { CommentsExtension } from \"./comments/CommentsExtension\";\nimport { MentionExtension } from \"./mentions/MentionExtension\";\n\nconst providersMap = new Map<\n string,\n LiveblocksYjsProvider<any, any, any, any, any>\n>();\n\nconst docMap = new Map<string, Doc>();\n\ntype LiveblocksExtensionOptions = {\n field?: string;\n comments: boolean; // | CommentsConfiguration\n mentions: boolean; // | MentionsConfiguration\n};\n\nconst LiveblocksCollab = Collaboration.extend({\n // Override the onCreate method to warn users about potential misconfigurations\n onCreate() {\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"doc\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap document extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n !this.editor.extensionManager.extensions.find(\n (e) => e.name === \"paragraph\"\n )\n ) {\n console.warn(\n \"[Liveblocks] The tiptap paragraph extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"text\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap text extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n this.editor.extensionManager.extensions.find((e) => e.name === \"history\")\n ) {\n console.warn(\n \"[Liveblocks] The history extension is enabled, Liveblocks extension provides its own. Please remove or disable the History plugin to prevent unwanted conflicts.\"\n );\n }\n },\n});\n\nexport const useLiveblocksExtension = (): Extension => {\n const room = useRoom();\n useEffect(() => {\n // Report that this is lexical and root is the rootKey\n // TODO: put this back in\n // room[kInternal].reportTextEditor(TextEditorType.TipTap, \"root\");\n }, [room]);\n\n const onCreateMention = useCallback(\n (userId: string, notificationId: string) => {\n try {\n room[kInternal].createTextMention(userId, notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n const onDeleteMention = useCallback(\n (notificationId: string) => {\n try {\n room[kInternal].deleteTextMention(notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n return Extension.create<\n LiveblocksExtensionOptions,\n {\n unsub: () => void;\n doc: Doc;\n provider: LiveblocksYjsProvider<any, any, any, any, any>;\n }\n >({\n name: \"liveblocksExtension\",\n\n onCreate() {\n if (\n this.options.mentions &&\n this.editor.extensionManager.extensions.find(\n (e) => e.name.toLowerCase() === \"mention\"\n )\n ) {\n console.warn(\n \"[Liveblocks] Liveblocks own mention plugin is enabled, using another mention plugin may cause a conflict.\"\n );\n }\n const self = room.getSelf();\n if (self?.info) {\n this.editor.commands.updateUser({\n name: self.info.name,\n color: self.info.color,\n });\n }\n this.storage.unsub = room.events.self.subscribe(({ info }) => {\n if (!info) {\n return;\n }\n const { name, color } = info;\n const { user } = this.storage.provider.awareness.getLocalState() as {\n user: IUserInfo;\n };\n // TODO: maybe we need a deep compare here so other info can be provided\n if (name !== user?.name || color !== user?.color) {\n this.editor.commands.updateUser({ name, color });\n }\n });\n },\n onDestroy() {\n this.storage.unsub();\n },\n addStorage() {\n if (!providersMap.has(room.id)) {\n const doc = new Doc();\n docMap.set(room.id, doc);\n providersMap.set(room.id, new LiveblocksYjsProvider(room, doc));\n }\n return {\n doc: docMap.get(room.id)!,\n provider: providersMap.get(room.id)!,\n unsub: () => {},\n };\n },\n\n addOptions() {\n return {\n field: \"default\",\n mentions: true,\n comments: true,\n };\n },\n addExtensions() {\n const extensions: AnyExtension[] = [\n LiveblocksCollab.configure({\n document: this.storage.doc,\n field: this.options.field,\n }),\n CollaborationCursor.configure({\n provider: this.storage.provider, //todo change the ! to an assert\n }),\n ];\n\n if (this.options.comments) {\n extensions.push(CommentsExtension);\n }\n if (this.options.mentions) {\n extensions.push(\n MentionExtension.configure({\n onCreateMention,\n onDeleteMention,\n })\n );\n }\n\n return extensions;\n },\n });\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LiveblocksExtension.mjs","sources":["../src/LiveblocksExtension.ts"],"sourcesContent":["import { type IUserInfo, kInternal, TextEditorType } from \"@liveblocks/core\";\nimport {\n CreateThreadError,\n getUmbrellaStoreForClient,\n selectThreads,\n useClient,\n useCommentsErrorListener,\n useRoom,\n} from \"@liveblocks/react\";\nimport { LiveblocksYjsProvider } from \"@liveblocks/yjs\";\nimport type { AnyExtension } from \"@tiptap/core\";\nimport { Extension, getMarkType } from \"@tiptap/core\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCursor from \"@tiptap/extension-collaboration-cursor\";\nimport type { Mark as PMMark } from \"@tiptap/pm/model\";\nimport { useCallback } from \"react\";\nimport { Doc } from \"yjs\";\n\nimport { CommentsExtension } from \"./comments/CommentsExtension\";\nimport { MentionExtension } from \"./mentions/MentionExtension\";\nimport { LIVEBLOCKS_COMMENT_MARK_TYPE } from \"./types\";\n\nconst providersMap = new Map<\n string,\n LiveblocksYjsProvider<any, any, any, any, any>\n>();\n\nconst docMap = new Map<string, Doc>();\n\ntype LiveblocksExtensionOptions = {\n field?: string;\n comments: boolean; // | CommentsConfiguration\n mentions: boolean; // | MentionsConfiguration\n};\n\nconst LiveblocksCollab = Collaboration.extend({\n // Override the onCreate method to warn users about potential misconfigurations\n onCreate() {\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"doc\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap document extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n !this.editor.extensionManager.extensions.find(\n (e) => e.name === \"paragraph\"\n )\n ) {\n console.warn(\n \"[Liveblocks] The tiptap paragraph extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n\n if (\n !this.editor.extensionManager.extensions.find((e) => e.name === \"text\")\n ) {\n console.warn(\n \"[Liveblocks] The tiptap text extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension.\"\n );\n }\n if (\n this.editor.extensionManager.extensions.find((e) => e.name === \"history\")\n ) {\n console.warn(\n \"[Liveblocks] The history extension is enabled, Liveblocks extension provides its own. Please remove or disable the History plugin to prevent unwanted conflicts.\"\n );\n }\n },\n});\n\nexport const useLiveblocksExtension = (): Extension => {\n const room = useRoom();\n\n // TODO: we don't need these things if comments isn't turned on...\n // TODO: we don't have a reference to the editor here, need to figure this out\n useCommentsErrorListener((error) => {\n // If thread creation fails, we remove the thread id from the associated nodes and unwrap the nodes if they are no longer associated with any threads\n if (error instanceof CreateThreadError) {\n // handleThreadDelete(error.context.threadId);\n }\n });\n const client = useClient();\n const store = getUmbrellaStoreForClient(client);\n const roomId = room.id;\n\n const reportTextEditorType = useCallback(\n (field: string) => {\n room[kInternal].reportTextEditor(TextEditorType.TipTap, field);\n },\n [room]\n );\n const onCreateMention = useCallback(\n (userId: string, notificationId: string) => {\n try {\n room[kInternal].createTextMention(userId, notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n const onDeleteMention = useCallback(\n (notificationId: string) => {\n try {\n room[kInternal].deleteTextMention(notificationId);\n } catch (err) {\n console.warn(err);\n }\n },\n [room]\n );\n return Extension.create<\n LiveblocksExtensionOptions,\n {\n unsubs: (() => void)[];\n doc: Doc;\n provider: LiveblocksYjsProvider<any, any, any, any, any>;\n }\n >({\n name: \"liveblocksExtension\",\n\n onCreate() {\n if (\n this.options.mentions &&\n this.editor.extensionManager.extensions.find(\n (e) => e.name.toLowerCase() === \"mention\"\n )\n ) {\n console.warn(\n \"[Liveblocks] Liveblocks own mention plugin is enabled, using another mention plugin may cause a conflict.\"\n );\n }\n const self = room.getSelf();\n if (self?.info) {\n this.editor.commands.updateUser({\n name: self.info.name,\n color: self.info.color,\n });\n }\n this.storage.unsubs.push(\n room.events.self.subscribe(({ info }) => {\n if (!info) {\n return;\n }\n const { name, color } = info;\n const { user } = this.storage.provider.awareness.getLocalState() as {\n user: IUserInfo;\n };\n // TODO: maybe we need a deep compare here so other info can be provided\n if (name !== user?.name || color !== user?.color) {\n this.editor.commands.updateUser({ name, color });\n }\n })\n );\n if (this.options.comments) {\n const commentMarkType = getMarkType(\n LIVEBLOCKS_COMMENT_MARK_TYPE,\n this.editor.schema\n );\n this.storage.unsubs.push(\n // Subscribe to threads so we can update comment marks if they become resolved/deleted\n store.subscribeThreads(() => {\n const threadMap = new Map(\n selectThreads(store.getFullState(), {\n roomId,\n orderBy: \"age\",\n query: {\n resolved: false,\n },\n }).map((thread) => [thread.id, true])\n );\n function isComment(mark: PMMark): mark is PMMark & {\n attrs: { orphan: boolean; threadId: string };\n } {\n return mark.type.name === LIVEBLOCKS_COMMENT_MARK_TYPE;\n }\n // when threads change, find marks and update them if needed\n this.editor.state.doc.descendants((node, pos) => {\n node.marks.forEach((mark) => {\n if (isComment(mark)) {\n const markThreadId = mark.attrs.threadId;\n const isOrphan = !threadMap.has(markThreadId);\n if (isOrphan !== mark.attrs.orphan) {\n const { tr } = this.editor.state;\n const trimmedFrom = Math.max(pos, 0);\n const trimmedTo = Math.min(\n pos + node.nodeSize,\n this.editor.state.doc.content.size - 1\n );\n tr.removeMark(trimmedFrom, trimmedTo, commentMarkType);\n tr.addMark(\n trimmedFrom,\n trimmedTo,\n commentMarkType.create({\n ...mark.attrs,\n orphan: isOrphan,\n })\n );\n this.editor.view.dispatch(tr);\n }\n }\n });\n });\n })\n );\n }\n\n reportTextEditorType(this.options.field ?? \"default\");\n },\n onDestroy() {\n this.storage.unsubs.forEach((unsub) => unsub());\n },\n addStorage() {\n if (!providersMap.has(room.id)) {\n const doc = new Doc();\n docMap.set(room.id, doc);\n providersMap.set(room.id, new LiveblocksYjsProvider(room, doc));\n }\n return {\n doc: docMap.get(room.id)!,\n provider: providersMap.get(room.id)!,\n unsubs: [],\n };\n },\n\n addOptions() {\n return {\n field: \"default\",\n mentions: true,\n comments: true,\n };\n },\n addExtensions() {\n const extensions: AnyExtension[] = [\n LiveblocksCollab.configure({\n document: this.storage.doc,\n field: this.options.field,\n }),\n CollaborationCursor.configure({\n provider: this.storage.provider, //todo change the ! to an assert\n }),\n ];\n\n if (this.options.comments) {\n extensions.push(CommentsExtension);\n }\n if (this.options.mentions) {\n extensions.push(\n MentionExtension.configure({\n onCreateMention,\n onDeleteMention,\n })\n );\n }\n\n return extensions;\n },\n });\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -68,7 +68,7 @@ function AnchoredThreads({
|
|
|
68
68
|
if (!pluginState)
|
|
69
69
|
return;
|
|
70
70
|
setOrderedThreads(Array.from(pluginState.threadPositions, ([threadId, position]) => ({ threadId, position })).reduce((acc, { threadId, position }) => {
|
|
71
|
-
const thread = threads.find((thread2) => thread2.id === threadId);
|
|
71
|
+
const thread = threads.find((thread2) => thread2.id === threadId && !thread2.resolved);
|
|
72
72
|
if (!thread)
|
|
73
73
|
return acc;
|
|
74
74
|
acc.push({ thread, position });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnchoredThreads.js","sources":["../../src/comments/AnchoredThreads.tsx"],"sourcesContent":["import type { BaseMetadata, DM, ThreadData } from \"@liveblocks/core\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport type { Editor } from \"@tiptap/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport React, {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\n\nimport { classNames } from \"../classnames\";\nimport type { ThreadPluginState } from \"../types\";\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\n// TODO: move that back to a variable\nconst GAP = `var(--lb-tiptap-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-tiptap-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 * The tiptap editor\n */\n editor: Editor | null;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n editor,\n ...props\n}: AnchoredThreadsProps) {\n const Thread = components?.Thread ?? DefaultThread;\n const containerRef = useRef<HTMLDivElement>(null);\n const [orderedThreads, setOrderedThreads] = useState<{ position: { from: number, to: number }, thread: ThreadData }[]>([]);\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\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 pluginState = editor ? THREADS_PLUGIN_KEY.getState(editor.state) as ThreadPluginState : null;\n\n // TODO: lexical supoprts multiple threads being active, should probably do that here as well\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (container === null || !editor) return;\n\n const activeIndex = orderedThreads.findIndex(({ thread }) =>\n thread.id === pluginState?.selectedThreadId\n );\n const ascending = activeIndex !== -1 ? orderedThreads.slice(activeIndex) : orderedThreads;\n const descending = activeIndex !== -1 ? orderedThreads.slice(0, activeIndex) : [];\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, position } of ascending) {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.view.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\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, position } of descending.reverse()) {\n const coords = editor.view.coordsAtPos(position.from);\n const rect = getRectFromCoords(coords);\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 }, [editor, orderedThreads, pluginState?.selectedThreadId, elements]);\n\n useEffect(() => {\n if (!pluginState) return;\n setOrderedThreads(Array.from(pluginState.threadPositions, ([threadId, position]) => ({ threadId, position })).reduce((acc, { threadId, position }) => {\n const thread = threads.find((thread) => thread.id === threadId);\n if (!thread) return acc;\n acc.push({ thread, position });\n return acc;\n }, [] as { thread: ThreadData, position: { from: number, to: number } }[]));\n handlePositionThreads();\n // disable exhaustive deps because we don't want an infinite loop\n // eslint-disable-next-line react-hooks/exhaustive-deps \n }, [pluginState, threads]);\n\n\n useLayoutEffect(() => {\n handlePositionThreads();\n }, [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 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 onThreadSelect = useCallback((id: string) => {\n if (!editor) return;\n editor.commands.selectThread(id);\n }, [editor]);\n\n\n if (!editor) return null;\n\n return (\n <div\n {...props}\n className={classNames(className, \"lb-root lb-tiptap-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, position }) => {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\n const offset = editor.options.element.getBoundingClientRect().top;\n\n let top = rect.top - offset;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = thread.id === pluginState?.selectedThreadId;\n\n return (\n <ThreadWrapper\n key={thread.id}\n onThreadClick={onThreadSelect}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n Thread={Thread}\n thread={thread}\n isActive={isActive}\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 onThreadClick: (id: string) => void;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n isActive: boolean;\n}\n\nfunction ThreadWrapper({\n onThreadClick,\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n isActive,\n ...props\n}: ThreadWrapperProps) {\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\n function handleThreadClick() {\n onThreadClick(thread.id);\n }\n\n return (\n <div\n ref={handleRef}\n className={classNames(\n \"lb-tiptap-anchored-threads-thread-container\",\n className\n )}\n {...props}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-tiptap-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n"],"names":["DefaultThread","useRef","useState","THREADS_PLUGIN_KEY","useCallback","getRectFromCoords","position","useEffect","thread","useLayoutEffect","classNames"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnchoredThreads.js","sources":["../../src/comments/AnchoredThreads.tsx"],"sourcesContent":["import type { BaseMetadata, DM, ThreadData } from \"@liveblocks/core\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport type { Editor } from \"@tiptap/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport React, {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\n\nimport { classNames } from \"../classnames\";\nimport type { ThreadPluginState } from \"../types\";\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\n// TODO: move that back to a variable\nconst GAP = `var(--lb-tiptap-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-tiptap-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 * The tiptap editor\n */\n editor: Editor | null;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n editor,\n ...props\n}: AnchoredThreadsProps) {\n const Thread = components?.Thread ?? DefaultThread;\n const containerRef = useRef<HTMLDivElement>(null);\n const [orderedThreads, setOrderedThreads] = useState<{ position: { from: number, to: number }, thread: ThreadData }[]>([]);\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\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 pluginState = editor ? THREADS_PLUGIN_KEY.getState(editor.state) as ThreadPluginState : null;\n\n // TODO: lexical supoprts multiple threads being active, should probably do that here as well\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (container === null || !editor) return;\n\n const activeIndex = orderedThreads.findIndex(({ thread }) =>\n thread.id === pluginState?.selectedThreadId\n );\n const ascending = activeIndex !== -1 ? orderedThreads.slice(activeIndex) : orderedThreads;\n const descending = activeIndex !== -1 ? orderedThreads.slice(0, activeIndex) : [];\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, position } of ascending) {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.view.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\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, position } of descending.reverse()) {\n const coords = editor.view.coordsAtPos(position.from);\n const rect = getRectFromCoords(coords);\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 }, [editor, orderedThreads, pluginState?.selectedThreadId, elements]);\n\n useEffect(() => {\n if (!pluginState) return;\n setOrderedThreads(Array.from(pluginState.threadPositions, ([threadId, position]) => ({ threadId, position })).reduce((acc, { threadId, position }) => {\n const thread = threads.find((thread) => thread.id === threadId && !thread.resolved);\n if (!thread) return acc;\n acc.push({ thread, position });\n return acc;\n }, [] as { thread: ThreadData, position: { from: number, to: number } }[]));\n handlePositionThreads();\n // disable exhaustive deps because we don't want an infinite loop\n // eslint-disable-next-line react-hooks/exhaustive-deps \n }, [pluginState, threads]);\n\n\n useLayoutEffect(() => {\n handlePositionThreads();\n }, [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 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 onThreadSelect = useCallback((id: string) => {\n if (!editor) return;\n editor.commands.selectThread(id);\n }, [editor]);\n\n\n if (!editor) return null;\n\n return (\n <div\n {...props}\n className={classNames(className, \"lb-root lb-tiptap-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, position }) => {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\n const offset = editor.options.element.getBoundingClientRect().top;\n\n let top = rect.top - offset;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = thread.id === pluginState?.selectedThreadId;\n\n return (\n <ThreadWrapper\n key={thread.id}\n onThreadClick={onThreadSelect}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n Thread={Thread}\n thread={thread}\n isActive={isActive}\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 onThreadClick: (id: string) => void;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n isActive: boolean;\n}\n\nfunction ThreadWrapper({\n onThreadClick,\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n isActive,\n ...props\n}: ThreadWrapperProps) {\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\n function handleThreadClick() {\n onThreadClick(thread.id);\n }\n\n return (\n <div\n ref={handleRef}\n className={classNames(\n \"lb-tiptap-anchored-threads-thread-container\",\n className\n )}\n {...props}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-tiptap-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n"],"names":["DefaultThread","useRef","useState","THREADS_PLUGIN_KEY","useCallback","getRectFromCoords","position","useEffect","thread","useLayoutEffect","classNames"],"mappings":";;;;;;;;AAoBA,MAAM,WAAc,GAAA,EAAA,CAAA;AACpB,MAAM,4BAA+B,GAAA,CAAA,EAAA,CAAA;AAGrC,MAAM,MAAM,CAAyC,sCAAA,EAAA,WAAA,CAAA,GAAA,CAAA,CAAA;AACrD,MAAM,uBAAuB,CAA0D,uDAAA,EAAA,4BAAA,CAAA,GAAA,CAAA,CAAA;AAuBhF,SAAS,eAAgB,CAAA;AAAA,EAC9B,OAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,MAAA;AAAA,EACG,GAAA,KAAA;AACL,CAAyB,EAAA;AACvB,EAAM,MAAA,MAAA,GAAS,YAAY,MAAU,IAAAA,cAAA,CAAA;AACrC,EAAM,MAAA,YAAA,GAAeC,aAAuB,IAAI,CAAA,CAAA;AAChD,EAAA,MAAM,CAAC,cAAgB,EAAA,iBAAiB,CAAI,GAAAC,cAAA,CAA2E,EAAE,CAAA,CAAA;AACzH,EAAA,MAAM,CAAC,QAAU,EAAA,WAAW,IAAIA,cAAmC,iBAAA,IAAI,KAAK,CAAA,CAAA;AAC5E,EAAA,MAAM,CAAC,SAAW,EAAA,YAAY,IAAIA,cAA8B,iBAAA,IAAI,KAAK,CAAA,CAAA;AAEzE,EAAA,MAAM,cAAc,MAAS,GAAAC,wBAAA,CAAmB,QAAS,CAAA,MAAA,CAAO,KAAK,CAAyB,GAAA,IAAA,CAAA;AAG9F,EAAM,MAAA,qBAAA,GAAwBC,kBAAY,MAAM;AAC9C,IAAA,MAAM,YAAY,YAAa,CAAA,OAAA,CAAA;AAC/B,IAAI,IAAA,SAAA,KAAc,QAAQ,CAAC,MAAA;AAAQ,MAAA,OAAA;AAEnC,IAAA,MAAM,cAAc,cAAe,CAAA,SAAA;AAAA,MAAU,CAAC,EAAE,MAAA,EAC9C,KAAA,MAAA,CAAO,OAAO,WAAa,EAAA,gBAAA;AAAA,KAC7B,CAAA;AACA,IAAA,MAAM,YAAY,WAAgB,KAAA,CAAA,CAAA,GAAK,cAAe,CAAA,KAAA,CAAM,WAAW,CAAI,GAAA,cAAA,CAAA;AAC3E,IAAM,MAAA,UAAA,GAAa,gBAAgB,CAAK,CAAA,GAAA,cAAA,CAAe,MAAM,CAAG,EAAA,WAAW,IAAI,EAAC,CAAA;AAEhF,IAAM,MAAA,YAAA,uBAAmB,GAAoB,EAAA,CAAA;AAG7C,IAAA,KAAA,MAAW,EAAE,MAAA,EAAQ,QAAS,EAAA,IAAK,SAAW,EAAA;AAC5C,MAAA,MAAM,MAAS,GAAA,MAAA,CAAO,IAAK,CAAA,WAAA,CAAY,KAAK,GAAI,CAAA,QAAA,CAAS,IAAM,EAAA,MAAA,CAAO,KAAK,KAAM,CAAA,GAAA,CAAI,OAAQ,CAAA,IAAA,GAAO,CAAC,CAAC,CAAA,CAAA;AACtG,MAAM,MAAA,IAAA,GAAOC,wBAAkB,MAAM,CAAA,CAAA;AACrC,MAAA,IAAI,GAAM,GAAA,IAAA,CAAK,GAAM,GAAA,SAAA,CAAU,uBAAwB,CAAA,GAAA,CAAA;AAEvD,MAAA,KAAA,MAAW,CAAC,EAAA,EAAIC,SAAQ,CAAA,IAAK,YAAc,EAAA;AAEzC,QAAM,MAAA,EAAA,GAAK,QAAS,CAAA,GAAA,CAAI,EAAE,CAAA,CAAA;AAC1B,QAAA,IAAI,EAAO,KAAA,KAAA,CAAA;AAAW,UAAA,SAAA;AAEtB,QAAA,IACE,OAAOA,SACP,IAAA,GAAA,IAAOA,YAAW,EAAG,CAAA,qBAAA,GAAwB,MAC7C,EAAA;AACA,UAAMA,GAAAA,GAAAA,SAAAA,GAAW,EAAG,CAAA,qBAAA,EAAwB,CAAA,MAAA,CAAA;AAAA,SAC9C;AAAA,OACF;AAEA,MAAa,YAAA,CAAA,GAAA,CAAI,MAAO,CAAA,EAAA,EAAI,GAAG,CAAA,CAAA;AAAA,KACjC;AAEA,IAAA,KAAA,MAAW,EAAE,MAAQ,EAAA,QAAA,EAAc,IAAA,UAAA,CAAW,SAAW,EAAA;AACvD,MAAA,MAAM,MAAS,GAAA,MAAA,CAAO,IAAK,CAAA,WAAA,CAAY,SAAS,IAAI,CAAA,CAAA;AACpD,MAAM,MAAA,IAAA,GAAOD,wBAAkB,MAAM,CAAA,CAAA;AAErC,MAAA,MAAM,EAAK,GAAA,QAAA,CAAS,GAAI,CAAA,MAAA,CAAO,EAAE,CAAA,CAAA;AACjC,MAAA,IAAI,EAAO,KAAA,KAAA,CAAA;AAAW,QAAA,SAAA;AAEtB,MAAA,IAAI,GAAM,GAAA,IAAA,CAAK,GAAM,GAAA,SAAA,CAAU,uBAAwB,CAAA,GAAA,CAAA;AACvD,MAAA,KAAA,MAAW,GAAGC,SAAQ,CAAA,IAAK,YAAc,EAAA;AACvC,QAAA,IAAI,GAAOA,IAAAA,SAAAA,GAAW,EAAG,CAAA,qBAAA,GAAwB,MAAQ,EAAA;AACvD,UAAMA,GAAAA,GAAAA,SAAAA,GAAW,EAAG,CAAA,qBAAA,EAAwB,CAAA,MAAA,CAAA;AAAA,SAC9C;AAAA,OACF;AAEA,MAAa,YAAA,CAAA,GAAA,CAAI,MAAO,CAAA,EAAA,EAAI,GAAG,CAAA,CAAA;AAAA,KACjC;AAEA,IAAA,YAAA,CAAa,YAAY,CAAA,CAAA;AAAA,KACxB,CAAC,MAAA,EAAQ,gBAAgB,WAAa,EAAA,gBAAA,EAAkB,QAAQ,CAAC,CAAA,CAAA;AAEpE,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,WAAA;AAAa,MAAA,OAAA;AAClB,IAAkB,iBAAA,CAAA,KAAA,CAAM,KAAK,WAAY,CAAA,eAAA,EAAiB,CAAC,CAAC,QAAA,EAAU,QAAQ,CAAO,MAAA,EAAE,UAAU,QAAS,EAAA,CAAE,EAAE,MAAO,CAAA,CAAC,KAAK,EAAE,QAAA,EAAU,UAAe,KAAA;AACpJ,MAAM,MAAA,MAAA,GAAS,OAAQ,CAAA,IAAA,CAAK,CAACC,OAAAA,KAAWA,QAAO,EAAO,KAAA,QAAA,IAAY,CAACA,OAAAA,CAAO,QAAQ,CAAA,CAAA;AAClF,MAAA,IAAI,CAAC,MAAA;AAAQ,QAAO,OAAA,GAAA,CAAA;AACpB,MAAA,GAAA,CAAI,IAAK,CAAA,EAAE,MAAQ,EAAA,QAAA,EAAU,CAAA,CAAA;AAC7B,MAAO,OAAA,GAAA,CAAA;AAAA,KACT,EAAG,EAAsE,CAAC,CAAA,CAAA;AAC1E,IAAsB,qBAAA,EAAA,CAAA;AAAA,GAGrB,EAAA,CAAC,WAAa,EAAA,OAAO,CAAC,CAAA,CAAA;AAGzB,EAAAC,qBAAA,CAAgB,MAAM;AACpB,IAAsB,qBAAA,EAAA,CAAA;AAAA,GACxB,EAAG,CAAC,qBAAqB,CAAC,CAAA,CAAA;AAE1B,EAAAF,eAAA,CAAU,MAAM;AACd,IAAM,MAAA,QAAA,GAAW,IAAI,cAAA,CAAe,qBAAqB,CAAA,CAAA;AACzD,IAAW,KAAA,MAAA,OAAA,IAAW,QAAS,CAAA,MAAA,EAAU,EAAA;AACvC,MAAA,QAAA,CAAS,QAAQ,OAAO,CAAA,CAAA;AAAA,KAC1B;AAEA,IAAO,OAAA,MAAM,SAAS,UAAW,EAAA,CAAA;AAAA,GAChC,EAAA,CAAC,QAAU,EAAA,qBAAqB,CAAC,CAAA,CAAA;AAEpC,EAAA,MAAM,SAAY,GAAAH,iBAAA,CAAY,CAAC,EAAA,EAAY,EAAoB,KAAA;AAC7D,IAAY,WAAA,CAAA,CAAC,SAAS,IAAI,GAAA,CAAI,IAAI,CAAE,CAAA,GAAA,CAAI,EAAI,EAAA,EAAE,CAAC,CAAA,CAAA;AAAA,GACjD,EAAG,EAAE,CAAA,CAAA;AAEL,EAAM,MAAA,YAAA,GAAeA,iBAAY,CAAA,CAAC,EAAe,KAAA;AAC/C,IAAA,WAAA,CAAY,CAAC,IAAS,KAAA;AACpB,MAAM,MAAA,KAAA,GAAQ,IAAI,GAAA,CAAI,IAAI,CAAA,CAAA;AAC1B,MAAA,KAAA,CAAM,OAAO,EAAE,CAAA,CAAA;AACf,MAAO,OAAA,KAAA,CAAA;AAAA,KACR,CAAA,CAAA;AAAA,GACH,EAAG,EAAE,CAAA,CAAA;AAEL,EAAM,MAAA,cAAA,GAAiBA,iBAAY,CAAA,CAAC,EAAe,KAAA;AACjD,IAAA,IAAI,CAAC,MAAA;AAAQ,MAAA,OAAA;AACb,IAAO,MAAA,CAAA,QAAA,CAAS,aAAa,EAAE,CAAA,CAAA;AAAA,GACjC,EAAG,CAAC,MAAM,CAAC,CAAA,CAAA;AAGX,EAAA,IAAI,CAAC,MAAA;AAAQ,IAAO,OAAA,IAAA,CAAA;AAEpB,EAAA,uBACG,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA;AAAA,IACE,GAAG,KAAA;AAAA,IACJ,SAAA,EAAWM,qBAAW,CAAA,SAAA,EAAW,oCAAoC,CAAA;AAAA,IACrE,GAAK,EAAA,YAAA;AAAA,IACL,KAAO,EAAA;AAAA,MACL,QAAU,EAAA,UAAA;AAAA,MACV,GAAG,KAAA;AAAA,KACL;AAAA,GAAA,EAEC,eAAe,GAAI,CAAA,CAAC,EAAE,MAAA,EAAQ,UAAe,KAAA;AAC5C,IAAA,MAAM,MAAS,GAAA,MAAA,CAAO,IAAK,CAAA,WAAA,CAAY,KAAK,GAAI,CAAA,QAAA,CAAS,IAAM,EAAA,MAAA,CAAO,KAAM,CAAA,GAAA,CAAI,OAAQ,CAAA,IAAA,GAAO,CAAC,CAAC,CAAA,CAAA;AACjG,IAAM,MAAA,IAAA,GAAOL,wBAAkB,MAAM,CAAA,CAAA;AACrC,IAAA,MAAM,MAAS,GAAA,MAAA,CAAO,OAAQ,CAAA,OAAA,CAAQ,uBAAwB,CAAA,GAAA,CAAA;AAE9D,IAAI,IAAA,GAAA,GAAM,KAAK,GAAM,GAAA,MAAA,CAAA;AAErB,IAAA,IAAI,SAAU,CAAA,GAAA,CAAI,MAAO,CAAA,EAAE,CAAG,EAAA;AAC5B,MAAM,GAAA,GAAA,SAAA,CAAU,GAAI,CAAA,MAAA,CAAO,EAAE,CAAA,CAAA;AAAA,KAC/B;AAEA,IAAM,MAAA,QAAA,GAAW,MAAO,CAAA,EAAA,KAAO,WAAa,EAAA,gBAAA,CAAA;AAE5C,IAAA,uBACG,KAAA,CAAA,aAAA,CAAA,aAAA,EAAA;AAAA,MACC,KAAK,MAAO,CAAA,EAAA;AAAA,MACZ,aAAe,EAAA,cAAA;AAAA,MACf,SAAA;AAAA,MACA,YAAA;AAAA,MACA,MAAA;AAAA,MACA,MAAA;AAAA,MACA,QAAA;AAAA,MACA,KAAO,EAAA;AAAA,QACL,QAAU,EAAA,UAAA;AAAA,QACV,SAAW,EAAA,CAAA,YAAA,EAAe,QAAW,GAAA,oBAAA,GAAuB,CAAM,CAAA,EAAA,EAAA,GAAA,CAAA,MAAA,CAAA;AAAA,QAClE,gBAAkB,EAAA,CAAA;AAAA,QAClB,UAAY,EAAA,MAAA;AAAA,QACZ,eAAiB,EAAA,GAAA;AAAA,OACnB;AAAA,KACF,CAAA,CAAA;AAAA,GAEH,CACH,CAAA,CAAA;AAEJ,CAAA;AAUA,SAAS,aAAc,CAAA;AAAA,EACrB,aAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACG,GAAA,KAAA;AACL,CAAuB,EAAA;AAErB,EAAA,MAAM,SAAY,GAAAD,iBAAA;AAAA,IAChB,CAAC,EAAuB,KAAA;AACtB,MAAU,SAAA,CAAA,MAAA,CAAO,IAAI,EAAE,CAAA,CAAA;AACvB,MAAO,OAAA,MAAM,YAAa,CAAA,MAAA,CAAO,EAAE,CAAA,CAAA;AAAA,KACrC;AAAA,IACA,CAAC,MAAA,CAAO,EAAI,EAAA,SAAA,EAAW,YAAY,CAAA;AAAA,GACrC,CAAA;AAGA,EAAA,SAAS,iBAAoB,GAAA;AAC3B,IAAA,aAAA,CAAc,OAAO,EAAE,CAAA,CAAA;AAAA,GACzB;AAEA,EAAA,uBACG,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA;AAAA,IACC,GAAK,EAAA,SAAA;AAAA,IACL,SAAW,EAAAM,qBAAA;AAAA,MACT,6CAAA;AAAA,MACA,SAAA;AAAA,KACF;AAAA,IACC,GAAG,KAAA;AAAA,GAAA,kBAEH,KAAA,CAAA,aAAA,CAAA,MAAA,EAAA;AAAA,IACC,MAAA;AAAA,IACA,YAAA,EAAY,WAAW,QAAW,GAAA,UAAA;AAAA,IAClC,OAAS,EAAA,iBAAA;AAAA,IACT,SAAU,EAAA,mCAAA;AAAA,IACV,YAAA,EAAc,WAAW,IAAO,GAAA,KAAA;AAAA,GAClC,CACF,CAAA,CAAA;AAEJ;;;;"}
|
|
@@ -66,7 +66,7 @@ function AnchoredThreads({
|
|
|
66
66
|
if (!pluginState)
|
|
67
67
|
return;
|
|
68
68
|
setOrderedThreads(Array.from(pluginState.threadPositions, ([threadId, position]) => ({ threadId, position })).reduce((acc, { threadId, position }) => {
|
|
69
|
-
const thread = threads.find((thread2) => thread2.id === threadId);
|
|
69
|
+
const thread = threads.find((thread2) => thread2.id === threadId && !thread2.resolved);
|
|
70
70
|
if (!thread)
|
|
71
71
|
return acc;
|
|
72
72
|
acc.push({ thread, position });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnchoredThreads.mjs","sources":["../../src/comments/AnchoredThreads.tsx"],"sourcesContent":["import type { BaseMetadata, DM, ThreadData } from \"@liveblocks/core\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport type { Editor } from \"@tiptap/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport React, {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\n\nimport { classNames } from \"../classnames\";\nimport type { ThreadPluginState } from \"../types\";\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\n// TODO: move that back to a variable\nconst GAP = `var(--lb-tiptap-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-tiptap-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 * The tiptap editor\n */\n editor: Editor | null;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n editor,\n ...props\n}: AnchoredThreadsProps) {\n const Thread = components?.Thread ?? DefaultThread;\n const containerRef = useRef<HTMLDivElement>(null);\n const [orderedThreads, setOrderedThreads] = useState<{ position: { from: number, to: number }, thread: ThreadData }[]>([]);\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\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 pluginState = editor ? THREADS_PLUGIN_KEY.getState(editor.state) as ThreadPluginState : null;\n\n // TODO: lexical supoprts multiple threads being active, should probably do that here as well\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (container === null || !editor) return;\n\n const activeIndex = orderedThreads.findIndex(({ thread }) =>\n thread.id === pluginState?.selectedThreadId\n );\n const ascending = activeIndex !== -1 ? orderedThreads.slice(activeIndex) : orderedThreads;\n const descending = activeIndex !== -1 ? orderedThreads.slice(0, activeIndex) : [];\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, position } of ascending) {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.view.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\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, position } of descending.reverse()) {\n const coords = editor.view.coordsAtPos(position.from);\n const rect = getRectFromCoords(coords);\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 }, [editor, orderedThreads, pluginState?.selectedThreadId, elements]);\n\n useEffect(() => {\n if (!pluginState) return;\n setOrderedThreads(Array.from(pluginState.threadPositions, ([threadId, position]) => ({ threadId, position })).reduce((acc, { threadId, position }) => {\n const thread = threads.find((thread) => thread.id === threadId);\n if (!thread) return acc;\n acc.push({ thread, position });\n return acc;\n }, [] as { thread: ThreadData, position: { from: number, to: number } }[]));\n handlePositionThreads();\n // disable exhaustive deps because we don't want an infinite loop\n // eslint-disable-next-line react-hooks/exhaustive-deps \n }, [pluginState, threads]);\n\n\n useLayoutEffect(() => {\n handlePositionThreads();\n }, [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 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 onThreadSelect = useCallback((id: string) => {\n if (!editor) return;\n editor.commands.selectThread(id);\n }, [editor]);\n\n\n if (!editor) return null;\n\n return (\n <div\n {...props}\n className={classNames(className, \"lb-root lb-tiptap-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, position }) => {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\n const offset = editor.options.element.getBoundingClientRect().top;\n\n let top = rect.top - offset;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = thread.id === pluginState?.selectedThreadId;\n\n return (\n <ThreadWrapper\n key={thread.id}\n onThreadClick={onThreadSelect}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n Thread={Thread}\n thread={thread}\n isActive={isActive}\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 onThreadClick: (id: string) => void;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n isActive: boolean;\n}\n\nfunction ThreadWrapper({\n onThreadClick,\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n isActive,\n ...props\n}: ThreadWrapperProps) {\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\n function handleThreadClick() {\n onThreadClick(thread.id);\n }\n\n return (\n <div\n ref={handleRef}\n className={classNames(\n \"lb-tiptap-anchored-threads-thread-container\",\n className\n )}\n {...props}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-tiptap-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n"],"names":["Thread","DefaultThread","position","thread"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnchoredThreads.mjs","sources":["../../src/comments/AnchoredThreads.tsx"],"sourcesContent":["import type { BaseMetadata, DM, ThreadData } from \"@liveblocks/core\";\nimport {\n Thread as DefaultThread,\n type ThreadProps,\n} from \"@liveblocks/react-ui\";\nimport type { Editor } from \"@tiptap/react\";\nimport type { ComponentPropsWithoutRef, ComponentType } from \"react\";\nimport React, {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\n\nimport { classNames } from \"../classnames\";\nimport type { ThreadPluginState } from \"../types\";\nimport { THREADS_PLUGIN_KEY } from \"../types\";\nimport { getRectFromCoords } from \"../utils\";\n\nconst DEFAULT_GAP = 20;\nconst DEFAULT_ACTIVE_THREAD_OFFSET = -12;\n\n// TODO: move that back to a variable\nconst GAP = `var(--lb-tiptap-anchored-threads-gap, ${DEFAULT_GAP}px)`;\nconst ACTIVE_THREAD_OFFSET = `var(--lb-tiptap-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 * The tiptap editor\n */\n editor: Editor | null;\n}\n\nexport function AnchoredThreads({\n threads,\n components,\n className,\n style,\n editor,\n ...props\n}: AnchoredThreadsProps) {\n const Thread = components?.Thread ?? DefaultThread;\n const containerRef = useRef<HTMLDivElement>(null);\n const [orderedThreads, setOrderedThreads] = useState<{ position: { from: number, to: number }, thread: ThreadData }[]>([]);\n const [elements, setElements] = useState<Map<string, HTMLElement>>(new Map());\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 pluginState = editor ? THREADS_PLUGIN_KEY.getState(editor.state) as ThreadPluginState : null;\n\n // TODO: lexical supoprts multiple threads being active, should probably do that here as well\n const handlePositionThreads = useCallback(() => {\n const container = containerRef.current;\n if (container === null || !editor) return;\n\n const activeIndex = orderedThreads.findIndex(({ thread }) =>\n thread.id === pluginState?.selectedThreadId\n );\n const ascending = activeIndex !== -1 ? orderedThreads.slice(activeIndex) : orderedThreads;\n const descending = activeIndex !== -1 ? orderedThreads.slice(0, activeIndex) : [];\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, position } of ascending) {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.view.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\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, position } of descending.reverse()) {\n const coords = editor.view.coordsAtPos(position.from);\n const rect = getRectFromCoords(coords);\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 }, [editor, orderedThreads, pluginState?.selectedThreadId, elements]);\n\n useEffect(() => {\n if (!pluginState) return;\n setOrderedThreads(Array.from(pluginState.threadPositions, ([threadId, position]) => ({ threadId, position })).reduce((acc, { threadId, position }) => {\n const thread = threads.find((thread) => thread.id === threadId && !thread.resolved);\n if (!thread) return acc;\n acc.push({ thread, position });\n return acc;\n }, [] as { thread: ThreadData, position: { from: number, to: number } }[]));\n handlePositionThreads();\n // disable exhaustive deps because we don't want an infinite loop\n // eslint-disable-next-line react-hooks/exhaustive-deps \n }, [pluginState, threads]);\n\n\n useLayoutEffect(() => {\n handlePositionThreads();\n }, [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 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 onThreadSelect = useCallback((id: string) => {\n if (!editor) return;\n editor.commands.selectThread(id);\n }, [editor]);\n\n\n if (!editor) return null;\n\n return (\n <div\n {...props}\n className={classNames(className, \"lb-root lb-tiptap-anchored-threads\")}\n ref={containerRef}\n style={{\n position: \"relative\",\n ...style,\n }}\n >\n {orderedThreads.map(({ thread, position }) => {\n const coords = editor.view.coordsAtPos(Math.min(position.from, editor.state.doc.content.size - 1));\n const rect = getRectFromCoords(coords);\n const offset = editor.options.element.getBoundingClientRect().top;\n\n let top = rect.top - offset;\n\n if (positions.has(thread.id)) {\n top = positions.get(thread.id)!;\n }\n\n const isActive = thread.id === pluginState?.selectedThreadId;\n\n return (\n <ThreadWrapper\n key={thread.id}\n onThreadClick={onThreadSelect}\n onItemAdd={onItemAdd}\n onItemRemove={onItemRemove}\n Thread={Thread}\n thread={thread}\n isActive={isActive}\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 onThreadClick: (id: string) => void;\n onItemAdd: (id: string, el: HTMLElement) => void;\n onItemRemove: (id: string) => void;\n isActive: boolean;\n}\n\nfunction ThreadWrapper({\n onThreadClick,\n onItemAdd,\n onItemRemove,\n thread,\n Thread,\n className,\n isActive,\n ...props\n}: ThreadWrapperProps) {\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\n function handleThreadClick() {\n onThreadClick(thread.id);\n }\n\n return (\n <div\n ref={handleRef}\n className={classNames(\n \"lb-tiptap-anchored-threads-thread-container\",\n className\n )}\n {...props}\n >\n <Thread\n thread={thread}\n data-state={isActive ? \"active\" : \"inactive\"}\n onClick={handleThreadClick}\n className=\"lb-tiptap-anchored-threads-thread\"\n showComposer={isActive ? true : false}\n />\n </div>\n );\n}\n"],"names":["Thread","DefaultThread","position","thread"],"mappings":"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"}
|
|
@@ -13,6 +13,15 @@ const Comment = core.Mark.create({
|
|
|
13
13
|
keepOnSplit: true,
|
|
14
14
|
addAttributes() {
|
|
15
15
|
return {
|
|
16
|
+
orphan: {
|
|
17
|
+
parseHTML: (element) => !!element.getAttribute("data-orphan"),
|
|
18
|
+
renderHTML: (attributes) => {
|
|
19
|
+
return attributes.orphan ? {
|
|
20
|
+
"data-orphan": "true"
|
|
21
|
+
} : {};
|
|
22
|
+
},
|
|
23
|
+
default: false
|
|
24
|
+
},
|
|
16
25
|
threadId: {
|
|
17
26
|
parseHTML: (element) => element.getAttribute("data-lb-thread-id"),
|
|
18
27
|
renderHTML: (attributes) => {
|
|
@@ -117,7 +126,14 @@ const Comment = core.Mark.create({
|
|
|
117
126
|
selectThread(null);
|
|
118
127
|
return;
|
|
119
128
|
}
|
|
120
|
-
const
|
|
129
|
+
const commentMark = node.marks.find(
|
|
130
|
+
(mark) => mark.type === this.type
|
|
131
|
+
);
|
|
132
|
+
if (commentMark?.attrs.orphan) {
|
|
133
|
+
selectThread(null);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const threadId = commentMark?.attrs.threadId;
|
|
121
137
|
selectThread(threadId ?? null);
|
|
122
138
|
}
|
|
123
139
|
}
|
|
@@ -127,6 +143,7 @@ const Comment = core.Mark.create({
|
|
|
127
143
|
});
|
|
128
144
|
const CommentsExtension = core.Extension.create({
|
|
129
145
|
name: "liveblocksComments",
|
|
146
|
+
priority: 95,
|
|
130
147
|
addExtensions() {
|
|
131
148
|
return [Comment];
|
|
132
149
|
},
|