@blocknote/core 0.30.1 → 0.31.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/blocknote.cjs +9 -9
- package/dist/blocknote.cjs.map +1 -1
- package/dist/blocknote.js +2754 -2230
- package/dist/blocknote.js.map +1 -1
- package/dist/{en-D4taoCs4.cjs → en-BXVKCwYt.cjs} +2 -2
- package/dist/en-BXVKCwYt.cjs.map +1 -0
- package/dist/{en-B7ycW7c8.js → en-qGo6sk9V.js} +2 -3
- package/dist/en-qGo6sk9V.js.map +1 -0
- package/dist/locales.cjs +1 -1
- package/dist/locales.cjs.map +1 -1
- package/dist/locales.js +20 -39
- package/dist/locales.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +4 -5
- package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +2 -3
- package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +1 -1
- package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +2816 -0
- package/src/api/blockManipulation/commands/updateBlock/updateBlock.test.ts +158 -0
- package/src/api/blockManipulation/commands/updateBlock/updateBlock.ts +87 -17
- package/src/api/blockManipulation/selections/selection.ts +48 -1
- package/src/api/blockManipulation/selections/{textCursorPosition/textCursorPosition.ts → textCursorPosition.ts} +7 -7
- package/src/api/getBlockInfoFromPos.ts +1 -1
- package/src/api/nodeConversions/blockToNode.ts +5 -2
- package/src/api/nodeConversions/nodeToBlock.ts +203 -8
- package/src/api/pmUtil.ts +3 -3
- package/src/blocks/CodeBlockContent/CodeBlockContent.ts +6 -6
- package/src/blocks/FileBlockContent/helpers/render/createAddFileButton.ts +1 -1
- package/src/blocks/TableBlockContent/TableBlockContent.ts +32 -2
- package/src/editor/Block.css +27 -1
- package/src/editor/BlockNoteEditor.test.ts +7 -0
- package/src/editor/BlockNoteEditor.ts +88 -37
- package/src/editor/BlockNoteExtension.ts +26 -0
- package/src/editor/BlockNoteExtensions.ts +28 -12
- package/src/editor/BlockNoteTipTapEditor.ts +23 -2
- package/src/extensions/Collaboration/CursorPlugin.ts +13 -7
- package/src/extensions/Collaboration/ForkYDocPlugin.test.ts +166 -0
- package/src/extensions/Collaboration/ForkYDocPlugin.ts +174 -0
- package/src/extensions/Collaboration/SyncPlugin.ts +7 -4
- package/src/extensions/Collaboration/UndoPlugin.ts +7 -4
- package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json +30 -0
- package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json +30 -0
- package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html +1 -0
- package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html +1 -0
- package/src/extensions/Comments/CommentsPlugin.ts +75 -70
- package/src/extensions/FilePanel/FilePanelPlugin.ts +50 -49
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +56 -26
- package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +22 -21
- package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts +45 -42
- package/src/extensions/Placeholder/PlaceholderPlugin.ts +111 -108
- package/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts +179 -170
- package/src/extensions/ShowSelection/ShowSelectionPlugin.ts +22 -19
- package/src/extensions/SideMenu/SideMenuPlugin.ts +19 -18
- package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +168 -168
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +4 -4
- package/src/extensions/Suggestions/SuggestionMarks.ts +175 -0
- package/src/extensions/TableHandles/TableHandlesPlugin.ts +153 -150
- package/src/i18n/locales/ar.ts +0 -1
- package/src/i18n/locales/de.ts +0 -1
- package/src/i18n/locales/en.ts +0 -1
- package/src/i18n/locales/es.ts +0 -1
- package/src/i18n/locales/fr.ts +0 -1
- package/src/i18n/locales/hr.ts +0 -1
- package/src/i18n/locales/is.ts +0 -1
- package/src/i18n/locales/it.ts +0 -1
- package/src/i18n/locales/ja.ts +0 -1
- package/src/i18n/locales/ko.ts +0 -1
- package/src/i18n/locales/nl.ts +0 -1
- package/src/i18n/locales/no.ts +0 -1
- package/src/i18n/locales/pl.ts +0 -1
- package/src/i18n/locales/pt.ts +0 -1
- package/src/i18n/locales/ru.ts +0 -1
- package/src/i18n/locales/sk.ts +0 -1
- package/src/i18n/locales/uk.ts +0 -1
- package/src/i18n/locales/vi.ts +0 -1
- package/src/i18n/locales/zh-tw.ts +0 -1
- package/src/i18n/locales/zh.ts +0 -1
- package/src/index.ts +18 -8
- package/src/pm-nodes/BlockContainer.ts +1 -1
- package/src/pm-nodes/BlockGroup.ts +1 -1
- package/src/pm-nodes/Doc.ts +1 -0
- package/types/src/api/blockManipulation/commands/insertBlocks/insertBlocks.d.ts +1 -1
- package/types/src/api/blockManipulation/commands/removeBlocks/removeBlocks.d.ts +4 -0
- package/types/src/api/blockManipulation/commands/removeBlocks/removeBlocks.test.d.ts +1 -0
- package/types/src/api/blockManipulation/commands/updateBlock/updateBlock.d.ts +3 -1
- package/types/src/api/blockManipulation/selections/selection.d.ts +10 -0
- package/types/src/api/blockManipulation/selections/textCursorPosition.d.ts +5 -0
- package/types/src/api/blockManipulation/transactions.test.d.ts +0 -0
- package/types/src/api/clipboard/clipboardExternal.test.d.ts +1 -0
- package/types/src/api/clipboard/clipboardInternal.test.d.ts +1 -0
- package/types/src/api/clipboard/testUtil.d.ts +541 -0
- package/types/src/api/exporters/html/htmlConversion.test.d.ts +1 -0
- package/types/src/api/exporters/markdown/markdownExporter.test.d.ts +1 -0
- package/types/src/api/nodeConversions/nodeConversions.test.d.ts +1 -0
- package/types/src/api/nodeConversions/nodeToBlock.d.ts +39 -2
- package/types/src/api/parsers/html/parseHTML.test.d.ts +1 -0
- package/types/src/api/parsers/markdown/parseMarkdown.test.d.ts +1 -0
- package/types/src/api/pmUtil.d.ts +3 -3
- package/types/src/api/testUtil/cases/customBlocks.d.ts +670 -0
- package/types/src/api/testUtil/cases/customInlineContent.d.ts +558 -0
- package/types/src/api/testUtil/cases/customStyles.d.ts +552 -0
- package/types/src/api/testUtil/cases/defaultSchema.d.ts +4 -0
- package/types/src/api/testUtil/index.d.ts +14 -0
- package/types/src/api/testUtil/partialBlockTestUtil.d.ts +9 -0
- package/types/src/api/testUtil/paste.d.ts +2 -0
- package/types/src/blocks/CodeBlockContent/defaultSupportedLanguages.d.ts +6 -0
- package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +9 -1
- package/types/src/editor/BlockNoteEditor.d.ts +55 -9
- package/types/src/editor/BlockNoteExtension.d.ts +9 -0
- package/types/src/editor/BlockNoteExtensions.d.ts +2 -2
- package/types/src/editor/BlockNoteTipTapEditor.d.ts +2 -2
- package/types/src/extensions/Collaboration/CursorPlugin.d.ts +3 -3
- package/types/src/extensions/Collaboration/ForkYDocPlugin.d.ts +41 -0
- package/types/src/extensions/Collaboration/ForkYDocPlugin.test.d.ts +1 -0
- package/types/src/extensions/Collaboration/SyncPlugin.d.ts +3 -3
- package/types/src/extensions/Collaboration/UndoPlugin.d.ts +3 -3
- package/types/src/extensions/Collaboration/createCollaborationExtensions.d.ts +17 -0
- package/types/src/extensions/Comments/CommentsPlugin.d.ts +2 -4
- package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +3 -4
- package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +5 -5
- package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +3 -4
- package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.d.ts +2 -3
- package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +2 -3
- package/types/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.d.ts +2 -3
- package/types/src/extensions/ShowSelection/ShowSelectionPlugin.d.ts +2 -3
- package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +3 -4
- package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +2 -4
- package/types/src/extensions/Suggestions/SuggestionMarks.d.ts +4 -0
- package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +5 -6
- package/types/src/i18n/locales/en.d.ts +0 -1
- package/types/src/i18n/locales/sk.d.ts +0 -1
- package/types/src/index.d.ts +15 -8
- package/dist/en-B7ycW7c8.js.map +0 -1
- package/dist/en-D4taoCs4.cjs.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/api/blockManipulation/selections/__snapshots__/selection.test.ts.snap +0 -844
- package/src/api/blockManipulation/selections/selection.test.ts +0 -72
- package/src/api/blockManipulation/selections/textCursorPosition/__snapshots__/textCursorPosition.test.ts.snap +0 -316
- package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.ts +0 -74
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
User,
|
|
10
10
|
} from "../../comments/index.js";
|
|
11
11
|
import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
|
|
12
|
-
import {
|
|
12
|
+
import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
|
|
13
13
|
import { UserStore } from "./userstore/UserStore.js";
|
|
14
14
|
|
|
15
15
|
const PLUGIN_KEY = new PluginKey(`blocknote-comments`);
|
|
@@ -56,8 +56,7 @@ function getUpdatedThreadPositions(doc: Node, markType: string) {
|
|
|
56
56
|
return threadPositions;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export class CommentsPlugin extends
|
|
60
|
-
public readonly plugin: Plugin;
|
|
59
|
+
export class CommentsPlugin extends BlockNoteExtension {
|
|
61
60
|
public readonly userStore: UserStore<User>;
|
|
62
61
|
|
|
63
62
|
/**
|
|
@@ -103,6 +102,7 @@ export class CommentsPlugin extends EventEmitter<any> {
|
|
|
103
102
|
const trimmedTo = Math.min(
|
|
104
103
|
pos + node.nodeSize,
|
|
105
104
|
tr.doc.content.size - 1,
|
|
105
|
+
tr.doc.content.size - 1,
|
|
106
106
|
);
|
|
107
107
|
tr.removeMark(trimmedFrom, trimmedTo, mark);
|
|
108
108
|
tr.addMark(
|
|
@@ -156,86 +156,91 @@ export class CommentsPlugin extends EventEmitter<any> {
|
|
|
156
156
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
157
157
|
const self = this;
|
|
158
158
|
|
|
159
|
-
this.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
159
|
+
this.addProsemirrorPlugin(
|
|
160
|
+
new Plugin<CommentsPluginState>({
|
|
161
|
+
key: PLUGIN_KEY,
|
|
162
|
+
state: {
|
|
163
|
+
init() {
|
|
164
|
+
return {
|
|
165
|
+
decorations: DecorationSet.empty,
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
apply(tr, state) {
|
|
169
|
+
const action = tr.getMeta(PLUGIN_KEY);
|
|
170
|
+
|
|
171
|
+
if (!tr.docChanged && !action) {
|
|
172
|
+
return state;
|
|
173
|
+
}
|
|
173
174
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
175
|
+
// only update threadPositions if the doc changed
|
|
176
|
+
const threadPositions = tr.docChanged
|
|
177
|
+
? getUpdatedThreadPositions(tr.doc, self.markType)
|
|
178
|
+
: self.threadPositions;
|
|
178
179
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
// update decorations if doc or selected thread changed
|
|
186
|
-
const decorations = [];
|
|
180
|
+
if (threadPositions.size > 0 || self.threadPositions.size > 0) {
|
|
181
|
+
// small optimization; don't emit event if threadPositions before / after were both empty
|
|
182
|
+
self.threadPositions = threadPositions;
|
|
183
|
+
self.emitStateUpdate();
|
|
184
|
+
}
|
|
187
185
|
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
self.selectedThreadId,
|
|
191
|
-
);
|
|
186
|
+
// update decorations if doc or selected thread changed
|
|
187
|
+
const decorations = [];
|
|
192
188
|
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
selectedThreadPosition.from,
|
|
197
|
-
selectedThreadPosition.to,
|
|
198
|
-
{
|
|
199
|
-
class: "bn-thread-mark-selected",
|
|
200
|
-
},
|
|
201
|
-
),
|
|
189
|
+
if (self.selectedThreadId) {
|
|
190
|
+
const selectedThreadPosition = threadPositions.get(
|
|
191
|
+
self.selectedThreadId,
|
|
202
192
|
);
|
|
193
|
+
|
|
194
|
+
if (selectedThreadPosition) {
|
|
195
|
+
decorations.push(
|
|
196
|
+
Decoration.inline(
|
|
197
|
+
selectedThreadPosition.from,
|
|
198
|
+
selectedThreadPosition.to,
|
|
199
|
+
{
|
|
200
|
+
class: "bn-thread-mark-selected",
|
|
201
|
+
},
|
|
202
|
+
),
|
|
203
|
+
);
|
|
204
|
+
}
|
|
203
205
|
}
|
|
204
|
-
}
|
|
205
206
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
},
|
|
211
|
-
props: {
|
|
212
|
-
decorations(state) {
|
|
213
|
-
return PLUGIN_KEY.getState(state)?.decorations ?? DecorationSet.empty;
|
|
207
|
+
return {
|
|
208
|
+
decorations: DecorationSet.create(tr.doc, decorations),
|
|
209
|
+
};
|
|
210
|
+
},
|
|
214
211
|
},
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
212
|
+
props: {
|
|
213
|
+
decorations(state) {
|
|
214
|
+
return (
|
|
215
|
+
PLUGIN_KEY.getState(state)?.decorations ?? DecorationSet.empty
|
|
216
|
+
);
|
|
217
|
+
},
|
|
218
|
+
/**
|
|
219
|
+
* Handle click on a thread mark and mark it as selected
|
|
220
|
+
*/
|
|
221
|
+
handleClick: (view, pos, event) => {
|
|
222
|
+
if (event.button !== 0) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
222
225
|
|
|
223
|
-
|
|
226
|
+
const node = view.state.doc.nodeAt(pos);
|
|
224
227
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
228
|
+
if (!node) {
|
|
229
|
+
self.selectThread(undefined);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
229
232
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
+
const commentMark = node.marks.find(
|
|
234
|
+
(mark) =>
|
|
235
|
+
mark.type.name === markType && mark.attrs.orphan !== true,
|
|
236
|
+
);
|
|
233
237
|
|
|
234
|
-
|
|
235
|
-
|
|
238
|
+
const threadId = commentMark?.attrs.threadId as string | undefined;
|
|
239
|
+
self.selectThread(threadId, false);
|
|
240
|
+
},
|
|
236
241
|
},
|
|
237
|
-
},
|
|
238
|
-
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
239
244
|
}
|
|
240
245
|
|
|
241
246
|
/**
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
|
|
2
2
|
import { EditorView } from "prosemirror-view";
|
|
3
3
|
|
|
4
|
+
import { ySyncPluginKey } from "y-prosemirror";
|
|
4
5
|
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
|
|
6
|
+
import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
|
|
5
7
|
import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
|
|
6
8
|
import type {
|
|
7
9
|
BlockFromConfig,
|
|
@@ -9,8 +11,6 @@ import type {
|
|
|
9
11
|
InlineContentSchema,
|
|
10
12
|
StyleSchema,
|
|
11
13
|
} from "../../schema/index.js";
|
|
12
|
-
import { EventEmitter } from "../../util/EventEmitter.js";
|
|
13
|
-
import { ySyncPluginKey } from "y-prosemirror";
|
|
14
14
|
|
|
15
15
|
export type FilePanelState<
|
|
16
16
|
I extends InlineContentSchema,
|
|
@@ -138,60 +138,61 @@ const filePanelPluginKey = new PluginKey<FilePanelState<any, any>>(
|
|
|
138
138
|
export class FilePanelProsemirrorPlugin<
|
|
139
139
|
I extends InlineContentSchema,
|
|
140
140
|
S extends StyleSchema,
|
|
141
|
-
> extends
|
|
141
|
+
> extends BlockNoteExtension {
|
|
142
142
|
private view: FilePanelView<I, S> | undefined;
|
|
143
|
-
public readonly plugin: Plugin;
|
|
144
143
|
|
|
145
144
|
constructor(editor: BlockNoteEditor<Record<string, FileBlockConfig>, I, S>) {
|
|
146
145
|
super();
|
|
147
|
-
this.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
props: {
|
|
163
|
-
handleKeyDown: (_view, event: KeyboardEvent) => {
|
|
164
|
-
if (event.key === "Escape" && this.shown) {
|
|
165
|
-
this.view?.closeMenu();
|
|
166
|
-
return true;
|
|
167
|
-
}
|
|
168
|
-
return false;
|
|
146
|
+
this.addProsemirrorPlugin(
|
|
147
|
+
new Plugin<{
|
|
148
|
+
block: BlockFromConfig<FileBlockConfig, I, S> | undefined;
|
|
149
|
+
}>({
|
|
150
|
+
key: filePanelPluginKey,
|
|
151
|
+
view: (editorView) => {
|
|
152
|
+
this.view = new FilePanelView<I, S>(
|
|
153
|
+
editor,
|
|
154
|
+
filePanelPluginKey,
|
|
155
|
+
editorView,
|
|
156
|
+
(state) => {
|
|
157
|
+
this.emit("update", state);
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
return this.view;
|
|
169
161
|
},
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
162
|
+
props: {
|
|
163
|
+
handleKeyDown: (_view, event: KeyboardEvent) => {
|
|
164
|
+
if (event.key === "Escape" && this.shown) {
|
|
165
|
+
this.view?.closeMenu();
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
},
|
|
176
170
|
},
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
171
|
+
state: {
|
|
172
|
+
init: () => {
|
|
173
|
+
return {
|
|
174
|
+
block: undefined,
|
|
175
|
+
};
|
|
176
|
+
},
|
|
177
|
+
apply: (transaction, prev) => {
|
|
178
|
+
const state: FilePanelState<I, S> | undefined =
|
|
179
|
+
transaction.getMeta(filePanelPluginKey);
|
|
180
|
+
|
|
181
|
+
if (state) {
|
|
182
|
+
return state;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (
|
|
186
|
+
!transaction.getMeta(ySyncPluginKey) &&
|
|
187
|
+
(transaction.selectionSet || transaction.docChanged)
|
|
188
|
+
) {
|
|
189
|
+
return { block: undefined };
|
|
190
|
+
}
|
|
191
|
+
return prev;
|
|
192
|
+
},
|
|
192
193
|
},
|
|
193
|
-
},
|
|
194
|
-
|
|
194
|
+
}),
|
|
195
|
+
);
|
|
195
196
|
}
|
|
196
197
|
|
|
197
198
|
public get shown() {
|
|
@@ -3,13 +3,13 @@ import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
|
|
|
3
3
|
import { EditorView } from "prosemirror-view";
|
|
4
4
|
|
|
5
5
|
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
|
|
6
|
+
import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
|
|
6
7
|
import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
|
|
7
8
|
import {
|
|
8
9
|
BlockSchema,
|
|
9
10
|
InlineContentSchema,
|
|
10
11
|
StyleSchema,
|
|
11
12
|
} from "../../schema/index.js";
|
|
12
|
-
import { EventEmitter } from "../../util/EventEmitter.js";
|
|
13
13
|
|
|
14
14
|
export type FormattingToolbarState = UiElementPosition;
|
|
15
15
|
|
|
@@ -25,7 +25,7 @@ export class FormattingToolbarView implements PluginView {
|
|
|
25
25
|
state: EditorState;
|
|
26
26
|
from: number;
|
|
27
27
|
to: number;
|
|
28
|
-
}) => boolean = ({ state, from, to }) => {
|
|
28
|
+
}) => boolean = ({ view, state, from, to }) => {
|
|
29
29
|
const { doc, selection } = state;
|
|
30
30
|
const { empty } = selection;
|
|
31
31
|
|
|
@@ -43,7 +43,16 @@ export class FormattingToolbarView implements PluginView {
|
|
|
43
43
|
return false;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
if (empty || isEmptyTextBlock) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const focusedElement = document.activeElement;
|
|
51
|
+
if (!this.isElementWithinEditorWrapper(focusedElement) && view.editable) {
|
|
52
|
+
// editable editors must have focus for the toolbar to show
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
return true;
|
|
47
56
|
};
|
|
48
57
|
|
|
49
58
|
constructor(
|
|
@@ -108,8 +117,22 @@ export class FormattingToolbarView implements PluginView {
|
|
|
108
117
|
}
|
|
109
118
|
};
|
|
110
119
|
|
|
111
|
-
|
|
112
|
-
|
|
120
|
+
isElementWithinEditorWrapper = (element: Node | null) => {
|
|
121
|
+
if (!element) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
const editorWrapper = this.pmView.dom.parentElement;
|
|
125
|
+
if (!editorWrapper) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return editorWrapper.contains(element);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
viewMousedownHandler = (e: MouseEvent) => {
|
|
133
|
+
if (!this.isElementWithinEditorWrapper(e.target as Node)) {
|
|
134
|
+
this.preventShow = true;
|
|
135
|
+
}
|
|
113
136
|
};
|
|
114
137
|
|
|
115
138
|
mouseupHandler = () => {
|
|
@@ -172,12 +195,18 @@ export class FormattingToolbarView implements PluginView {
|
|
|
172
195
|
// e.g. the download file button, should still be accessible. Therefore,
|
|
173
196
|
// logic for hiding when the editor is non-editable is handled
|
|
174
197
|
// individually in each button.
|
|
175
|
-
|
|
198
|
+
const nextState = {
|
|
176
199
|
show: true,
|
|
177
200
|
referencePos: this.getSelectionBoundingBox(),
|
|
178
201
|
};
|
|
179
202
|
|
|
180
|
-
|
|
203
|
+
if (
|
|
204
|
+
nextState.show !== this.state?.show ||
|
|
205
|
+
nextState.referencePos.toJSON() !== this.state?.referencePos.toJSON()
|
|
206
|
+
) {
|
|
207
|
+
this.state = nextState;
|
|
208
|
+
this.emitUpdate();
|
|
209
|
+
}
|
|
181
210
|
|
|
182
211
|
return;
|
|
183
212
|
}
|
|
@@ -236,30 +265,31 @@ export const formattingToolbarPluginKey = new PluginKey(
|
|
|
236
265
|
"FormattingToolbarPlugin",
|
|
237
266
|
);
|
|
238
267
|
|
|
239
|
-
export class FormattingToolbarProsemirrorPlugin extends
|
|
268
|
+
export class FormattingToolbarProsemirrorPlugin extends BlockNoteExtension {
|
|
240
269
|
private view: FormattingToolbarView | undefined;
|
|
241
|
-
public readonly plugin: Plugin;
|
|
242
270
|
|
|
243
271
|
constructor(editor: BlockNoteEditor<any, any, any>) {
|
|
244
272
|
super();
|
|
245
|
-
this.
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
this.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
props: {
|
|
254
|
-
handleKeyDown: (_view, event: KeyboardEvent) => {
|
|
255
|
-
if (event.key === "Escape" && this.shown) {
|
|
256
|
-
this.view!.closeMenu();
|
|
257
|
-
return true;
|
|
258
|
-
}
|
|
259
|
-
return false;
|
|
273
|
+
this.addProsemirrorPlugin(
|
|
274
|
+
new Plugin({
|
|
275
|
+
key: formattingToolbarPluginKey,
|
|
276
|
+
view: (editorView) => {
|
|
277
|
+
this.view = new FormattingToolbarView(editor, editorView, (state) => {
|
|
278
|
+
this.emit("update", state);
|
|
279
|
+
});
|
|
280
|
+
return this.view;
|
|
260
281
|
},
|
|
261
|
-
|
|
262
|
-
|
|
282
|
+
props: {
|
|
283
|
+
handleKeyDown: (_view, event: KeyboardEvent) => {
|
|
284
|
+
if (event.key === "Escape" && this.shown) {
|
|
285
|
+
this.view!.closeMenu();
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
return false;
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
}),
|
|
292
|
+
);
|
|
263
293
|
}
|
|
264
294
|
|
|
265
295
|
public get shown() {
|
|
@@ -4,15 +4,15 @@ import { EditorView } from "@tiptap/pm/view";
|
|
|
4
4
|
import { Mark } from "prosemirror-model";
|
|
5
5
|
import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
|
|
6
6
|
|
|
7
|
+
import { getPmSchema } from "../../api/pmUtil.js";
|
|
7
8
|
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
|
|
9
|
+
import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
|
|
8
10
|
import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
|
|
9
11
|
import {
|
|
10
12
|
BlockSchema,
|
|
11
13
|
InlineContentSchema,
|
|
12
14
|
StyleSchema,
|
|
13
15
|
} from "../../schema/index.js";
|
|
14
|
-
import { EventEmitter } from "../../util/EventEmitter.js";
|
|
15
|
-
import { getPmSchema } from "../../api/pmUtil.js";
|
|
16
16
|
|
|
17
17
|
export type LinkToolbarState = UiElementPosition & {
|
|
18
18
|
// The hovered link's URL, and the text it's displayed with in the
|
|
@@ -301,30 +301,31 @@ export class LinkToolbarProsemirrorPlugin<
|
|
|
301
301
|
BSchema extends BlockSchema,
|
|
302
302
|
I extends InlineContentSchema,
|
|
303
303
|
S extends StyleSchema,
|
|
304
|
-
> extends
|
|
304
|
+
> extends BlockNoteExtension {
|
|
305
305
|
private view: LinkToolbarView | undefined;
|
|
306
|
-
public readonly plugin: Plugin;
|
|
307
306
|
|
|
308
307
|
constructor(editor: BlockNoteEditor<BSchema, I, S>) {
|
|
309
308
|
super();
|
|
310
|
-
this.
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
this.
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
props: {
|
|
319
|
-
handleKeyDown: (_view, event: KeyboardEvent) => {
|
|
320
|
-
if (event.key === "Escape" && this.shown) {
|
|
321
|
-
this.view!.closeMenu();
|
|
322
|
-
return true;
|
|
323
|
-
}
|
|
324
|
-
return false;
|
|
309
|
+
this.addProsemirrorPlugin(
|
|
310
|
+
new Plugin({
|
|
311
|
+
key: linkToolbarPluginKey,
|
|
312
|
+
view: (editorView) => {
|
|
313
|
+
this.view = new LinkToolbarView(editor, editorView, (state) => {
|
|
314
|
+
this.emit("update", state);
|
|
315
|
+
});
|
|
316
|
+
return this.view;
|
|
325
317
|
},
|
|
326
|
-
|
|
327
|
-
|
|
318
|
+
props: {
|
|
319
|
+
handleKeyDown: (_view, event: KeyboardEvent) => {
|
|
320
|
+
if (event.key === "Escape" && this.shown) {
|
|
321
|
+
this.view!.closeMenu();
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
return false;
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
}),
|
|
328
|
+
);
|
|
328
329
|
}
|
|
329
330
|
|
|
330
331
|
public onUpdate(callback: (state: LinkToolbarState) => void) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
|
|
2
|
+
import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
|
|
2
3
|
|
|
3
4
|
const PLUGIN_KEY = new PluginKey("node-selection-keyboard");
|
|
4
5
|
// By default, typing with a node selection active will cause ProseMirror to
|
|
@@ -15,54 +16,56 @@ const PLUGIN_KEY = new PluginKey("node-selection-keyboard");
|
|
|
15
16
|
// While a more elegant solution would probably process transactions instead of
|
|
16
17
|
// keystrokes, this brings us most of the way to Notion's UX without much added
|
|
17
18
|
// complexity.
|
|
18
|
-
export class NodeSelectionKeyboardPlugin {
|
|
19
|
-
public readonly plugin: Plugin;
|
|
19
|
+
export class NodeSelectionKeyboardPlugin extends BlockNoteExtension {
|
|
20
20
|
constructor() {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// Checks
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
21
|
+
super();
|
|
22
|
+
this.addProsemirrorPlugin(
|
|
23
|
+
new Plugin({
|
|
24
|
+
key: PLUGIN_KEY,
|
|
25
|
+
props: {
|
|
26
|
+
handleKeyDown: (view, event) => {
|
|
27
|
+
// Checks for node selection
|
|
28
|
+
if ("node" in view.state.selection) {
|
|
29
|
+
// Checks if key press uses ctrl/meta modifier
|
|
30
|
+
if (event.ctrlKey || event.metaKey) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
// Checks if key press is alphanumeric
|
|
34
|
+
if (event.key.length === 1) {
|
|
35
|
+
event.preventDefault();
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
// Checks if key press is Enter
|
|
40
|
+
if (
|
|
41
|
+
event.key === "Enter" &&
|
|
42
|
+
!event.shiftKey &&
|
|
43
|
+
!event.altKey &&
|
|
44
|
+
!event.ctrlKey &&
|
|
45
|
+
!event.metaKey
|
|
46
|
+
) {
|
|
47
|
+
const tr = view.state.tr;
|
|
48
|
+
view.dispatch(
|
|
49
|
+
tr
|
|
50
|
+
.insert(
|
|
51
|
+
view.state.tr.selection.$to.after(),
|
|
52
|
+
view.state.schema.nodes["paragraph"].createChecked(),
|
|
53
|
+
)
|
|
54
|
+
.setSelection(
|
|
55
|
+
new TextSelection(
|
|
56
|
+
tr.doc.resolve(view.state.tr.selection.$to.after() + 1),
|
|
57
|
+
),
|
|
55
58
|
),
|
|
56
|
-
|
|
57
|
-
);
|
|
59
|
+
);
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
60
63
|
}
|
|
61
|
-
}
|
|
62
64
|
|
|
63
|
-
|
|
65
|
+
return false;
|
|
66
|
+
},
|
|
64
67
|
},
|
|
65
|
-
},
|
|
66
|
-
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
67
70
|
}
|
|
68
71
|
}
|