@blocknote/core 0.42.2 → 0.43.0
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/BlockNoteExtension-BWw0r8Gy.cjs +2 -0
- package/dist/BlockNoteExtension-BWw0r8Gy.cjs.map +1 -0
- package/dist/BlockNoteExtension-C2X7LW-V.js +25 -0
- package/dist/BlockNoteExtension-C2X7LW-V.js.map +1 -0
- package/dist/BlockNoteSchema-CbSavEwr.js +270 -0
- package/dist/BlockNoteSchema-CbSavEwr.js.map +1 -0
- package/dist/BlockNoteSchema-D8TyvlfU.cjs +2 -0
- package/dist/BlockNoteSchema-D8TyvlfU.cjs.map +1 -0
- package/dist/EventEmitter-CLwfmbqG.cjs +2 -0
- package/dist/EventEmitter-CLwfmbqG.cjs.map +1 -0
- package/dist/EventEmitter-CjSwpTbz.js +27 -0
- package/dist/EventEmitter-CjSwpTbz.js.map +1 -0
- package/dist/ShowSelection-BW37oJ6h.cjs +2 -0
- package/dist/ShowSelection-BW37oJ6h.cjs.map +1 -0
- package/dist/ShowSelection-Dz-NEase.js +43 -0
- package/dist/ShowSelection-Dz-NEase.js.map +1 -0
- package/dist/TrailingNode-BUhuMJrB.js +2096 -0
- package/dist/TrailingNode-BUhuMJrB.js.map +1 -0
- package/dist/TrailingNode-CaT_wbho.cjs +2 -0
- package/dist/TrailingNode-CaT_wbho.cjs.map +1 -0
- package/dist/{blockToNode-DIfPWLH8.js → blockToNode-DBNbhwwC.js} +33 -33
- package/dist/blockToNode-DBNbhwwC.js.map +1 -0
- package/dist/blockToNode-w7H99R6p.cjs.map +1 -1
- package/dist/blocknote.cjs +4 -4
- package/dist/blocknote.cjs.map +1 -1
- package/dist/blocknote.js +2401 -5592
- package/dist/blocknote.js.map +1 -1
- package/dist/blocks.cjs +1 -1
- package/dist/blocks.js +71 -70
- package/dist/blocks.js.map +1 -1
- package/dist/comments.cjs +1 -1
- package/dist/comments.cjs.map +1 -1
- package/dist/comments.js +451 -137
- package/dist/comments.js.map +1 -1
- package/dist/{BlockNoteSchema-Bi-eeHal.js → defaultBlocks-BJtxTOM2.js} +991 -1047
- package/dist/defaultBlocks-BJtxTOM2.js.map +1 -0
- package/dist/defaultBlocks-BxFclIGP.cjs +6 -0
- package/dist/defaultBlocks-BxFclIGP.cjs.map +1 -0
- package/dist/extensions.cjs +2 -0
- package/dist/extensions.cjs.map +1 -0
- package/dist/extensions.js +57 -0
- package/dist/extensions.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/webpack-stats.json +1 -1
- package/dist/yjs.js +1 -1
- package/package.json +12 -6
- package/src/api/nodeConversions/blockToNode.ts +1 -1
- package/src/api/nodeConversions/nodeToBlock.ts +1 -1
- package/src/blocks/Code/block.ts +4 -4
- package/src/blocks/Divider/block.ts +2 -2
- package/src/blocks/File/helpers/render/createAddFileButton.ts +7 -5
- package/src/blocks/Heading/block.ts +23 -20
- package/src/blocks/ListItem/BulletListItem/block.ts +2 -2
- package/src/blocks/ListItem/CheckListItem/block.ts +2 -2
- package/src/blocks/ListItem/NumberedListItem/block.ts +3 -3
- package/src/blocks/ListItem/ToggleListItem/block.ts +2 -2
- package/src/blocks/PageBreak/getPageBreakSlashMenuItems.ts +2 -2
- package/src/blocks/Paragraph/block.ts +2 -2
- package/src/blocks/Quote/block.ts +2 -2
- package/src/blocks/Table/block.ts +4 -3
- package/src/blocks/ToggleWrapper/createToggleWrapper.ts +2 -1
- package/src/comments/extension.ts +353 -0
- package/src/comments/index.ts +2 -1
- package/src/comments/types.ts +8 -0
- package/src/{extensions/Comments → comments}/userstore/UserStore.ts +2 -2
- package/src/editor/BlockNoteEditor.test.ts +2 -23
- package/src/editor/BlockNoteEditor.ts +66 -453
- package/src/editor/BlockNoteExtension.test.ts +103 -0
- package/src/editor/BlockNoteExtension.ts +174 -56
- package/src/editor/managers/EventManager.ts +64 -35
- package/src/editor/managers/ExtensionManager/extensions.ts +214 -0
- package/src/editor/managers/ExtensionManager/index.ts +514 -0
- package/src/editor/managers/ExtensionManager/symbol.ts +6 -0
- package/src/editor/managers/SelectionManager.ts +5 -1
- package/src/editor/managers/StateManager.ts +29 -17
- package/src/editor/managers/index.ts +1 -5
- package/src/extensions/BlockChange/{BlockChangePlugin.ts → BlockChange.ts} +27 -29
- package/src/extensions/Collaboration/{ForkYDocPlugin.test.ts → ForkYDoc.test.ts} +6 -5
- package/src/extensions/Collaboration/ForkYDoc.ts +158 -0
- package/src/extensions/Collaboration/YCursorPlugin.ts +183 -0
- package/src/extensions/Collaboration/YSync.ts +16 -0
- package/src/extensions/Collaboration/YUndo.ts +12 -0
- package/src/extensions/Collaboration/schemaMigration/SchemaMigration.ts +59 -0
- package/src/extensions/DropCursor/DropCursor.ts +26 -0
- package/src/extensions/FilePanel/FilePanel.ts +41 -0
- package/src/extensions/FormattingToolbar/FormattingToolbar.ts +119 -0
- package/src/extensions/History/History.ts +11 -0
- package/src/extensions/LinkToolbar/LinkToolbar.ts +121 -0
- package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboard.ts +74 -0
- package/src/extensions/Placeholder/Placeholder.ts +148 -0
- package/src/extensions/PreviousBlockType/{PreviousBlockTypePlugin.ts → PreviousBlockType.ts} +9 -13
- package/src/extensions/ShowSelection/{ShowSelectionPlugin.ts → ShowSelection.ts} +27 -33
- package/src/extensions/SideMenu/{SideMenuPlugin.ts → SideMenu.ts} +63 -83
- package/src/extensions/SuggestionMenu/{SuggestionPlugin.ts → SuggestionMenu.ts} +71 -77
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +29 -44
- package/src/extensions/TableHandles/{TableHandlesPlugin.ts → TableHandles.ts} +416 -437
- package/src/extensions/TrailingNode/{TrailingNodeExtension.ts → TrailingNode.ts} +8 -17
- package/src/extensions/index.ts +24 -0
- package/src/extensions/{BackgroundColor → tiptap-extensions/BackgroundColor}/BackgroundColorExtension.ts +1 -1
- package/src/extensions/{KeyboardShortcuts → tiptap-extensions/KeyboardShortcuts}/KeyboardShortcutsExtension.ts +21 -16
- package/src/extensions/{TextColor → tiptap-extensions/TextColor}/TextColorExtension.ts +1 -1
- package/src/extensions/tiptap-extensions/index.ts +31 -0
- package/src/index.ts +1 -13
- package/src/schema/blocks/createSpec.ts +14 -11
- package/src/schema/blocks/internal.ts +2 -2
- package/src/schema/blocks/types.ts +8 -5
- package/src/schema/schema.ts +11 -36
- package/src/util/topo-sort.ts +46 -0
- package/types/src/comments/extension.d.ts +70 -0
- package/types/src/comments/index.d.ts +2 -1
- package/types/src/comments/types.d.ts +8 -0
- package/types/src/{extensions/Comments → comments}/userstore/UserStore.d.ts +2 -2
- package/types/src/editor/BlockNoteEditor.d.ts +34 -105
- package/types/src/editor/BlockNoteExtension.d.ts +87 -22
- package/types/src/editor/managers/EventManager.d.ts +25 -16
- package/types/src/editor/managers/ExtensionManager/extensions.d.ts +8 -0
- package/types/src/editor/managers/ExtensionManager/index.d.ts +83 -0
- package/types/src/editor/managers/ExtensionManager/symbol.d.ts +5 -0
- package/types/src/editor/managers/StateManager.d.ts +1 -12
- package/types/src/editor/managers/index.d.ts +1 -2
- package/types/src/extensions/BlockChange/BlockChange.d.ts +16 -0
- package/types/src/extensions/Collaboration/ForkYDoc.d.ts +34 -0
- package/types/src/extensions/Collaboration/ForkYDoc.test.d.ts +1 -0
- package/types/src/extensions/Collaboration/YCursorPlugin.d.ts +24 -0
- package/types/src/extensions/Collaboration/YSync.d.ts +8 -0
- package/types/src/extensions/Collaboration/YUndo.d.ts +12 -0
- package/types/src/extensions/Collaboration/schemaMigration/SchemaMigration.d.ts +8 -0
- package/types/src/extensions/DropCursor/DropCursor.d.ts +5 -0
- package/types/src/extensions/FilePanel/FilePanel.d.ts +11 -0
- package/types/src/extensions/FormattingToolbar/FormattingToolbar.d.ts +9 -0
- package/types/src/extensions/History/History.d.ts +6 -0
- package/types/src/extensions/LinkToolbar/LinkToolbar.d.ts +24 -0
- package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboard.d.ts +5 -0
- package/types/src/extensions/Placeholder/Placeholder.d.ts +6 -0
- package/types/src/extensions/PreviousBlockType/{PreviousBlockTypePlugin.d.ts → PreviousBlockType.d.ts} +9 -5
- package/types/src/extensions/ShowSelection/ShowSelection.d.ts +21 -0
- package/types/src/extensions/SideMenu/{SideMenuPlugin.d.ts → SideMenu.d.ts} +11 -15
- package/types/src/extensions/SuggestionMenu/SuggestionMenu.d.ts +54 -0
- package/types/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.d.ts +1 -1
- package/types/src/extensions/TableHandles/{TableHandlesPlugin.d.ts → TableHandles.d.ts} +28 -31
- package/types/src/extensions/TrailingNode/TrailingNode.d.ts +8 -0
- package/types/src/extensions/index.d.ts +24 -0
- package/types/src/extensions/{KeyboardShortcuts → tiptap-extensions/KeyboardShortcuts}/KeyboardShortcutsExtension.d.ts +1 -1
- package/types/src/extensions/tiptap-extensions/index.d.ts +11 -0
- package/types/src/index.d.ts +1 -13
- package/types/src/schema/blocks/createSpec.d.ts +4 -4
- package/types/src/schema/blocks/internal.d.ts +2 -2
- package/types/src/schema/blocks/types.d.ts +5 -5
- package/types/src/util/topo-sort.d.ts +8 -0
- package/dist/BlockNoteSchema-Bi-eeHal.js.map +0 -1
- package/dist/BlockNoteSchema-DjDaA2C3.cjs +0 -6
- package/dist/BlockNoteSchema-DjDaA2C3.cjs.map +0 -1
- package/dist/blockToNode-DIfPWLH8.js.map +0 -1
- package/src/comments/models/User.ts +0 -8
- package/src/editor/BlockNoteExtensions.ts +0 -325
- package/src/editor/managers/CollaborationManager.ts +0 -212
- package/src/editor/managers/ExtensionManager.ts +0 -130
- package/src/extensions/Collaboration/CursorPlugin.ts +0 -189
- package/src/extensions/Collaboration/ForkYDocPlugin.ts +0 -192
- package/src/extensions/Collaboration/SyncPlugin.ts +0 -18
- package/src/extensions/Collaboration/UndoPlugin.ts +0 -18
- package/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.ts +0 -59
- package/src/extensions/Comments/CommentsPlugin.ts +0 -392
- package/src/extensions/FilePanel/FilePanelPlugin.ts +0 -206
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +0 -363
- package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +0 -380
- package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts +0 -75
- package/src/extensions/Placeholder/PlaceholderPlugin.ts +0 -147
- package/types/src/comments/models/User.d.ts +0 -8
- package/types/src/editor/BlockNoteExtensions.d.ts +0 -43
- package/types/src/editor/managers/CollaborationManager.d.ts +0 -115
- package/types/src/editor/managers/ExtensionManager.d.ts +0 -68
- package/types/src/extensions/BlockChange/BlockChangePlugin.d.ts +0 -15
- package/types/src/extensions/Collaboration/CursorPlugin.d.ts +0 -37
- package/types/src/extensions/Collaboration/ForkYDocPlugin.d.ts +0 -41
- package/types/src/extensions/Collaboration/SyncPlugin.d.ts +0 -7
- package/types/src/extensions/Collaboration/UndoPlugin.d.ts +0 -9
- package/types/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.d.ts +0 -7
- package/types/src/extensions/Comments/CommentsPlugin.d.ts +0 -66
- package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +0 -31
- package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +0 -41
- package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +0 -42
- package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.d.ts +0 -5
- package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +0 -6
- package/types/src/extensions/ShowSelection/ShowSelectionPlugin.d.ts +0 -15
- package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +0 -31
- package/types/src/extensions/TrailingNode/TrailingNodeExtension.d.ts +0 -13
- /package/src/{extensions/Comments/CommentMark.ts → comments/mark.ts} +0 -0
- /package/src/extensions/{HardBreak → tiptap-extensions/HardBreak}/HardBreak.ts +0 -0
- /package/src/extensions/{Suggestions → tiptap-extensions/Suggestions}/SuggestionMarks.ts +0 -0
- /package/src/extensions/{TextAlignment → tiptap-extensions/TextAlignment}/TextAlignmentExtension.ts +0 -0
- /package/src/extensions/{UniqueID → tiptap-extensions/UniqueID}/UniqueID.ts +0 -0
- /package/types/src/{extensions/Comments/CommentMark.d.ts → comments/mark.d.ts} +0 -0
- /package/types/src/{extensions/Collaboration/ForkYDocPlugin.test.d.ts → editor/BlockNoteExtension.test.d.ts} +0 -0
- /package/types/src/extensions/{BackgroundColor → tiptap-extensions/BackgroundColor}/BackgroundColorExtension.d.ts +0 -0
- /package/types/src/extensions/{HardBreak → tiptap-extensions/HardBreak}/HardBreak.d.ts +0 -0
- /package/types/src/extensions/{Suggestions → tiptap-extensions/Suggestions}/SuggestionMarks.d.ts +0 -0
- /package/types/src/extensions/{TextAlignment → tiptap-extensions/TextAlignment}/TextAlignmentExtension.d.ts +0 -0
- /package/types/src/extensions/{TextColor → tiptap-extensions/TextColor}/TextColorExtension.d.ts +0 -0
- /package/types/src/extensions/{UniqueID → tiptap-extensions/UniqueID}/UniqueID.d.ts +0 -0
|
@@ -1,392 +0,0 @@
|
|
|
1
|
-
import { Node } from "prosemirror-model";
|
|
2
|
-
import { Plugin, PluginKey } from "prosemirror-state";
|
|
3
|
-
import { Decoration, DecorationSet } from "prosemirror-view";
|
|
4
|
-
import { getRelativeSelection, ySyncPluginKey } from "y-prosemirror";
|
|
5
|
-
import type {
|
|
6
|
-
CommentBody,
|
|
7
|
-
ThreadData,
|
|
8
|
-
ThreadStore,
|
|
9
|
-
User,
|
|
10
|
-
} from "../../comments/index.js";
|
|
11
|
-
import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
|
|
12
|
-
import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
|
|
13
|
-
import { CustomBlockNoteSchema } from "../../schema/schema.js";
|
|
14
|
-
import { UserStore } from "./userstore/UserStore.js";
|
|
15
|
-
import { getMarkRange } from "@tiptap/core";
|
|
16
|
-
|
|
17
|
-
const PLUGIN_KEY = new PluginKey(`blocknote-comments`);
|
|
18
|
-
const SET_SELECTED_THREAD_ID = "SET_SELECTED_THREAD_ID";
|
|
19
|
-
|
|
20
|
-
type CommentsPluginState = {
|
|
21
|
-
/**
|
|
22
|
-
* Decorations to be rendered, specifically to indicate the selected thread
|
|
23
|
-
*/
|
|
24
|
-
decorations: DecorationSet;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Calculate the thread positions from the current document state
|
|
29
|
-
*/
|
|
30
|
-
function getUpdatedThreadPositions(doc: Node, markType: string) {
|
|
31
|
-
const threadPositions = new Map<string, { from: number; to: number }>();
|
|
32
|
-
|
|
33
|
-
// find all thread marks and store their position + create decoration for selected thread
|
|
34
|
-
doc.descendants((node, pos) => {
|
|
35
|
-
node.marks.forEach((mark) => {
|
|
36
|
-
if (mark.type.name === markType) {
|
|
37
|
-
const thisThreadId = (mark.attrs as { threadId: string | undefined })
|
|
38
|
-
.threadId;
|
|
39
|
-
if (!thisThreadId) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const from = pos;
|
|
43
|
-
const to = from + node.nodeSize;
|
|
44
|
-
|
|
45
|
-
// FloatingThreads component uses "to" as the position, so always store the largest "to" found
|
|
46
|
-
// AnchoredThreads component uses "from" as the position, so always store the smallest "from" found
|
|
47
|
-
const currentPosition = threadPositions.get(thisThreadId) ?? {
|
|
48
|
-
from: Infinity,
|
|
49
|
-
to: 0,
|
|
50
|
-
};
|
|
51
|
-
threadPositions.set(thisThreadId, {
|
|
52
|
-
from: Math.min(from, currentPosition.from),
|
|
53
|
-
to: Math.max(to, currentPosition.to),
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
return threadPositions;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export class CommentsPlugin extends BlockNoteExtension {
|
|
62
|
-
public static key() {
|
|
63
|
-
return "comments";
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public readonly userStore: UserStore<User>;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Whether a comment is currently being composed
|
|
70
|
-
*/
|
|
71
|
-
private pendingComment = false;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* The currently selected thread id
|
|
75
|
-
*/
|
|
76
|
-
private selectedThreadId: string | undefined;
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Store the positions of all threads in the document.
|
|
80
|
-
* this can be used later to implement a floating sidebar
|
|
81
|
-
*/
|
|
82
|
-
private threadPositions: Map<string, { from: number; to: number }> =
|
|
83
|
-
new Map();
|
|
84
|
-
|
|
85
|
-
private emitStateUpdate() {
|
|
86
|
-
this.emit("update", {
|
|
87
|
-
selectedThreadId: this.selectedThreadId,
|
|
88
|
-
pendingComment: this.pendingComment,
|
|
89
|
-
threadPositions: this.threadPositions,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* when a thread is resolved or deleted, we need to update the marks to reflect the new state
|
|
95
|
-
*/
|
|
96
|
-
private updateMarksFromThreads = (threads: Map<string, ThreadData>) => {
|
|
97
|
-
this.editor.transact((tr) => {
|
|
98
|
-
tr.doc.descendants((node, pos) => {
|
|
99
|
-
node.marks.forEach((mark) => {
|
|
100
|
-
if (mark.type.name === this.markType) {
|
|
101
|
-
const markType = mark.type;
|
|
102
|
-
const markThreadId = mark.attrs.threadId;
|
|
103
|
-
const thread = threads.get(markThreadId);
|
|
104
|
-
const isOrphan = !!(!thread || thread.resolved || thread.deletedAt);
|
|
105
|
-
|
|
106
|
-
if (isOrphan !== mark.attrs.orphan) {
|
|
107
|
-
const trimmedFrom = Math.max(pos, 0);
|
|
108
|
-
const trimmedTo = Math.min(
|
|
109
|
-
pos + node.nodeSize,
|
|
110
|
-
tr.doc.content.size - 1,
|
|
111
|
-
tr.doc.content.size - 1,
|
|
112
|
-
);
|
|
113
|
-
tr.removeMark(trimmedFrom, trimmedTo, mark);
|
|
114
|
-
tr.addMark(
|
|
115
|
-
trimmedFrom,
|
|
116
|
-
trimmedTo,
|
|
117
|
-
markType.create({
|
|
118
|
-
...mark.attrs,
|
|
119
|
-
orphan: isOrphan,
|
|
120
|
-
}),
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
if (isOrphan && this.selectedThreadId === markThreadId) {
|
|
124
|
-
// unselect
|
|
125
|
-
this.selectedThreadId = undefined;
|
|
126
|
-
this.emitStateUpdate();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
constructor(
|
|
136
|
-
private readonly editor: BlockNoteEditor<any, any, any>,
|
|
137
|
-
public readonly threadStore: ThreadStore,
|
|
138
|
-
private readonly markType: string,
|
|
139
|
-
public readonly resolveUsers:
|
|
140
|
-
| undefined
|
|
141
|
-
| ((userIds: string[]) => Promise<User[]>),
|
|
142
|
-
public readonly commentEditorSchema?: CustomBlockNoteSchema<any, any, any>,
|
|
143
|
-
) {
|
|
144
|
-
super();
|
|
145
|
-
|
|
146
|
-
if (!resolveUsers) {
|
|
147
|
-
throw new Error("resolveUsers is required for comments");
|
|
148
|
-
}
|
|
149
|
-
this.userStore = new UserStore<User>(resolveUsers);
|
|
150
|
-
|
|
151
|
-
// Note: Plugins are currently not destroyed when the editor is destroyed.
|
|
152
|
-
// We should unsubscribe from the threadStore when the editor is destroyed.
|
|
153
|
-
this.threadStore.subscribe(this.updateMarksFromThreads);
|
|
154
|
-
|
|
155
|
-
editor.onCreate(() => {
|
|
156
|
-
// Need to wait for TipTap editor state to be initialized
|
|
157
|
-
this.updateMarksFromThreads(this.threadStore.getThreads());
|
|
158
|
-
editor.onSelectionChange(() => {
|
|
159
|
-
if (this.pendingComment) {
|
|
160
|
-
this.pendingComment = false;
|
|
161
|
-
this.emitStateUpdate();
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
167
|
-
const self = this;
|
|
168
|
-
|
|
169
|
-
this.addProsemirrorPlugin(
|
|
170
|
-
new Plugin<CommentsPluginState>({
|
|
171
|
-
key: PLUGIN_KEY,
|
|
172
|
-
state: {
|
|
173
|
-
init() {
|
|
174
|
-
return {
|
|
175
|
-
decorations: DecorationSet.empty,
|
|
176
|
-
};
|
|
177
|
-
},
|
|
178
|
-
apply(tr, state) {
|
|
179
|
-
const action = tr.getMeta(PLUGIN_KEY);
|
|
180
|
-
|
|
181
|
-
if (!tr.docChanged && !action) {
|
|
182
|
-
return state;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// only update threadPositions if the doc changed
|
|
186
|
-
const threadPositions = tr.docChanged
|
|
187
|
-
? getUpdatedThreadPositions(tr.doc, self.markType)
|
|
188
|
-
: self.threadPositions;
|
|
189
|
-
|
|
190
|
-
if (threadPositions.size > 0 || self.threadPositions.size > 0) {
|
|
191
|
-
// small optimization; don't emit event if threadPositions before / after were both empty
|
|
192
|
-
self.threadPositions = threadPositions;
|
|
193
|
-
self.emitStateUpdate();
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// update decorations if doc or selected thread changed
|
|
197
|
-
const decorations = [];
|
|
198
|
-
|
|
199
|
-
if (self.selectedThreadId) {
|
|
200
|
-
const selectedThreadPosition = threadPositions.get(
|
|
201
|
-
self.selectedThreadId,
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
if (selectedThreadPosition) {
|
|
205
|
-
decorations.push(
|
|
206
|
-
Decoration.inline(
|
|
207
|
-
selectedThreadPosition.from,
|
|
208
|
-
selectedThreadPosition.to,
|
|
209
|
-
{
|
|
210
|
-
class: "bn-thread-mark-selected",
|
|
211
|
-
},
|
|
212
|
-
),
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
return {
|
|
218
|
-
decorations: DecorationSet.create(tr.doc, decorations),
|
|
219
|
-
};
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
props: {
|
|
223
|
-
decorations(state) {
|
|
224
|
-
return (
|
|
225
|
-
PLUGIN_KEY.getState(state)?.decorations ?? DecorationSet.empty
|
|
226
|
-
);
|
|
227
|
-
},
|
|
228
|
-
/**
|
|
229
|
-
* Handle click on a thread mark and mark it as selected
|
|
230
|
-
*/
|
|
231
|
-
handleClick: (view, pos, event) => {
|
|
232
|
-
if (event.button !== 0) {
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const node = view.state.doc.nodeAt(pos);
|
|
237
|
-
|
|
238
|
-
if (!node) {
|
|
239
|
-
self.selectThread(undefined);
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const markInSchema = view.state.schema.marks[markType];
|
|
244
|
-
const resolvedPos = view.state.doc.resolve(pos);
|
|
245
|
-
const commentMark = node.marks
|
|
246
|
-
.filter(
|
|
247
|
-
(mark) =>
|
|
248
|
-
mark.type.name === markType && mark.attrs.orphan !== true,
|
|
249
|
-
)
|
|
250
|
-
.map((mark) => {
|
|
251
|
-
// get the range of this mark within the document, to check how close it is to the click position
|
|
252
|
-
const range = getMarkRange(
|
|
253
|
-
resolvedPos,
|
|
254
|
-
markInSchema,
|
|
255
|
-
mark.attrs,
|
|
256
|
-
)!;
|
|
257
|
-
|
|
258
|
-
return {
|
|
259
|
-
mark,
|
|
260
|
-
// calculate how far the mark is from the click position
|
|
261
|
-
distance:
|
|
262
|
-
(Math.abs(range.from - pos) + Math.abs(range.to - pos)) / 2,
|
|
263
|
-
// calculate the length of text the mark spans
|
|
264
|
-
length: range.to - range.from,
|
|
265
|
-
};
|
|
266
|
-
})
|
|
267
|
-
// This allows us to not have comments which are unreachable because they are completely overlapped by other comments (issue #2073)
|
|
268
|
-
.sort((a, b) => {
|
|
269
|
-
// Find the mark which is closest to the click position
|
|
270
|
-
if (a.distance !== b.distance) {
|
|
271
|
-
return a.distance - b.distance;
|
|
272
|
-
}
|
|
273
|
-
// Otherwise, select the mark which spans the smallest amount of text (most likely to be unreachable)
|
|
274
|
-
return a.length - b.length;
|
|
275
|
-
})[0]?.mark;
|
|
276
|
-
|
|
277
|
-
const threadId = commentMark?.attrs.threadId as string | undefined;
|
|
278
|
-
self.selectThread(threadId);
|
|
279
|
-
},
|
|
280
|
-
},
|
|
281
|
-
}),
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Subscribe to state updates
|
|
287
|
-
*/
|
|
288
|
-
public onUpdate(
|
|
289
|
-
callback: (state: {
|
|
290
|
-
pendingComment: boolean;
|
|
291
|
-
selectedThreadId: string | undefined;
|
|
292
|
-
threadPositions: Map<string, { from: number; to: number }>;
|
|
293
|
-
}) => void,
|
|
294
|
-
) {
|
|
295
|
-
return this.on("update", callback);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* Set the selected thread
|
|
300
|
-
*/
|
|
301
|
-
public selectThread(threadId: string | undefined) {
|
|
302
|
-
if (this.selectedThreadId === threadId) {
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
305
|
-
this.selectedThreadId = threadId;
|
|
306
|
-
this.emitStateUpdate();
|
|
307
|
-
this.editor.transact((tr) =>
|
|
308
|
-
tr.setMeta(PLUGIN_KEY, {
|
|
309
|
-
name: SET_SELECTED_THREAD_ID,
|
|
310
|
-
}),
|
|
311
|
-
);
|
|
312
|
-
|
|
313
|
-
if (threadId) {
|
|
314
|
-
const selectedThreadPosition = this.threadPositions.get(threadId);
|
|
315
|
-
|
|
316
|
-
if (!selectedThreadPosition) {
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// When a new thread is selected, scrolls the page to its reference text in
|
|
321
|
-
// the editor.
|
|
322
|
-
(
|
|
323
|
-
this.editor.prosemirrorView?.domAtPos(selectedThreadPosition.from)
|
|
324
|
-
.node as Element | undefined
|
|
325
|
-
)?.scrollIntoView({
|
|
326
|
-
behavior: "smooth",
|
|
327
|
-
block: "center",
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Start a pending comment (e.g.: when clicking the "Add comment" button)
|
|
334
|
-
*/
|
|
335
|
-
public startPendingComment() {
|
|
336
|
-
this.pendingComment = true;
|
|
337
|
-
this.emitStateUpdate();
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Stop a pending comment (e.g.: user closes the comment composer)
|
|
342
|
-
*/
|
|
343
|
-
public stopPendingComment() {
|
|
344
|
-
this.pendingComment = false;
|
|
345
|
-
this.emitStateUpdate();
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
/**
|
|
349
|
-
* Create a thread at the current selection
|
|
350
|
-
*/
|
|
351
|
-
public async createThread(options: {
|
|
352
|
-
initialComment: {
|
|
353
|
-
body: CommentBody;
|
|
354
|
-
metadata?: any;
|
|
355
|
-
};
|
|
356
|
-
metadata?: any;
|
|
357
|
-
}) {
|
|
358
|
-
const thread = await this.threadStore.createThread(options);
|
|
359
|
-
|
|
360
|
-
if (this.threadStore.addThreadToDocument) {
|
|
361
|
-
// creating the mark is handled by the store
|
|
362
|
-
// this is useful if we don't have write-access to the document.
|
|
363
|
-
// We can then offload the responsibility of creating the mark to the server.
|
|
364
|
-
// (e.g.: RESTYjsThreadStore)
|
|
365
|
-
const view = this.editor.prosemirrorView!;
|
|
366
|
-
const pmSelection = view.state.selection;
|
|
367
|
-
|
|
368
|
-
const ystate = ySyncPluginKey.getState(view.state);
|
|
369
|
-
|
|
370
|
-
const selection = {
|
|
371
|
-
prosemirror: {
|
|
372
|
-
head: pmSelection.head,
|
|
373
|
-
anchor: pmSelection.anchor,
|
|
374
|
-
},
|
|
375
|
-
yjs: ystate
|
|
376
|
-
? getRelativeSelection(ystate.binding, view.state)
|
|
377
|
-
: undefined, // if we're not using yjs
|
|
378
|
-
};
|
|
379
|
-
|
|
380
|
-
await this.threadStore.addThreadToDocument({
|
|
381
|
-
threadId: thread.id,
|
|
382
|
-
selection,
|
|
383
|
-
});
|
|
384
|
-
} else {
|
|
385
|
-
// we create the mark directly in the document
|
|
386
|
-
this.editor._tiptapEditor.commands.setMark(this.markType, {
|
|
387
|
-
orphan: false,
|
|
388
|
-
threadId: thread.id,
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
}
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
|
|
2
|
-
import { EditorView } from "prosemirror-view";
|
|
3
|
-
|
|
4
|
-
import { ySyncPluginKey } from "y-prosemirror";
|
|
5
|
-
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
|
|
6
|
-
import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
|
|
7
|
-
import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
|
|
8
|
-
import type {
|
|
9
|
-
BlockFromConfig,
|
|
10
|
-
InlineContentSchema,
|
|
11
|
-
StyleSchema,
|
|
12
|
-
} from "../../schema/index.js";
|
|
13
|
-
|
|
14
|
-
export type FilePanelState<
|
|
15
|
-
I extends InlineContentSchema,
|
|
16
|
-
S extends StyleSchema,
|
|
17
|
-
> = UiElementPosition & {
|
|
18
|
-
// TODO: This typing is not quite right (children should be from BSchema)
|
|
19
|
-
block: BlockFromConfig<any, I, S>;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export class FilePanelView<I extends InlineContentSchema, S extends StyleSchema>
|
|
23
|
-
implements PluginView
|
|
24
|
-
{
|
|
25
|
-
public state?: FilePanelState<I, S>;
|
|
26
|
-
public emitUpdate: () => void;
|
|
27
|
-
|
|
28
|
-
constructor(
|
|
29
|
-
private readonly editor: BlockNoteEditor<Record<string, any>, I, S>,
|
|
30
|
-
private readonly pluginKey: PluginKey<FilePanelState<I, S>>,
|
|
31
|
-
private readonly pmView: EditorView,
|
|
32
|
-
emitUpdate: (state: FilePanelState<I, S>) => void,
|
|
33
|
-
) {
|
|
34
|
-
this.emitUpdate = () => {
|
|
35
|
-
if (!this.state) {
|
|
36
|
-
throw new Error("Attempting to update uninitialized file panel");
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
emitUpdate(this.state);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
pmView.dom.addEventListener("mousedown", this.mouseDownHandler);
|
|
43
|
-
pmView.dom.addEventListener("dragstart", this.dragstartHandler);
|
|
44
|
-
|
|
45
|
-
// Setting capture=true ensures that any parent container of the editor that
|
|
46
|
-
// gets scrolled will trigger the scroll event. Scroll events do not bubble
|
|
47
|
-
// and so won't propagate to the document by default.
|
|
48
|
-
pmView.root.addEventListener("scroll", this.scrollHandler, true);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
mouseDownHandler = () => {
|
|
52
|
-
if (this.state?.show) {
|
|
53
|
-
this.state.show = false;
|
|
54
|
-
this.emitUpdate();
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// For dragging the whole editor.
|
|
59
|
-
dragstartHandler = () => {
|
|
60
|
-
if (this.state?.show) {
|
|
61
|
-
this.state.show = false;
|
|
62
|
-
this.emitUpdate();
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
scrollHandler = () => {
|
|
67
|
-
if (this.state?.show) {
|
|
68
|
-
const blockElement = this.pmView.root.querySelector(
|
|
69
|
-
`[data-node-type="blockContainer"][data-id="${this.state.block.id}"]`,
|
|
70
|
-
);
|
|
71
|
-
if (!blockElement) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
this.state.referencePos = blockElement.getBoundingClientRect();
|
|
75
|
-
this.emitUpdate();
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
update(view: EditorView, prevState: EditorState) {
|
|
80
|
-
const pluginState = this.pluginKey.getState(view.state);
|
|
81
|
-
const prevPluginState = this.pluginKey.getState(prevState);
|
|
82
|
-
|
|
83
|
-
if (!this.state?.show && pluginState?.block && this.editor.isEditable) {
|
|
84
|
-
const blockElement = this.pmView.root.querySelector(
|
|
85
|
-
`[data-node-type="blockContainer"][data-id="${pluginState.block.id}"]`,
|
|
86
|
-
);
|
|
87
|
-
if (!blockElement) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
this.state = {
|
|
91
|
-
show: true,
|
|
92
|
-
referencePos: blockElement.getBoundingClientRect(),
|
|
93
|
-
block: pluginState.block,
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
this.emitUpdate();
|
|
97
|
-
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const isOpening = pluginState?.block && !prevPluginState?.block;
|
|
102
|
-
const isClosing = !pluginState?.block && prevPluginState?.block;
|
|
103
|
-
if (isOpening && this.state && !this.state.show) {
|
|
104
|
-
this.state.show = true;
|
|
105
|
-
this.emitUpdate();
|
|
106
|
-
}
|
|
107
|
-
if (isClosing && this.state?.show) {
|
|
108
|
-
this.state.show = false;
|
|
109
|
-
this.emitUpdate();
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
closeMenu = () => {
|
|
114
|
-
if (this.state?.show) {
|
|
115
|
-
this.state.show = false;
|
|
116
|
-
this.emitUpdate();
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
destroy() {
|
|
121
|
-
this.pmView.dom.removeEventListener("mousedown", this.mouseDownHandler);
|
|
122
|
-
|
|
123
|
-
this.pmView.dom.removeEventListener("dragstart", this.dragstartHandler);
|
|
124
|
-
|
|
125
|
-
this.pmView.root.removeEventListener("scroll", this.scrollHandler, true);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const filePanelPluginKey = new PluginKey<FilePanelState<any, any>>(
|
|
130
|
-
"FilePanelPlugin",
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
export class FilePanelProsemirrorPlugin<
|
|
134
|
-
I extends InlineContentSchema,
|
|
135
|
-
S extends StyleSchema,
|
|
136
|
-
> extends BlockNoteExtension {
|
|
137
|
-
public static key() {
|
|
138
|
-
return "filePanel";
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
private view: FilePanelView<I, S> | undefined;
|
|
142
|
-
|
|
143
|
-
constructor(editor: BlockNoteEditor<Record<string, any>, I, S>) {
|
|
144
|
-
super();
|
|
145
|
-
this.addProsemirrorPlugin(
|
|
146
|
-
new Plugin<{
|
|
147
|
-
block: BlockFromConfig<any, I, S> | undefined;
|
|
148
|
-
}>({
|
|
149
|
-
key: filePanelPluginKey,
|
|
150
|
-
view: (editorView) => {
|
|
151
|
-
this.view = new FilePanelView<I, S>(
|
|
152
|
-
editor,
|
|
153
|
-
filePanelPluginKey as any,
|
|
154
|
-
editorView,
|
|
155
|
-
(state) => {
|
|
156
|
-
this.emit("update", state);
|
|
157
|
-
},
|
|
158
|
-
);
|
|
159
|
-
return this.view;
|
|
160
|
-
},
|
|
161
|
-
props: {
|
|
162
|
-
handleKeyDown: (_view, event: KeyboardEvent) => {
|
|
163
|
-
if (event.key === "Escape" && this.shown) {
|
|
164
|
-
this.view?.closeMenu();
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
return false;
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
state: {
|
|
171
|
-
init: () => {
|
|
172
|
-
return {
|
|
173
|
-
block: undefined,
|
|
174
|
-
};
|
|
175
|
-
},
|
|
176
|
-
apply: (transaction, prev) => {
|
|
177
|
-
const state: FilePanelState<I, S> | undefined =
|
|
178
|
-
transaction.getMeta(filePanelPluginKey);
|
|
179
|
-
|
|
180
|
-
if (state) {
|
|
181
|
-
return state;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (
|
|
185
|
-
!transaction.getMeta(ySyncPluginKey) &&
|
|
186
|
-
(transaction.selectionSet || transaction.docChanged)
|
|
187
|
-
) {
|
|
188
|
-
return { block: undefined };
|
|
189
|
-
}
|
|
190
|
-
return prev;
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
}),
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
public get shown() {
|
|
198
|
-
return this.view?.state?.show || false;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
public onUpdate(callback: (state: FilePanelState<I, S>) => void) {
|
|
202
|
-
return this.on("update", callback);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
public closeMenu = () => this.view?.closeMenu();
|
|
206
|
-
}
|