@blocknote/core 0.2.2 → 0.2.4-alpha.7
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.js +1061 -936
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +1 -1
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +22 -29
- package/src/BlockNoteExtensions.ts +11 -10
- package/src/extensions/BackgroundColor/BackgroundColorExtension.ts +61 -0
- package/src/extensions/BackgroundColor/BackgroundColorMark.ts +62 -0
- package/src/extensions/Blocks/PreviousBlockTypePlugin.ts +112 -106
- package/src/extensions/Blocks/apiTypes.ts +48 -0
- package/src/extensions/Blocks/helpers/findBlock.ts +3 -1
- package/src/extensions/Blocks/helpers/getBlockInfoFromPos.ts +1 -1
- package/src/extensions/Blocks/index.ts +10 -8
- package/src/extensions/Blocks/nodes/Block.module.css +122 -35
- package/src/extensions/Blocks/{BlockAttributes.ts → nodes/BlockAttributes.ts} +0 -0
- package/src/extensions/Blocks/nodes/{Block.ts → BlockContainer.ts} +113 -119
- package/src/extensions/Blocks/nodes/{BlockTypes/HeadingBlock/HeadingContent.ts → BlockContent/HeadingBlockContent/HeadingBlockContent.ts} +16 -24
- package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +76 -0
- package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/ListItemKeyboardShortcuts.ts +47 -0
- package/src/extensions/Blocks/nodes/{BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.ts → BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts} +10 -14
- package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +95 -0
- package/src/extensions/Blocks/nodes/{BlockTypes/TextBlock/TextContent.ts → BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts} +7 -12
- package/src/extensions/Blocks/nodes/BlockGroup.ts +4 -4
- package/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.ts +9 -1
- package/src/extensions/DraggableBlocks/DraggableBlocksPlugin.ts +87 -42
- package/src/extensions/{Blocks → DraggableBlocks}/MultipleNodeSelection.ts +0 -0
- package/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.ts +20 -7
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +51 -12
- package/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.ts +1 -1
- package/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.ts +3 -1
- package/src/extensions/Placeholder/PlaceholderExtension.ts +1 -1
- package/src/extensions/SlashMenu/SlashMenuExtension.ts +1 -1
- package/src/extensions/SlashMenu/SlashMenuItem.ts +3 -28
- package/src/extensions/SlashMenu/defaultCommands.tsx +36 -55
- package/src/extensions/SlashMenu/index.ts +1 -6
- package/src/extensions/TextAlignment/TextAlignmentExtension.ts +75 -0
- package/src/extensions/TextColor/TextColorExtension.ts +54 -0
- package/src/extensions/TextColor/TextColorMark.ts +62 -0
- package/src/extensions/TrailingNode/TrailingNodeExtension.ts +4 -4
- package/src/extensions/UniqueID/UniqueID.ts +6 -0
- package/src/index.ts +2 -1
- package/src/shared/EditorElement.ts +12 -6
- package/src/shared/plugins/suggestion/SuggestionItem.ts +0 -9
- package/src/shared/plugins/suggestion/SuggestionPlugin.ts +191 -228
- package/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.ts +2 -2
- package/types/src/BlockNoteEditor.d.ts +1 -1
- package/types/src/BlockNoteExtensions.d.ts +1 -3
- package/types/src/api/Document.d.ts +5 -0
- package/types/src/extensions/BackgroundColor/BackgroundColorExtension.d.ts +9 -0
- package/types/src/extensions/BackgroundColor/BackgroundColorMark.d.ts +9 -0
- package/types/src/extensions/Blocks/PreviousBlockTypePlugin.d.ts +3 -2
- package/types/src/extensions/Blocks/apiTypes.d.ts +16 -0
- package/types/src/extensions/Blocks/helpers/getBlockInfoFromPos.d.ts +1 -1
- package/types/src/extensions/Blocks/nodes/BlockAttributes.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContainer.d.ts +21 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/BlockContentTypes.d.ts +4 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContent.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContentTypes.d.ts +4 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContentTypes.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/ListItemKeyboardShortcuts.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContentTypes.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContentTypes.d.ts +2 -0
- package/types/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.d.ts +9 -5
- package/types/src/extensions/DraggableBlocks/DraggableBlocksExtension.d.ts +1 -1
- package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +6 -11
- package/types/src/extensions/DraggableBlocks/MultipleNodeSelection.d.ts +24 -0
- package/types/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.d.ts +18 -8
- package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +1 -1
- package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.d.ts +5 -5
- package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.d.ts +2 -2
- package/types/src/extensions/SlashMenu/SlashMenuExtension.d.ts +1 -1
- package/types/src/extensions/SlashMenu/SlashMenuItem.d.ts +2 -19
- package/types/src/extensions/SlashMenu/defaultSlashCommands.d.ts +5 -0
- package/types/src/extensions/SlashMenu/index.d.ts +1 -2
- package/types/src/extensions/TextAlignment/TextAlignmentExtension.d.ts +9 -0
- package/types/src/extensions/TextColor/TextColorExtension.d.ts +9 -0
- package/types/src/extensions/TextColor/TextColorMark.d.ts +9 -0
- package/types/src/index.d.ts +2 -1
- package/types/src/shared/EditorElement.d.ts +6 -2
- package/types/src/shared/plugins/suggestion/SuggestionItem.d.ts +0 -6
- package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +11 -25
- package/types/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.d.ts +6 -6
- package/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.ts +0 -177
- package/src/extensions/Paragraph/FixedParagraph.ts +0 -12
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Editor, Range } from "@tiptap/core";
|
|
2
|
-
import {
|
|
3
|
-
import { EditorState, Plugin, PluginKey, Selection } from "prosemirror-state";
|
|
2
|
+
import { EditorState, Plugin, PluginKey } from "prosemirror-state";
|
|
4
3
|
import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
|
|
5
4
|
import { findBlock } from "../../../extensions/Blocks/helpers/findBlock";
|
|
6
5
|
import {
|
|
@@ -25,9 +24,9 @@ export type SuggestionPluginOptions<T extends SuggestionItem> = {
|
|
|
25
24
|
editor: Editor;
|
|
26
25
|
|
|
27
26
|
/**
|
|
28
|
-
* The character that should trigger the suggestion menu to pop up (e.g. a '/' for commands)
|
|
27
|
+
* The character that should trigger the suggestion menu to pop up (e.g. a '/' for commands), when typed by the user.
|
|
29
28
|
*/
|
|
30
|
-
|
|
29
|
+
defaultTriggerCharacter: string;
|
|
31
30
|
|
|
32
31
|
suggestionsMenuFactory: SuggestionsMenuFactory<T>;
|
|
33
32
|
|
|
@@ -49,16 +48,38 @@ export type SuggestionPluginOptions<T extends SuggestionItem> = {
|
|
|
49
48
|
};
|
|
50
49
|
|
|
51
50
|
type SuggestionPluginState<T extends SuggestionItem> = {
|
|
51
|
+
// True when the menu is shown, false when hidden.
|
|
52
52
|
active: boolean;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
// The character that triggered the menu being shown. Allowing the trigger to be different to the default
|
|
54
|
+
// trigger allows other extensions to open it programmatically.
|
|
55
|
+
triggerCharacter: string | undefined;
|
|
56
|
+
// The editor position just after the trigger character, i.e. where the user query begins. Used to figure out
|
|
57
|
+
// which menu items to show and can also be used to delete the trigger character.
|
|
58
|
+
queryStartPos: number | undefined;
|
|
59
|
+
// The items that should be shown in the menu.
|
|
56
60
|
items: T[];
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
// The index of the item in the menu that's currently hovered using the keyboard.
|
|
62
|
+
keyboardHoveredItemIndex: number | undefined;
|
|
63
|
+
// The number of characters typed after the last query that matched with at least 1 item. Used to close the
|
|
64
|
+
// menu if the user keeps entering queries that don't return any results.
|
|
65
|
+
notFoundCount: number | undefined;
|
|
66
|
+
decorationId: string | undefined;
|
|
60
67
|
};
|
|
61
68
|
|
|
69
|
+
function getDefaultPluginState<
|
|
70
|
+
T extends SuggestionItem
|
|
71
|
+
>(): SuggestionPluginState<T> {
|
|
72
|
+
return {
|
|
73
|
+
active: false,
|
|
74
|
+
triggerCharacter: undefined,
|
|
75
|
+
queryStartPos: undefined,
|
|
76
|
+
items: [] as T[],
|
|
77
|
+
keyboardHoveredItemIndex: undefined,
|
|
78
|
+
notFoundCount: 0,
|
|
79
|
+
decorationId: undefined,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
62
83
|
type SuggestionPluginViewOptions<T extends SuggestionItem> = {
|
|
63
84
|
editor: Editor;
|
|
64
85
|
pluginKey: PluginKey;
|
|
@@ -66,47 +87,6 @@ type SuggestionPluginViewOptions<T extends SuggestionItem> = {
|
|
|
66
87
|
suggestionsMenuFactory: SuggestionsMenuFactory<T>;
|
|
67
88
|
};
|
|
68
89
|
|
|
69
|
-
export type MenuType = "slash" | "drag";
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Finds a command: a specified character (e.g. '/') followed by a string of characters (all characters except the specified character are allowed).
|
|
73
|
-
* Returns the string following the specified character or undefined if no command was found.
|
|
74
|
-
*
|
|
75
|
-
* @param char the character that indicates the start of a command
|
|
76
|
-
* @param selection the selection (only works if the selection is empty; i.e. is a blinking cursor).
|
|
77
|
-
* @returns an object containing the matching word (excluding the specified character) and the range of the match (including the specified character) or undefined if there is no match.
|
|
78
|
-
*/
|
|
79
|
-
export function findCommandBeforeCursor(
|
|
80
|
-
char: string,
|
|
81
|
-
selection: Selection
|
|
82
|
-
): { range: Range; query: string } | undefined {
|
|
83
|
-
if (!selection.empty) {
|
|
84
|
-
return undefined;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// get the text before the cursor as a node
|
|
88
|
-
const node = selection.$anchor.nodeBefore;
|
|
89
|
-
if (!node || !node.text) {
|
|
90
|
-
return undefined;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// regex to match anything between with the specified char (e.g. '/') and the end of text (which is the end of selection)
|
|
94
|
-
const regex = new RegExp(`${escapeRegExp(char)}([^${escapeRegExp(char)}]*)$`);
|
|
95
|
-
const match = node.text.match(regex);
|
|
96
|
-
|
|
97
|
-
if (!match) {
|
|
98
|
-
return undefined;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
query: match[1],
|
|
103
|
-
range: {
|
|
104
|
-
from: selection.$anchor.pos - match[1].length - char.length,
|
|
105
|
-
to: selection.$anchor.pos,
|
|
106
|
-
},
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
90
|
class SuggestionPluginView<T extends SuggestionItem> {
|
|
111
91
|
editor: Editor;
|
|
112
92
|
pluginKey: PluginKey;
|
|
@@ -125,22 +105,18 @@ class SuggestionPluginView<T extends SuggestionItem> {
|
|
|
125
105
|
this.editor = editor;
|
|
126
106
|
this.pluginKey = pluginKey;
|
|
127
107
|
|
|
128
|
-
this.pluginState =
|
|
129
|
-
active: false,
|
|
130
|
-
range: null,
|
|
131
|
-
query: null,
|
|
132
|
-
notFoundCount: 0,
|
|
133
|
-
items: [],
|
|
134
|
-
selectedItemIndex: 0,
|
|
135
|
-
type: "slash",
|
|
136
|
-
decorationId: null,
|
|
137
|
-
};
|
|
108
|
+
this.pluginState = getDefaultPluginState<T>();
|
|
138
109
|
|
|
139
110
|
this.itemCallback = (item: T) =>
|
|
140
111
|
selectItemCallback({
|
|
141
112
|
item: item,
|
|
142
113
|
editor: editor,
|
|
143
|
-
range:
|
|
114
|
+
range: {
|
|
115
|
+
from:
|
|
116
|
+
this.pluginState.queryStartPos! -
|
|
117
|
+
this.pluginState.triggerCharacter!.length,
|
|
118
|
+
to: editor.state.selection.from,
|
|
119
|
+
},
|
|
144
120
|
});
|
|
145
121
|
|
|
146
122
|
this.suggestionsMenu = suggestionsMenuFactory(this.getStaticParams());
|
|
@@ -200,8 +176,8 @@ class SuggestionPluginView<T extends SuggestionItem> {
|
|
|
200
176
|
|
|
201
177
|
return {
|
|
202
178
|
items: this.pluginState.items,
|
|
203
|
-
|
|
204
|
-
|
|
179
|
+
keyboardHoveredItemIndex: this.pluginState.keyboardHoveredItemIndex!,
|
|
180
|
+
referenceRect: decorationNode!.getBoundingClientRect(),
|
|
205
181
|
};
|
|
206
182
|
}
|
|
207
183
|
}
|
|
@@ -222,13 +198,13 @@ class SuggestionPluginView<T extends SuggestionItem> {
|
|
|
222
198
|
export function createSuggestionPlugin<T extends SuggestionItem>({
|
|
223
199
|
pluginKey,
|
|
224
200
|
editor,
|
|
225
|
-
|
|
201
|
+
defaultTriggerCharacter,
|
|
226
202
|
suggestionsMenuFactory,
|
|
227
203
|
onSelectItem: selectItemCallback = () => {},
|
|
228
204
|
items = () => [],
|
|
229
205
|
}: SuggestionPluginOptions<T>) {
|
|
230
206
|
// Assertions
|
|
231
|
-
if (
|
|
207
|
+
if (defaultTriggerCharacter.length !== 1) {
|
|
232
208
|
throw new Error("'char' should be a single character");
|
|
233
209
|
}
|
|
234
210
|
|
|
@@ -236,7 +212,7 @@ export function createSuggestionPlugin<T extends SuggestionItem>({
|
|
|
236
212
|
view.dispatch(view.state.tr.setMeta(pluginKey, { deactivate: true }));
|
|
237
213
|
};
|
|
238
214
|
|
|
239
|
-
// Plugin key is passed in parameter so it can be exported and used in
|
|
215
|
+
// Plugin key is passed in as a parameter, so it can be exported and used in the DraggableBlocksPlugin.
|
|
240
216
|
return new Plugin({
|
|
241
217
|
key: pluginKey,
|
|
242
218
|
|
|
@@ -254,126 +230,93 @@ export function createSuggestionPlugin<T extends SuggestionItem>({
|
|
|
254
230
|
state: {
|
|
255
231
|
// Initialize the plugin's internal state.
|
|
256
232
|
init(): SuggestionPluginState<T> {
|
|
257
|
-
return
|
|
258
|
-
active: false,
|
|
259
|
-
range: null, // TODO
|
|
260
|
-
query: null,
|
|
261
|
-
notFoundCount: 0,
|
|
262
|
-
items: [] as T[],
|
|
263
|
-
selectedItemIndex: 0,
|
|
264
|
-
type: "slash",
|
|
265
|
-
decorationId: null,
|
|
266
|
-
};
|
|
233
|
+
return getDefaultPluginState<T>();
|
|
267
234
|
},
|
|
268
235
|
|
|
269
|
-
// Apply changes to the plugin state from
|
|
270
|
-
apply(transaction, prev,
|
|
271
|
-
|
|
272
|
-
const next = { ...prev };
|
|
273
|
-
|
|
274
|
-
// TODO: More clearly define which transactions should be ignored and which should deactivate the menu.
|
|
236
|
+
// Apply changes to the plugin state from an editor transaction.
|
|
237
|
+
apply(transaction, prev, oldState, newState): SuggestionPluginState<T> {
|
|
238
|
+
// TODO: More clearly define which transactions should be ignored.
|
|
275
239
|
if (transaction.getMeta("orderedListIndexing") !== undefined) {
|
|
276
|
-
return
|
|
240
|
+
return prev;
|
|
277
241
|
}
|
|
278
242
|
|
|
279
|
-
//
|
|
280
|
-
if (
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
243
|
+
// Checks if the menu should be shown.
|
|
244
|
+
if (transaction.getMeta(pluginKey)?.activate) {
|
|
245
|
+
return {
|
|
246
|
+
active: true,
|
|
247
|
+
triggerCharacter:
|
|
248
|
+
transaction.getMeta(pluginKey)?.triggerCharacter || "",
|
|
249
|
+
queryStartPos: newState.selection.from,
|
|
250
|
+
items: items(""),
|
|
251
|
+
keyboardHoveredItemIndex: 0,
|
|
252
|
+
// TODO: Maybe should be 1 if the menu has no possible items? Probably redundant since a menu with no items
|
|
253
|
+
// is useless in practice.
|
|
254
|
+
notFoundCount: 0,
|
|
255
|
+
decorationId: `id_${Math.floor(Math.random() * 0xffffffff)}`,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
285
258
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
259
|
+
// Checks if the menu is hidden, in which case it doesn't need to be hidden or updated.
|
|
260
|
+
if (!prev.active) {
|
|
261
|
+
return prev;
|
|
262
|
+
}
|
|
289
263
|
|
|
290
|
-
|
|
291
|
-
newIndex = 0;
|
|
292
|
-
}
|
|
264
|
+
const next = { ...prev };
|
|
293
265
|
|
|
294
|
-
|
|
266
|
+
// Updates which menu items to show by checking which items the current query (the text between the trigger
|
|
267
|
+
// character and caret) matches with.
|
|
268
|
+
next.items = items(
|
|
269
|
+
newState.doc.textBetween(prev.queryStartPos!, newState.selection.from)
|
|
270
|
+
);
|
|
295
271
|
|
|
296
|
-
|
|
272
|
+
// Updates notFoundCount if the query doesn't match any items.
|
|
273
|
+
next.notFoundCount = 0;
|
|
274
|
+
if (next.items.length === 0) {
|
|
275
|
+
// Checks how many characters were typed or deleted since the last transaction, and updates the notFoundCount
|
|
276
|
+
// accordingly. Also ensures the notFoundCount does not become negative.
|
|
277
|
+
next.notFoundCount = Math.max(
|
|
278
|
+
0,
|
|
279
|
+
prev.notFoundCount! +
|
|
280
|
+
(newState.selection.from - oldState.selection.from)
|
|
281
|
+
);
|
|
297
282
|
}
|
|
298
283
|
|
|
284
|
+
// Hides the menu. This is done after items and notFoundCount are already updated as notFoundCount is needed to
|
|
285
|
+
// check if the menu should be hidden.
|
|
299
286
|
if (
|
|
300
|
-
//
|
|
301
|
-
selection.from
|
|
302
|
-
//
|
|
303
|
-
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
287
|
+
// Highlighting text should hide the menu.
|
|
288
|
+
newState.selection.from !== newState.selection.to ||
|
|
289
|
+
// Transactions with plugin metadata {deactivate: true} should hide the menu.
|
|
290
|
+
transaction.getMeta(pluginKey)?.deactivate ||
|
|
291
|
+
// Certain mouse events should hide the menu.
|
|
292
|
+
// TODO: Change to global mousedown listener.
|
|
293
|
+
transaction.getMeta("focus") ||
|
|
294
|
+
transaction.getMeta("blur") ||
|
|
295
|
+
transaction.getMeta("pointer") ||
|
|
296
|
+
// Moving the caret before the character which triggered the menu should hide it.
|
|
297
|
+
(prev.active && newState.selection.from < prev.queryStartPos!) ||
|
|
298
|
+
// Entering more than 3 characters, after the last query that matched with at least 1 menu item, should hide
|
|
299
|
+
// the menu.
|
|
300
|
+
next.notFoundCount > 3
|
|
308
301
|
) {
|
|
309
|
-
|
|
310
|
-
if (prev.active && selection.from <= prev.range!.from) {
|
|
311
|
-
next.active = false;
|
|
312
|
-
} else if (transaction.getMeta(pluginKey)?.activate) {
|
|
313
|
-
// Start showing suggestions. activate has been set after typing a "/" (or whatever the specified character is), so let's create the decoration and initialize
|
|
314
|
-
const newDecorationId = `id_${Math.floor(
|
|
315
|
-
Math.random() * 0xffffffff
|
|
316
|
-
)}`;
|
|
317
|
-
next.decorationId = newDecorationId;
|
|
318
|
-
next.range = {
|
|
319
|
-
from: selection.from - 1,
|
|
320
|
-
to: selection.to,
|
|
321
|
-
};
|
|
322
|
-
next.query = "";
|
|
323
|
-
next.active = true;
|
|
324
|
-
next.type = transaction.getMeta(pluginKey)?.type;
|
|
325
|
-
next.selectedItemIndex = 0;
|
|
326
|
-
} else if (prev.active) {
|
|
327
|
-
// Try to match against where our cursor currently is
|
|
328
|
-
// if the type is slash we get the command after the character
|
|
329
|
-
// otherwise we get the whole query
|
|
330
|
-
const match = findCommandBeforeCursor(
|
|
331
|
-
prev.type === "slash" ? char : "",
|
|
332
|
-
newState.selection
|
|
333
|
-
);
|
|
334
|
-
if (!match) {
|
|
335
|
-
throw new Error("active but no match (suggestions)");
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
next.range = match.range;
|
|
339
|
-
next.active = true;
|
|
340
|
-
next.decorationId = prev.decorationId;
|
|
341
|
-
next.query = match.query;
|
|
342
|
-
next.selectedItemIndex = 0;
|
|
343
|
-
}
|
|
344
|
-
} else {
|
|
345
|
-
next.active = false;
|
|
302
|
+
return getDefaultPluginState<T>();
|
|
346
303
|
}
|
|
347
304
|
|
|
348
|
-
if
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
// which indicates how many characters have been typed after showing no results
|
|
355
|
-
if (next.range!.to > prev.range!.to) {
|
|
356
|
-
// Text has been entered (selection moved to right), but still no items found, update Count
|
|
357
|
-
next.notFoundCount = prev.notFoundCount + 1;
|
|
358
|
-
} else {
|
|
359
|
-
// No text has been entered in this tr, keep not found count
|
|
360
|
-
// (e.g.: user hits backspace after no results)
|
|
361
|
-
next.notFoundCount = prev.notFoundCount;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
305
|
+
// Updates keyboardHoveredItemIndex if necessary.
|
|
306
|
+
if (
|
|
307
|
+
transaction.getMeta(pluginKey)?.selectedItemIndexChanged !== undefined
|
|
308
|
+
) {
|
|
309
|
+
let newIndex =
|
|
310
|
+
transaction.getMeta(pluginKey).selectedItemIndexChanged;
|
|
364
311
|
|
|
365
|
-
|
|
366
|
-
|
|
312
|
+
// Allows selection to jump between first and last items.
|
|
313
|
+
if (newIndex < 0) {
|
|
314
|
+
newIndex = prev.items.length - 1;
|
|
315
|
+
} else if (newIndex >= prev.items.length) {
|
|
316
|
+
newIndex = 0;
|
|
367
317
|
}
|
|
368
|
-
}
|
|
369
318
|
|
|
370
|
-
|
|
371
|
-
if (!next.active) {
|
|
372
|
-
next.decorationId = null;
|
|
373
|
-
next.range = null;
|
|
374
|
-
next.query = null;
|
|
375
|
-
next.notFoundCount = 0;
|
|
376
|
-
next.items = [];
|
|
319
|
+
next.keyboardHoveredItemIndex = newIndex;
|
|
377
320
|
}
|
|
378
321
|
|
|
379
322
|
return next;
|
|
@@ -382,57 +325,74 @@ export function createSuggestionPlugin<T extends SuggestionItem>({
|
|
|
382
325
|
|
|
383
326
|
props: {
|
|
384
327
|
handleKeyDown(view, event) {
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
if
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
view.
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
return true;
|
|
398
|
-
}
|
|
399
|
-
} else {
|
|
400
|
-
const { items, range, selectedItemIndex } = pluginKey.getState(
|
|
401
|
-
view.state
|
|
328
|
+
const menuIsActive = (this as Plugin).getState(view.state).active;
|
|
329
|
+
|
|
330
|
+
// Shows the menu if the default trigger character was pressed and the menu isn't active.
|
|
331
|
+
if (event.key === defaultTriggerCharacter && !menuIsActive) {
|
|
332
|
+
view.dispatch(
|
|
333
|
+
view.state.tr
|
|
334
|
+
.insertText(defaultTriggerCharacter)
|
|
335
|
+
.scrollIntoView()
|
|
336
|
+
.setMeta(pluginKey, {
|
|
337
|
+
activate: true,
|
|
338
|
+
triggerCharacter: defaultTriggerCharacter,
|
|
339
|
+
})
|
|
402
340
|
);
|
|
403
341
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
view.state.tr.setMeta(pluginKey, {
|
|
407
|
-
selectedItemIndexChanged: selectedItemIndex - 1,
|
|
408
|
-
})
|
|
409
|
-
);
|
|
410
|
-
return true;
|
|
411
|
-
}
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
412
344
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
})
|
|
418
|
-
);
|
|
419
|
-
return true;
|
|
420
|
-
}
|
|
345
|
+
// Doesn't handle other keystrokes if the menu isn't active.
|
|
346
|
+
if (!menuIsActive) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
421
349
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
350
|
+
// Handles keystrokes for navigating the menu.
|
|
351
|
+
const {
|
|
352
|
+
triggerCharacter,
|
|
353
|
+
queryStartPos,
|
|
354
|
+
items,
|
|
355
|
+
keyboardHoveredItemIndex,
|
|
356
|
+
} = pluginKey.getState(view.state);
|
|
357
|
+
|
|
358
|
+
// Moves the keyboard selection to the previous item.
|
|
359
|
+
if (event.key === "ArrowUp") {
|
|
360
|
+
view.dispatch(
|
|
361
|
+
view.state.tr.setMeta(pluginKey, {
|
|
362
|
+
selectedItemIndexChanged: keyboardHoveredItemIndex - 1,
|
|
363
|
+
})
|
|
364
|
+
);
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
431
367
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
368
|
+
// Moves the keyboard selection to the next item.
|
|
369
|
+
if (event.key === "ArrowDown") {
|
|
370
|
+
view.dispatch(
|
|
371
|
+
view.state.tr.setMeta(pluginKey, {
|
|
372
|
+
selectedItemIndexChanged: keyboardHoveredItemIndex + 1,
|
|
373
|
+
})
|
|
374
|
+
);
|
|
375
|
+
return true;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Selects an item and closes the menu.
|
|
379
|
+
if (event.key === "Enter") {
|
|
380
|
+
deactivate(view);
|
|
381
|
+
selectItemCallback({
|
|
382
|
+
item: items[keyboardHoveredItemIndex],
|
|
383
|
+
editor: editor,
|
|
384
|
+
range: {
|
|
385
|
+
from: queryStartPos - triggerCharacter.length,
|
|
386
|
+
to: view.state.selection.from,
|
|
387
|
+
},
|
|
388
|
+
});
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Closes the menu.
|
|
393
|
+
if (event.key === "Escape") {
|
|
394
|
+
deactivate(view);
|
|
395
|
+
return true;
|
|
436
396
|
}
|
|
437
397
|
|
|
438
398
|
return false;
|
|
@@ -445,18 +405,17 @@ export function createSuggestionPlugin<T extends SuggestionItem>({
|
|
|
445
405
|
|
|
446
406
|
// Setup decorator on the currently active suggestion.
|
|
447
407
|
decorations(state) {
|
|
448
|
-
const { active,
|
|
449
|
-
|
|
450
|
-
);
|
|
408
|
+
const { active, decorationId, queryStartPos, triggerCharacter } = (
|
|
409
|
+
this as Plugin
|
|
410
|
+
).getState(state);
|
|
451
411
|
|
|
452
412
|
if (!active) {
|
|
453
413
|
return null;
|
|
454
414
|
}
|
|
455
415
|
|
|
456
|
-
// If
|
|
457
|
-
//
|
|
458
|
-
|
|
459
|
-
if (type === "drag") {
|
|
416
|
+
// If the menu was opened programmatically by another extension, it may not use a trigger character. In this
|
|
417
|
+
// case, the decoration is set on the whole block instead, as the decoration range would otherwise be empty.
|
|
418
|
+
if (triggerCharacter === "") {
|
|
460
419
|
const blockNode = findBlock(state.selection);
|
|
461
420
|
if (blockNode) {
|
|
462
421
|
return DecorationSet.create(state.doc, [
|
|
@@ -472,13 +431,17 @@ export function createSuggestionPlugin<T extends SuggestionItem>({
|
|
|
472
431
|
]);
|
|
473
432
|
}
|
|
474
433
|
}
|
|
475
|
-
//
|
|
434
|
+
// Creates an inline decoration around the trigger character.
|
|
476
435
|
return DecorationSet.create(state.doc, [
|
|
477
|
-
Decoration.inline(
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
436
|
+
Decoration.inline(
|
|
437
|
+
queryStartPos - triggerCharacter.length,
|
|
438
|
+
queryStartPos,
|
|
439
|
+
{
|
|
440
|
+
nodeName: "span",
|
|
441
|
+
class: "suggestion-decorator",
|
|
442
|
+
"data-decoration-id": decorationId,
|
|
443
|
+
}
|
|
444
|
+
),
|
|
482
445
|
]);
|
|
483
446
|
},
|
|
484
447
|
},
|
|
@@ -7,9 +7,9 @@ export type SuggestionsMenuStaticParams<T extends SuggestionItem> = {
|
|
|
7
7
|
|
|
8
8
|
export type SuggestionsMenuDynamicParams<T extends SuggestionItem> = {
|
|
9
9
|
items: T[];
|
|
10
|
-
|
|
10
|
+
keyboardHoveredItemIndex: number;
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
referenceRect: DOMRect;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export type SuggestionsMenu<T extends SuggestionItem> = EditorElement<
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Editor, EditorOptions } from "@tiptap/core";
|
|
2
2
|
import { UiFactories } from "./BlockNoteExtensions";
|
|
3
|
-
export
|
|
3
|
+
export type BlockNoteEditorOptions = EditorOptions & {
|
|
4
4
|
enableBlockNoteExtensions: boolean;
|
|
5
5
|
disableHistoryExtension: boolean;
|
|
6
6
|
uiFactories: UiFactories;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { Extensions } from "@tiptap/core";
|
|
2
|
-
import { Node } from "@tiptap/core";
|
|
3
2
|
import { FormattingToolbarFactory } from "./extensions/FormattingToolbar/FormattingToolbarFactoryTypes";
|
|
4
3
|
import { HyperlinkToolbarFactory } from "./extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes";
|
|
5
4
|
import { SuggestionsMenuFactory } from "./shared/plugins/suggestion/SuggestionsMenuFactoryTypes";
|
|
6
5
|
import { BlockSideMenuFactory } from "./extensions/DraggableBlocks/BlockSideMenuFactoryTypes";
|
|
7
6
|
import { SlashMenuItem } from "./extensions/SlashMenu/SlashMenuItem";
|
|
8
|
-
export
|
|
9
|
-
export declare type UiFactories = Partial<{
|
|
7
|
+
export type UiFactories = Partial<{
|
|
10
8
|
formattingToolbarFactory: FormattingToolbarFactory;
|
|
11
9
|
hyperlinkToolbarFactory: HyperlinkToolbarFactory;
|
|
12
10
|
slashMenuFactory: SuggestionsMenuFactory<SlashMenuItem>;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Node } from "prosemirror-model";
|
|
2
|
+
import { EditorState } from "prosemirror-state";
|
|
3
|
+
import { Block, Document } from "../extensions/Blocks/apiTypes";
|
|
4
|
+
export declare function BlockFromPMNode(node: Node): Block;
|
|
5
|
+
export declare function DocumentFromPMState(_state: EditorState): Document;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Extension } from "@tiptap/core";
|
|
2
|
+
declare module "@tiptap/core" {
|
|
3
|
+
interface Commands<ReturnType> {
|
|
4
|
+
blockBackgroundColor: {
|
|
5
|
+
setBlockBackgroundColor: (posInBlock: number, color: string) => ReturnType;
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export declare const BackgroundColorExtension: Extension<any, any>;
|
|
@@ -8,6 +8,7 @@ import { Plugin } from "prosemirror-state";
|
|
|
8
8
|
* Solution: When attributes change on a node, this plugin sets a data-* attribute with the "previous" value. This way we can still use CSS transitions. (See block.module.css)
|
|
9
9
|
*/
|
|
10
10
|
export declare const PreviousBlockTypePlugin: () => Plugin<{
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
prevTransactionOldBlockAttrs: any;
|
|
12
|
+
currentTransactionOldBlockAttrs: any;
|
|
13
|
+
updatedBlocks: Set<string>;
|
|
13
14
|
}>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type BlockSpec<Type extends string, Props extends Record<string, string>> = {
|
|
2
|
+
type: Type;
|
|
3
|
+
props: Props;
|
|
4
|
+
};
|
|
5
|
+
export type BlockSpecUpdate<Spec> = Spec extends BlockSpec<infer Type, infer Props> ? {
|
|
6
|
+
type: Type;
|
|
7
|
+
props?: Partial<Props>;
|
|
8
|
+
} : never;
|
|
9
|
+
export type NumberedListItemBlock = BlockSpec<"numberedListItem", {}>;
|
|
10
|
+
export type BulletListItemBlock = BlockSpec<"bulletListItem", {}>;
|
|
11
|
+
export type HeadingBlock = BlockSpec<"heading", {
|
|
12
|
+
level: "1" | "2" | "3";
|
|
13
|
+
}>;
|
|
14
|
+
export type ParagraphBlock = BlockSpec<"paragraph", {}>;
|
|
15
|
+
export type Block = ParagraphBlock | HeadingBlock | BulletListItemBlock | NumberedListItemBlock;
|
|
16
|
+
export type BlockUpdate = BlockSpecUpdate<Block>;
|