@liveblocks/react-tiptap 0.0.1 → 2.8.3-tiptap1
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 +144 -0
- package/dist/LiveblocksExtension.js.map +1 -0
- package/dist/LiveblocksExtension.mjs +142 -0
- package/dist/LiveblocksExtension.mjs.map +1 -0
- package/dist/classnames.js +8 -0
- package/dist/classnames.js.map +1 -0
- package/dist/classnames.mjs +6 -0
- package/dist/classnames.mjs.map +1 -0
- package/dist/comments/AnchoredThreads.js +178 -0
- package/dist/comments/AnchoredThreads.js.map +1 -0
- package/dist/comments/AnchoredThreads.mjs +176 -0
- package/dist/comments/AnchoredThreads.mjs.map +1 -0
- package/dist/comments/CommentsExtension.js +207 -0
- package/dist/comments/CommentsExtension.js.map +1 -0
- package/dist/comments/CommentsExtension.mjs +205 -0
- package/dist/comments/CommentsExtension.mjs.map +1 -0
- package/dist/comments/FloatingComposer.js +103 -0
- package/dist/comments/FloatingComposer.js.map +1 -0
- package/dist/comments/FloatingComposer.mjs +100 -0
- package/dist/comments/FloatingComposer.mjs.map +1 -0
- package/dist/comments/FloatingThreads.js +154 -0
- package/dist/comments/FloatingThreads.js.map +1 -0
- package/dist/comments/FloatingThreads.mjs +151 -0
- package/dist/comments/FloatingThreads.mjs.map +1 -0
- package/dist/index.d.mts +65 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +10 -0
- package/dist/index.mjs.map +1 -0
- package/dist/mentions/Avatar.js +53 -0
- package/dist/mentions/Avatar.js.map +1 -0
- package/dist/mentions/Avatar.mjs +51 -0
- package/dist/mentions/Avatar.mjs.map +1 -0
- package/dist/mentions/Mention.js +24 -0
- package/dist/mentions/Mention.js.map +1 -0
- package/dist/mentions/Mention.mjs +22 -0
- package/dist/mentions/Mention.mjs.map +1 -0
- package/dist/mentions/MentionExtension.js +221 -0
- package/dist/mentions/MentionExtension.js.map +1 -0
- package/dist/mentions/MentionExtension.mjs +219 -0
- package/dist/mentions/MentionExtension.mjs.map +1 -0
- package/dist/mentions/MentionsList.js +123 -0
- package/dist/mentions/MentionsList.js.map +1 -0
- package/dist/mentions/MentionsList.mjs +119 -0
- package/dist/mentions/MentionsList.mjs.map +1 -0
- package/dist/types.js +33 -0
- package/dist/types.js.map +1 -0
- package/dist/types.mjs +24 -0
- package/dist/types.mjs.map +1 -0
- package/dist/utils.js +47 -0
- package/dist/utils.js.map +1 -0
- package/dist/utils.mjs +43 -0
- package/dist/utils.mjs.map +1 -0
- package/dist/version-history/HistoryVersionPreview.js +79 -0
- package/dist/version-history/HistoryVersionPreview.js.map +1 -0
- package/dist/version-history/HistoryVersionPreview.mjs +77 -0
- package/dist/version-history/HistoryVersionPreview.mjs.map +1 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/dist/version.mjs +6 -0
- package/dist/version.mjs.map +1 -0
- package/package.json +77 -1
- package/src/styles/constants.css +9 -0
- package/src/styles/index.css +247 -0
- package/src/styles/utils.css +6 -0
- package/styles.css +1 -0
- package/styles.css.d.ts +1 -0
- package/styles.css.map +1 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@liveblocks/core');
|
|
4
|
+
var react = require('@liveblocks/react');
|
|
5
|
+
var yjs$1 = require('@liveblocks/yjs');
|
|
6
|
+
var core$1 = require('@tiptap/core');
|
|
7
|
+
var Collaboration = require('@tiptap/extension-collaboration');
|
|
8
|
+
var CollaborationCursor = require('@tiptap/extension-collaboration-cursor');
|
|
9
|
+
var React = require('react');
|
|
10
|
+
var yjs = require('yjs');
|
|
11
|
+
var CommentsExtension = require('./comments/CommentsExtension.js');
|
|
12
|
+
var MentionExtension = require('./mentions/MentionExtension.js');
|
|
13
|
+
|
|
14
|
+
const providersMap = /* @__PURE__ */ new Map();
|
|
15
|
+
const docMap = /* @__PURE__ */ new Map();
|
|
16
|
+
const LiveblocksCollab = Collaboration.extend({
|
|
17
|
+
onCreate() {
|
|
18
|
+
if (!this.editor.extensionManager.extensions.find((e) => e.name === "doc")) {
|
|
19
|
+
console.warn(
|
|
20
|
+
"[Liveblocks] The tiptap document extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension."
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
if (!this.editor.extensionManager.extensions.find(
|
|
24
|
+
(e) => e.name === "paragraph"
|
|
25
|
+
)) {
|
|
26
|
+
console.warn(
|
|
27
|
+
"[Liveblocks] The tiptap paragraph extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension."
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
if (!this.editor.extensionManager.extensions.find((e) => e.name === "text")) {
|
|
31
|
+
console.warn(
|
|
32
|
+
"[Liveblocks] The tiptap text extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension."
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (this.editor.extensionManager.extensions.find((e) => e.name === "history")) {
|
|
36
|
+
console.warn(
|
|
37
|
+
"[Liveblocks] The history extension is enabled, Liveblocks extension provides its own. Please remove or disable the History plugin to prevent unwanted conflicts."
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
const useLiveblocksExtension = () => {
|
|
43
|
+
const room = react.useRoom();
|
|
44
|
+
React.useEffect(() => {
|
|
45
|
+
}, [room]);
|
|
46
|
+
const onCreateMention = React.useCallback(
|
|
47
|
+
(userId, notificationId) => {
|
|
48
|
+
try {
|
|
49
|
+
room[core.kInternal].createTextMention(userId, notificationId);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.warn(err);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
[room]
|
|
55
|
+
);
|
|
56
|
+
const onDeleteMention = React.useCallback(
|
|
57
|
+
(notificationId) => {
|
|
58
|
+
try {
|
|
59
|
+
room[core.kInternal].deleteTextMention(notificationId);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.warn(err);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
[room]
|
|
65
|
+
);
|
|
66
|
+
return core$1.Extension.create({
|
|
67
|
+
name: "liveblocksExtension",
|
|
68
|
+
onCreate() {
|
|
69
|
+
if (this.options.mentions && this.editor.extensionManager.extensions.find(
|
|
70
|
+
(e) => e.name.toLowerCase() === "mention"
|
|
71
|
+
)) {
|
|
72
|
+
console.warn(
|
|
73
|
+
"[Liveblocks] Liveblocks own mention plugin is enabled, using another mention plugin may cause a conflict."
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
const self = room.getSelf();
|
|
77
|
+
if (self?.info) {
|
|
78
|
+
this.editor.commands.updateUser({
|
|
79
|
+
name: self.info.name,
|
|
80
|
+
color: self.info.color
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
this.storage.unsub = room.events.self.subscribe(({ info }) => {
|
|
84
|
+
if (!info) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const { name, color } = info;
|
|
88
|
+
const { user } = this.storage.provider.awareness.getLocalState();
|
|
89
|
+
if (name !== user?.name || color !== user?.color) {
|
|
90
|
+
this.editor.commands.updateUser({ name, color });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
onDestroy() {
|
|
95
|
+
this.storage.unsub();
|
|
96
|
+
},
|
|
97
|
+
addStorage() {
|
|
98
|
+
if (!providersMap.has(room.id)) {
|
|
99
|
+
const doc = new yjs.Doc();
|
|
100
|
+
docMap.set(room.id, doc);
|
|
101
|
+
providersMap.set(room.id, new yjs$1.LiveblocksYjsProvider(room, doc));
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
doc: docMap.get(room.id),
|
|
105
|
+
provider: providersMap.get(room.id),
|
|
106
|
+
unsub: () => {
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
addOptions() {
|
|
111
|
+
return {
|
|
112
|
+
field: "default",
|
|
113
|
+
mentions: true,
|
|
114
|
+
comments: true
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
addExtensions() {
|
|
118
|
+
const extensions = [
|
|
119
|
+
LiveblocksCollab.configure({
|
|
120
|
+
document: this.storage.doc,
|
|
121
|
+
field: this.options.field
|
|
122
|
+
}),
|
|
123
|
+
CollaborationCursor.configure({
|
|
124
|
+
provider: this.storage.provider
|
|
125
|
+
})
|
|
126
|
+
];
|
|
127
|
+
if (this.options.comments) {
|
|
128
|
+
extensions.push(CommentsExtension.CommentsExtension);
|
|
129
|
+
}
|
|
130
|
+
if (this.options.mentions) {
|
|
131
|
+
extensions.push(
|
|
132
|
+
MentionExtension.MentionExtension.configure({
|
|
133
|
+
onCreateMention,
|
|
134
|
+
onDeleteMention
|
|
135
|
+
})
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
return extensions;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
exports.useLiveblocksExtension = useLiveblocksExtension;
|
|
144
|
+
//# sourceMappingURL=LiveblocksExtension.js.map
|
|
@@ -0,0 +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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"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { kInternal } from '@liveblocks/core';
|
|
2
|
+
import { useRoom } from '@liveblocks/react';
|
|
3
|
+
import { LiveblocksYjsProvider } from '@liveblocks/yjs';
|
|
4
|
+
import { Extension } from '@tiptap/core';
|
|
5
|
+
import Collaboration from '@tiptap/extension-collaboration';
|
|
6
|
+
import CollaborationCursor from '@tiptap/extension-collaboration-cursor';
|
|
7
|
+
import { useEffect, useCallback } from 'react';
|
|
8
|
+
import { Doc } from 'yjs';
|
|
9
|
+
import { CommentsExtension } from './comments/CommentsExtension.mjs';
|
|
10
|
+
import { MentionExtension } from './mentions/MentionExtension.mjs';
|
|
11
|
+
|
|
12
|
+
const providersMap = /* @__PURE__ */ new Map();
|
|
13
|
+
const docMap = /* @__PURE__ */ new Map();
|
|
14
|
+
const LiveblocksCollab = Collaboration.extend({
|
|
15
|
+
onCreate() {
|
|
16
|
+
if (!this.editor.extensionManager.extensions.find((e) => e.name === "doc")) {
|
|
17
|
+
console.warn(
|
|
18
|
+
"[Liveblocks] The tiptap document extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension."
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
if (!this.editor.extensionManager.extensions.find(
|
|
22
|
+
(e) => e.name === "paragraph"
|
|
23
|
+
)) {
|
|
24
|
+
console.warn(
|
|
25
|
+
"[Liveblocks] The tiptap paragraph extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension."
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
if (!this.editor.extensionManager.extensions.find((e) => e.name === "text")) {
|
|
29
|
+
console.warn(
|
|
30
|
+
"[Liveblocks] The tiptap text extension is required for Liveblocks collaboration. Please add it or use Tiptap StarterKit extension."
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
if (this.editor.extensionManager.extensions.find((e) => e.name === "history")) {
|
|
34
|
+
console.warn(
|
|
35
|
+
"[Liveblocks] The history extension is enabled, Liveblocks extension provides its own. Please remove or disable the History plugin to prevent unwanted conflicts."
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
const useLiveblocksExtension = () => {
|
|
41
|
+
const room = useRoom();
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
}, [room]);
|
|
44
|
+
const onCreateMention = useCallback(
|
|
45
|
+
(userId, notificationId) => {
|
|
46
|
+
try {
|
|
47
|
+
room[kInternal].createTextMention(userId, notificationId);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.warn(err);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
[room]
|
|
53
|
+
);
|
|
54
|
+
const onDeleteMention = useCallback(
|
|
55
|
+
(notificationId) => {
|
|
56
|
+
try {
|
|
57
|
+
room[kInternal].deleteTextMention(notificationId);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.warn(err);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
[room]
|
|
63
|
+
);
|
|
64
|
+
return Extension.create({
|
|
65
|
+
name: "liveblocksExtension",
|
|
66
|
+
onCreate() {
|
|
67
|
+
if (this.options.mentions && this.editor.extensionManager.extensions.find(
|
|
68
|
+
(e) => e.name.toLowerCase() === "mention"
|
|
69
|
+
)) {
|
|
70
|
+
console.warn(
|
|
71
|
+
"[Liveblocks] Liveblocks own mention plugin is enabled, using another mention plugin may cause a conflict."
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const self = room.getSelf();
|
|
75
|
+
if (self?.info) {
|
|
76
|
+
this.editor.commands.updateUser({
|
|
77
|
+
name: self.info.name,
|
|
78
|
+
color: self.info.color
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
this.storage.unsub = room.events.self.subscribe(({ info }) => {
|
|
82
|
+
if (!info) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const { name, color } = info;
|
|
86
|
+
const { user } = this.storage.provider.awareness.getLocalState();
|
|
87
|
+
if (name !== user?.name || color !== user?.color) {
|
|
88
|
+
this.editor.commands.updateUser({ name, color });
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
onDestroy() {
|
|
93
|
+
this.storage.unsub();
|
|
94
|
+
},
|
|
95
|
+
addStorage() {
|
|
96
|
+
if (!providersMap.has(room.id)) {
|
|
97
|
+
const doc = new Doc();
|
|
98
|
+
docMap.set(room.id, doc);
|
|
99
|
+
providersMap.set(room.id, new LiveblocksYjsProvider(room, doc));
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
doc: docMap.get(room.id),
|
|
103
|
+
provider: providersMap.get(room.id),
|
|
104
|
+
unsub: () => {
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
addOptions() {
|
|
109
|
+
return {
|
|
110
|
+
field: "default",
|
|
111
|
+
mentions: true,
|
|
112
|
+
comments: true
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
addExtensions() {
|
|
116
|
+
const extensions = [
|
|
117
|
+
LiveblocksCollab.configure({
|
|
118
|
+
document: this.storage.doc,
|
|
119
|
+
field: this.options.field
|
|
120
|
+
}),
|
|
121
|
+
CollaborationCursor.configure({
|
|
122
|
+
provider: this.storage.provider
|
|
123
|
+
})
|
|
124
|
+
];
|
|
125
|
+
if (this.options.comments) {
|
|
126
|
+
extensions.push(CommentsExtension);
|
|
127
|
+
}
|
|
128
|
+
if (this.options.mentions) {
|
|
129
|
+
extensions.push(
|
|
130
|
+
MentionExtension.configure({
|
|
131
|
+
onCreateMention,
|
|
132
|
+
onDeleteMention
|
|
133
|
+
})
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
return extensions;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export { useLiveblocksExtension };
|
|
142
|
+
//# sourceMappingURL=LiveblocksExtension.mjs.map
|
|
@@ -0,0 +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":";;;;;;;;;;;AAaA,MAAM,YAAA,uBAAmB,GAGvB,EAAA,CAAA;AAEF,MAAM,MAAA,uBAAa,GAAiB,EAAA,CAAA;AAQpC,MAAM,gBAAA,GAAmB,cAAc,MAAO,CAAA;AAAA,EAE5C,QAAW,GAAA;AACT,IACE,IAAA,CAAC,IAAK,CAAA,MAAA,CAAO,gBAAiB,CAAA,UAAA,CAAW,IAAK,CAAA,CAAC,CAAM,KAAA,CAAA,CAAE,IAAS,KAAA,KAAK,CACrE,EAAA;AACA,MAAQ,OAAA,CAAA,IAAA;AAAA,QACN,wIAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAA,IACE,CAAC,IAAA,CAAK,MAAO,CAAA,gBAAA,CAAiB,UAAW,CAAA,IAAA;AAAA,MACvC,CAAC,CAAM,KAAA,CAAA,CAAE,IAAS,KAAA,WAAA;AAAA,KAEpB,EAAA;AACA,MAAQ,OAAA,CAAA,IAAA;AAAA,QACN,yIAAA;AAAA,OACF,CAAA;AAAA,KACF;AAEA,IACE,IAAA,CAAC,IAAK,CAAA,MAAA,CAAO,gBAAiB,CAAA,UAAA,CAAW,IAAK,CAAA,CAAC,CAAM,KAAA,CAAA,CAAE,IAAS,KAAA,MAAM,CACtE,EAAA;AACA,MAAQ,OAAA,CAAA,IAAA;AAAA,QACN,oIAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IACE,IAAA,IAAA,CAAK,MAAO,CAAA,gBAAA,CAAiB,UAAW,CAAA,IAAA,CAAK,CAAC,CAAM,KAAA,CAAA,CAAE,IAAS,KAAA,SAAS,CACxE,EAAA;AACA,MAAQ,OAAA,CAAA,IAAA;AAAA,QACN,kKAAA;AAAA,OACF,CAAA;AAAA,KACF;AAAA,GACF;AACF,CAAC,CAAA,CAAA;AAEM,MAAM,yBAAyB,MAAiB;AACrD,EAAA,MAAM,OAAO,OAAQ,EAAA,CAAA;AACrB,EAAA,SAAA,CAAU,MAAM;AAAA,GAIhB,EAAG,CAAC,IAAI,CAAC,CAAA,CAAA;AAET,EAAA,MAAM,eAAkB,GAAA,WAAA;AAAA,IACtB,CAAC,QAAgB,cAA2B,KAAA;AAC1C,MAAI,IAAA;AACF,QAAK,IAAA,CAAA,SAAA,CAAA,CAAW,iBAAkB,CAAA,MAAA,EAAQ,cAAc,CAAA,CAAA;AAAA,eACjD,GAAP,EAAA;AACA,QAAA,OAAA,CAAQ,KAAK,GAAG,CAAA,CAAA;AAAA,OAClB;AAAA,KACF;AAAA,IACA,CAAC,IAAI,CAAA;AAAA,GACP,CAAA;AACA,EAAA,MAAM,eAAkB,GAAA,WAAA;AAAA,IACtB,CAAC,cAA2B,KAAA;AAC1B,MAAI,IAAA;AACF,QAAK,IAAA,CAAA,SAAA,CAAA,CAAW,kBAAkB,cAAc,CAAA,CAAA;AAAA,eACzC,GAAP,EAAA;AACA,QAAA,OAAA,CAAQ,KAAK,GAAG,CAAA,CAAA;AAAA,OAClB;AAAA,KACF;AAAA,IACA,CAAC,IAAI,CAAA;AAAA,GACP,CAAA;AACA,EAAA,OAAO,UAAU,MAOf,CAAA;AAAA,IACA,IAAM,EAAA,qBAAA;AAAA,IAEN,QAAW,GAAA;AACT,MAAA,IACE,KAAK,OAAQ,CAAA,QAAA,IACb,IAAK,CAAA,MAAA,CAAO,iBAAiB,UAAW,CAAA,IAAA;AAAA,QACtC,CAAC,CAAA,KAAM,CAAE,CAAA,IAAA,CAAK,aAAkB,KAAA,SAAA;AAAA,OAElC,EAAA;AACA,QAAQ,OAAA,CAAA,IAAA;AAAA,UACN,2GAAA;AAAA,SACF,CAAA;AAAA,OACF;AACA,MAAM,MAAA,IAAA,GAAO,KAAK,OAAQ,EAAA,CAAA;AAC1B,MAAA,IAAI,MAAM,IAAM,EAAA;AACd,QAAK,IAAA,CAAA,MAAA,CAAO,SAAS,UAAW,CAAA;AAAA,UAC9B,IAAA,EAAM,KAAK,IAAK,CAAA,IAAA;AAAA,UAChB,KAAA,EAAO,KAAK,IAAK,CAAA,KAAA;AAAA,SAClB,CAAA,CAAA;AAAA,OACH;AACA,MAAK,IAAA,CAAA,OAAA,CAAQ,QAAQ,IAAK,CAAA,MAAA,CAAO,KAAK,SAAU,CAAA,CAAC,EAAE,IAAA,EAAW,KAAA;AAC5D,QAAA,IAAI,CAAC,IAAM,EAAA;AACT,UAAA,OAAA;AAAA,SACF;AACA,QAAM,MAAA,EAAE,IAAM,EAAA,KAAA,EAAU,GAAA,IAAA,CAAA;AACxB,QAAA,MAAM,EAAE,IAAK,EAAA,GAAI,KAAK,OAAQ,CAAA,QAAA,CAAS,UAAU,aAAc,EAAA,CAAA;AAI/D,QAAA,IAAI,IAAS,KAAA,IAAA,EAAM,IAAQ,IAAA,KAAA,KAAU,MAAM,KAAO,EAAA;AAChD,UAAA,IAAA,CAAK,OAAO,QAAS,CAAA,UAAA,CAAW,EAAE,IAAA,EAAM,OAAO,CAAA,CAAA;AAAA,SACjD;AAAA,OACD,CAAA,CAAA;AAAA,KACH;AAAA,IACA,SAAY,GAAA;AACV,MAAA,IAAA,CAAK,QAAQ,KAAM,EAAA,CAAA;AAAA,KACrB;AAAA,IACA,UAAa,GAAA;AACX,MAAA,IAAI,CAAC,YAAA,CAAa,GAAI,CAAA,IAAA,CAAK,EAAE,CAAG,EAAA;AAC9B,QAAM,MAAA,GAAA,GAAM,IAAI,GAAI,EAAA,CAAA;AACpB,QAAO,MAAA,CAAA,GAAA,CAAI,IAAK,CAAA,EAAA,EAAI,GAAG,CAAA,CAAA;AACvB,QAAA,YAAA,CAAa,IAAI,IAAK,CAAA,EAAA,EAAI,IAAI,qBAAsB,CAAA,IAAA,EAAM,GAAG,CAAC,CAAA,CAAA;AAAA,OAChE;AACA,MAAO,OAAA;AAAA,QACL,GAAK,EAAA,MAAA,CAAO,GAAI,CAAA,IAAA,CAAK,EAAE,CAAA;AAAA,QACvB,QAAU,EAAA,YAAA,CAAa,GAAI,CAAA,IAAA,CAAK,EAAE,CAAA;AAAA,QAClC,OAAO,MAAM;AAAA,SAAC;AAAA,OAChB,CAAA;AAAA,KACF;AAAA,IAEA,UAAa,GAAA;AACX,MAAO,OAAA;AAAA,QACL,KAAO,EAAA,SAAA;AAAA,QACP,QAAU,EAAA,IAAA;AAAA,QACV,QAAU,EAAA,IAAA;AAAA,OACZ,CAAA;AAAA,KACF;AAAA,IACA,aAAgB,GAAA;AACd,MAAA,MAAM,UAA6B,GAAA;AAAA,QACjC,iBAAiB,SAAU,CAAA;AAAA,UACzB,QAAA,EAAU,KAAK,OAAQ,CAAA,GAAA;AAAA,UACvB,KAAA,EAAO,KAAK,OAAQ,CAAA,KAAA;AAAA,SACrB,CAAA;AAAA,QACD,oBAAoB,SAAU,CAAA;AAAA,UAC5B,QAAA,EAAU,KAAK,OAAQ,CAAA,QAAA;AAAA,SACxB,CAAA;AAAA,OACH,CAAA;AAEA,MAAI,IAAA,IAAA,CAAK,QAAQ,QAAU,EAAA;AACzB,QAAA,UAAA,CAAW,KAAK,iBAAiB,CAAA,CAAA;AAAA,OACnC;AACA,MAAI,IAAA,IAAA,CAAK,QAAQ,QAAU,EAAA;AACzB,QAAW,UAAA,CAAA,IAAA;AAAA,UACT,iBAAiB,SAAU,CAAA;AAAA,YACzB,eAAA;AAAA,YACA,eAAA;AAAA,WACD,CAAA;AAAA,SACH,CAAA;AAAA,OACF;AAEA,MAAO,OAAA,UAAA,CAAA;AAAA,KACT;AAAA,GACD,CAAA,CAAA;AACH;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classnames.js","sources":["../src/classnames.ts"],"sourcesContent":["export function classNames(\n ...args: (string | number | boolean | undefined | null)[]\n) {\n return args\n .filter((arg) => typeof arg === \"string\" || typeof arg === \"number\")\n .join(\" \");\n}\n"],"names":[],"mappings":";;AAAO,SAAS,cACX,IACH,EAAA;AACA,EAAA,OAAO,IACJ,CAAA,MAAA,CAAO,CAAC,GAAA,KAAQ,OAAO,GAAA,KAAQ,QAAY,IAAA,OAAO,GAAQ,KAAA,QAAQ,CAClE,CAAA,IAAA,CAAK,GAAG,CAAA,CAAA;AACb;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classnames.mjs","sources":["../src/classnames.ts"],"sourcesContent":["export function classNames(\n ...args: (string | number | boolean | undefined | null)[]\n) {\n return args\n .filter((arg) => typeof arg === \"string\" || typeof arg === \"number\")\n .join(\" \");\n}\n"],"names":[],"mappings":"AAAO,SAAS,cACX,IACH,EAAA;AACA,EAAA,OAAO,IACJ,CAAA,MAAA,CAAO,CAAC,GAAA,KAAQ,OAAO,GAAA,KAAQ,QAAY,IAAA,OAAO,GAAQ,KAAA,QAAQ,CAClE,CAAA,IAAA,CAAK,GAAG,CAAA,CAAA;AACb;;;;"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var reactUi = require('@liveblocks/react-ui');
|
|
4
|
+
var React = require('react');
|
|
5
|
+
var classnames = require('../classnames.js');
|
|
6
|
+
var types = require('../types.js');
|
|
7
|
+
var utils = require('../utils.js');
|
|
8
|
+
|
|
9
|
+
const DEFAULT_GAP = 20;
|
|
10
|
+
const DEFAULT_ACTIVE_THREAD_OFFSET = -12;
|
|
11
|
+
const GAP = `var(--lb-tiptap-anchored-threads-gap, ${DEFAULT_GAP}px)`;
|
|
12
|
+
const ACTIVE_THREAD_OFFSET = `var(--lb-tiptap-anchored-threads-active-thread-offset, ${DEFAULT_ACTIVE_THREAD_OFFSET}px)`;
|
|
13
|
+
function AnchoredThreads({
|
|
14
|
+
threads,
|
|
15
|
+
components,
|
|
16
|
+
className,
|
|
17
|
+
style,
|
|
18
|
+
editor,
|
|
19
|
+
...props
|
|
20
|
+
}) {
|
|
21
|
+
const Thread = components?.Thread ?? reactUi.Thread;
|
|
22
|
+
const containerRef = React.useRef(null);
|
|
23
|
+
const [orderedThreads, setOrderedThreads] = React.useState([]);
|
|
24
|
+
const [elements, setElements] = React.useState(/* @__PURE__ */ new Map());
|
|
25
|
+
const [positions, setPositions] = React.useState(/* @__PURE__ */ new Map());
|
|
26
|
+
const pluginState = editor ? types.THREADS_PLUGIN_KEY.getState(editor.state) : null;
|
|
27
|
+
const handlePositionThreads = React.useCallback(() => {
|
|
28
|
+
const container = containerRef.current;
|
|
29
|
+
if (container === null || !editor)
|
|
30
|
+
return;
|
|
31
|
+
const activeIndex = orderedThreads.findIndex(
|
|
32
|
+
({ thread }) => thread.id === pluginState?.selectedThreadId
|
|
33
|
+
);
|
|
34
|
+
const ascending = activeIndex !== -1 ? orderedThreads.slice(activeIndex) : orderedThreads;
|
|
35
|
+
const descending = activeIndex !== -1 ? orderedThreads.slice(0, activeIndex) : [];
|
|
36
|
+
const newPositions = /* @__PURE__ */ new Map();
|
|
37
|
+
for (const { thread, position } of ascending) {
|
|
38
|
+
const coords = editor.view.coordsAtPos(Math.min(position.from, editor.view.state.doc.content.size - 1));
|
|
39
|
+
const rect = utils.getRectFromCoords(coords);
|
|
40
|
+
let top = rect.top - container.getBoundingClientRect().top;
|
|
41
|
+
for (const [id, position2] of newPositions) {
|
|
42
|
+
const el = elements.get(id);
|
|
43
|
+
if (el === void 0)
|
|
44
|
+
continue;
|
|
45
|
+
if (top >= position2 && top <= position2 + el.getBoundingClientRect().height) {
|
|
46
|
+
top = position2 + el.getBoundingClientRect().height;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
newPositions.set(thread.id, top);
|
|
50
|
+
}
|
|
51
|
+
for (const { thread, position } of descending.reverse()) {
|
|
52
|
+
const coords = editor.view.coordsAtPos(position.from);
|
|
53
|
+
const rect = utils.getRectFromCoords(coords);
|
|
54
|
+
const el = elements.get(thread.id);
|
|
55
|
+
if (el === void 0)
|
|
56
|
+
continue;
|
|
57
|
+
let top = rect.top - container.getBoundingClientRect().top;
|
|
58
|
+
for (const [, position2] of newPositions) {
|
|
59
|
+
if (top >= position2 - el.getBoundingClientRect().height) {
|
|
60
|
+
top = position2 - el.getBoundingClientRect().height;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
newPositions.set(thread.id, top);
|
|
64
|
+
}
|
|
65
|
+
setPositions(newPositions);
|
|
66
|
+
}, [editor, orderedThreads, pluginState?.selectedThreadId, elements]);
|
|
67
|
+
React.useEffect(() => {
|
|
68
|
+
if (!pluginState)
|
|
69
|
+
return;
|
|
70
|
+
setOrderedThreads(Array.from(pluginState.threadPositions, ([threadId, position]) => ({ threadId, position })).reduce((acc, { threadId, position }) => {
|
|
71
|
+
const thread = threads.find((thread2) => thread2.id === threadId);
|
|
72
|
+
if (!thread)
|
|
73
|
+
return acc;
|
|
74
|
+
acc.push({ thread, position });
|
|
75
|
+
return acc;
|
|
76
|
+
}, []));
|
|
77
|
+
handlePositionThreads();
|
|
78
|
+
}, [pluginState, threads]);
|
|
79
|
+
React.useLayoutEffect(() => {
|
|
80
|
+
handlePositionThreads();
|
|
81
|
+
}, [handlePositionThreads]);
|
|
82
|
+
React.useEffect(() => {
|
|
83
|
+
const observer = new ResizeObserver(handlePositionThreads);
|
|
84
|
+
for (const element of elements.values()) {
|
|
85
|
+
observer.observe(element);
|
|
86
|
+
}
|
|
87
|
+
return () => observer.disconnect();
|
|
88
|
+
}, [elements, handlePositionThreads]);
|
|
89
|
+
const onItemAdd = React.useCallback((id, el) => {
|
|
90
|
+
setElements((prev) => new Map(prev).set(id, el));
|
|
91
|
+
}, []);
|
|
92
|
+
const onItemRemove = React.useCallback((id) => {
|
|
93
|
+
setElements((prev) => {
|
|
94
|
+
const items = new Map(prev);
|
|
95
|
+
items.delete(id);
|
|
96
|
+
return items;
|
|
97
|
+
});
|
|
98
|
+
}, []);
|
|
99
|
+
const onThreadSelect = React.useCallback((id) => {
|
|
100
|
+
if (!editor)
|
|
101
|
+
return;
|
|
102
|
+
editor.commands.selectThread(id);
|
|
103
|
+
}, [editor]);
|
|
104
|
+
if (!editor)
|
|
105
|
+
return null;
|
|
106
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
107
|
+
...props,
|
|
108
|
+
className: classnames.classNames(className, "lb-root lb-tiptap-anchored-threads"),
|
|
109
|
+
ref: containerRef,
|
|
110
|
+
style: {
|
|
111
|
+
position: "relative",
|
|
112
|
+
...style
|
|
113
|
+
}
|
|
114
|
+
}, orderedThreads.map(({ thread, position }) => {
|
|
115
|
+
const coords = editor.view.coordsAtPos(Math.min(position.from, editor.state.doc.content.size - 1));
|
|
116
|
+
const rect = utils.getRectFromCoords(coords);
|
|
117
|
+
const offset = editor.options.element.getBoundingClientRect().top;
|
|
118
|
+
let top = rect.top - offset;
|
|
119
|
+
if (positions.has(thread.id)) {
|
|
120
|
+
top = positions.get(thread.id);
|
|
121
|
+
}
|
|
122
|
+
const isActive = thread.id === pluginState?.selectedThreadId;
|
|
123
|
+
return /* @__PURE__ */ React.createElement(ThreadWrapper, {
|
|
124
|
+
key: thread.id,
|
|
125
|
+
onThreadClick: onThreadSelect,
|
|
126
|
+
onItemAdd,
|
|
127
|
+
onItemRemove,
|
|
128
|
+
Thread,
|
|
129
|
+
thread,
|
|
130
|
+
isActive,
|
|
131
|
+
style: {
|
|
132
|
+
position: "absolute",
|
|
133
|
+
transform: `translate3d(${isActive ? ACTIVE_THREAD_OFFSET : 0}, ${top}px, 0)`,
|
|
134
|
+
insetInlineStart: 0,
|
|
135
|
+
inlineSize: "100%",
|
|
136
|
+
paddingBlockEnd: GAP
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
function ThreadWrapper({
|
|
142
|
+
onThreadClick,
|
|
143
|
+
onItemAdd,
|
|
144
|
+
onItemRemove,
|
|
145
|
+
thread,
|
|
146
|
+
Thread,
|
|
147
|
+
className,
|
|
148
|
+
isActive,
|
|
149
|
+
...props
|
|
150
|
+
}) {
|
|
151
|
+
const handleRef = React.useCallback(
|
|
152
|
+
(el) => {
|
|
153
|
+
onItemAdd(thread.id, el);
|
|
154
|
+
return () => onItemRemove(thread.id);
|
|
155
|
+
},
|
|
156
|
+
[thread.id, onItemAdd, onItemRemove]
|
|
157
|
+
);
|
|
158
|
+
function handleThreadClick() {
|
|
159
|
+
onThreadClick(thread.id);
|
|
160
|
+
}
|
|
161
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
162
|
+
ref: handleRef,
|
|
163
|
+
className: classnames.classNames(
|
|
164
|
+
"lb-tiptap-anchored-threads-thread-container",
|
|
165
|
+
className
|
|
166
|
+
),
|
|
167
|
+
...props
|
|
168
|
+
}, /* @__PURE__ */ React.createElement(Thread, {
|
|
169
|
+
thread,
|
|
170
|
+
"data-state": isActive ? "active" : "inactive",
|
|
171
|
+
onClick: handleThreadClick,
|
|
172
|
+
className: "lb-tiptap-anchored-threads-thread",
|
|
173
|
+
showComposer: isActive ? true : false
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
exports.AnchoredThreads = AnchoredThreads;
|
|
178
|
+
//# sourceMappingURL=AnchoredThreads.js.map
|
|
@@ -0,0 +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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"}
|