@blocknote/core 0.4.0 → 0.4.3
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 +12371 -12268
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +20 -20
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +2 -2
- package/src/BlockNoteEditor.ts +237 -14
- package/src/BlockNoteExtensions.ts +19 -15
- package/src/api/blockManipulation/__snapshots__/blockManipulation.test.ts.snap +616 -0
- package/src/api/blockManipulation/blockManipulation.test.ts +172 -0
- package/src/api/blockManipulation/blockManipulation.ts +25 -14
- package/src/api/formatConversions/__snapshots__/formatConversions.test.ts.snap +346 -0
- package/src/api/formatConversions/formatConversions.test.ts +766 -0
- package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +268 -0
- package/src/api/nodeConversions/nodeConversions.test.ts +244 -0
- package/src/api/nodeConversions/nodeConversions.ts +167 -58
- package/src/api/nodeConversions/testUtil.ts +61 -0
- package/src/api/util/nodeUtil.ts +38 -0
- package/src/editor.module.css +1 -0
- package/src/extensions/Blocks/api/blockTypes.ts +14 -9
- package/src/extensions/Blocks/api/cursorPositionTypes.ts +2 -0
- package/src/extensions/Blocks/api/inlineContentTypes.ts +27 -36
- package/src/extensions/Blocks/nodes/Block.module.css +39 -36
- package/src/extensions/Blocks/nodes/BlockContainer.ts +15 -14
- package/src/extensions/DraggableBlocks/DraggableBlocksPlugin.ts +149 -87
- package/src/extensions/SlashMenu/BaseSlashMenuItem.ts +31 -0
- package/src/extensions/SlashMenu/SlashMenuExtension.ts +10 -7
- package/src/extensions/SlashMenu/{defaultSlashCommands.tsx → defaultSlashMenuItems.tsx} +59 -106
- package/src/extensions/SlashMenu/index.ts +3 -7
- package/src/index.ts +2 -3
- package/src/shared/plugins/suggestion/SuggestionItem.ts +2 -13
- package/src/shared/plugins/suggestion/SuggestionPlugin.ts +31 -18
- package/src/shared/utils.ts +6 -0
- package/types/src/BlockNoteEditor.d.ts +82 -8
- package/types/src/BlockNoteExtensions.d.ts +5 -4
- package/types/src/api/Editor.d.ts +26 -6
- package/types/src/api/blockManipulation/blockManipulation.d.ts +5 -5
- package/types/src/api/blockManipulation/blockManipulation.test.d.ts +1 -0
- package/types/src/api/formatConversions/formatConversions.test.d.ts +1 -0
- package/types/src/api/nodeConversions/nodeConversions.d.ts +11 -4
- package/types/src/api/nodeConversions/nodeConversions.test.d.ts +1 -0
- package/types/src/api/nodeConversions/testUtil.d.ts +2 -0
- package/types/src/api/util/nodeUtil.d.ts +8 -0
- package/types/src/extensions/Blocks/api/blockTypes.d.ts +10 -9
- package/types/src/extensions/Blocks/api/cursorPositionTypes.d.ts +2 -0
- package/types/src/extensions/Blocks/api/inlineContentTypes.d.ts +25 -19
- package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +15 -0
- package/types/src/extensions/SlashMenu/BaseSlashMenuItem.d.ts +20 -0
- package/types/src/extensions/SlashMenu/SlashMenuExtension.d.ts +4 -2
- package/types/src/extensions/SlashMenu/defaultSlashMenuItems.d.ts +5 -0
- package/types/src/extensions/SlashMenu/index.d.ts +3 -3
- package/types/src/index.d.ts +2 -3
- package/types/src/shared/plugins/suggestion/SuggestionItem.d.ts +3 -11
- package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +4 -4
- package/types/src/shared/utils.d.ts +3 -0
- package/src/api/Editor.ts +0 -142
- package/src/extensions/SlashMenu/SlashMenuItem.ts +0 -34
- package/types/src/EditorElement.d.ts +0 -7
- package/types/src/api/Document.d.ts +0 -5
- package/types/src/api/removeUnderlinesRehypePlugin.d.ts +0 -6
- package/types/src/api/simplifyBlocksRehypePlugin.d.ts +0 -16
- package/types/src/extensions/Blocks/BlockAttributes.d.ts +0 -2
- package/types/src/extensions/Blocks/MultipleNodeSelection.d.ts +0 -24
- package/types/src/extensions/Blocks/api/apiTypes.d.ts +0 -18
- package/types/src/extensions/Blocks/api/styleTypes.d.ts +0 -22
- package/types/src/extensions/Blocks/apiTypes.d.ts +0 -16
- package/types/src/extensions/Blocks/nodes/Block.d.ts +0 -24
- package/types/src/extensions/Blocks/nodes/BlockContent/BlockContentTypes.d.ts +0 -4
- package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContentTypes.d.ts +0 -4
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContentTypes.d.ts +0 -2
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContentTypes.d.ts +0 -2
- package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContentTypes.d.ts +0 -2
- package/types/src/extensions/Blocks/nodes/BlockTypes/HeadingBlock/HeadingContent.d.ts +0 -8
- package/types/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.d.ts +0 -8
- package/types/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.d.ts +0 -2
- package/types/src/extensions/Blocks/nodes/BlockTypes/TextBlock/TextContent.d.ts +0 -6
- package/types/src/extensions/BubbleMenu/BubbleMenuExtension.d.ts +0 -8
- package/types/src/extensions/BubbleMenu/BubbleMenuFactoryTypes.d.ts +0 -27
- package/types/src/extensions/BubbleMenu/BubbleMenuPlugin.d.ts +0 -44
- package/types/src/extensions/DraggableBlocks/BlockMenuFactoryTypes.d.ts +0 -12
- package/types/src/extensions/DraggableBlocks/DragMenuFactoryTypes.d.ts +0 -18
- package/types/src/extensions/Hyperlinks/HyperlinkMark.d.ts +0 -8
- package/types/src/extensions/Hyperlinks/HyperlinkMenuFactoryTypes.d.ts +0 -11
- package/types/src/extensions/Hyperlinks/HyperlinkMenuPlugin.d.ts +0 -11
- package/types/src/extensions/Paragraph/FixedParagraph.d.ts +0 -1
- package/types/src/extensions/SlashMenu/defaultCommands.d.ts +0 -8
- package/types/src/utils.d.ts +0 -2
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { mergeAttributes, Node } from "@tiptap/core";
|
|
2
2
|
import { Fragment, Node as PMNode, Slice } from "prosemirror-model";
|
|
3
3
|
import { TextSelection } from "prosemirror-state";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
blockToNode,
|
|
6
|
+
inlineContentToNodes,
|
|
7
|
+
} from "../../../api/nodeConversions/nodeConversions";
|
|
5
8
|
import { PartialBlock } from "../api/blockTypes";
|
|
6
9
|
import { getBlockInfoFromPos } from "../helpers/getBlockInfoFromPos";
|
|
7
10
|
import { PreviousBlockTypePlugin } from "../PreviousBlockTypePlugin";
|
|
@@ -329,15 +332,7 @@ export const BlockContainer = Node.create<IBlock>({
|
|
|
329
332
|
} else {
|
|
330
333
|
// Adds a text node with the provided styles converted into marks to the content, for each InlineContent
|
|
331
334
|
// object.
|
|
332
|
-
|
|
333
|
-
const marks = [];
|
|
334
|
-
|
|
335
|
-
for (const style of styledText.styles) {
|
|
336
|
-
marks.push(state.schema.mark(style.type, style.props));
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
content.push(state.schema.text(styledText.text, marks));
|
|
340
|
-
}
|
|
335
|
+
content = inlineContentToNodes(block.content, state.schema);
|
|
341
336
|
}
|
|
342
337
|
|
|
343
338
|
// Replaces the contents of the blockContent node with the previously created text node(s).
|
|
@@ -350,10 +345,16 @@ export const BlockContainer = Node.create<IBlock>({
|
|
|
350
345
|
|
|
351
346
|
// Changes the block type and adds the provided props as node attributes. Also preserves all existing node
|
|
352
347
|
// attributes that are compatible with the new type.
|
|
353
|
-
state.tr.setNodeMarkup(
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
348
|
+
state.tr.setNodeMarkup(
|
|
349
|
+
startPos,
|
|
350
|
+
block.type === undefined
|
|
351
|
+
? undefined
|
|
352
|
+
: state.schema.nodes[block.type],
|
|
353
|
+
{
|
|
354
|
+
...contentNode.attrs,
|
|
355
|
+
...block.props,
|
|
356
|
+
}
|
|
357
|
+
);
|
|
357
358
|
}
|
|
358
359
|
|
|
359
360
|
return true;
|
|
@@ -150,8 +150,10 @@ function setDragImage(view: EditorView, from: number, to = from) {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
// dataTransfer.setDragImage(element) only works if element is attached to the DOM.
|
|
153
|
+
unsetDragImage();
|
|
153
154
|
dragImageElement = parentClone;
|
|
154
|
-
dragImageElement.className =
|
|
155
|
+
dragImageElement.className =
|
|
156
|
+
dragImageElement.className + " " + styles.dragPreview;
|
|
155
157
|
document.body.appendChild(dragImageElement);
|
|
156
158
|
}
|
|
157
159
|
|
|
@@ -245,104 +247,164 @@ export class BlockMenuView {
|
|
|
245
247
|
|
|
246
248
|
this.blockMenu = blockMenuFactory(this.getStaticParams());
|
|
247
249
|
|
|
250
|
+
document.body.addEventListener("drop", this.onDrop, true);
|
|
251
|
+
document.body.addEventListener("dragover", this.onDragOver);
|
|
252
|
+
|
|
248
253
|
// Shows or updates menu position whenever the cursor moves, if the menu isn't frozen.
|
|
249
|
-
document.body.addEventListener(
|
|
250
|
-
"mousemove",
|
|
251
|
-
(event) => {
|
|
252
|
-
if (this.menuFrozen) {
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Editor itself may have padding or other styling which affects size/position, so we get the boundingRect of
|
|
257
|
-
// the first child (i.e. the blockGroup that wraps all blocks in the editor) for a more accurate bounding box.
|
|
258
|
-
const editorBoundingBox = (
|
|
259
|
-
this.editor.view.dom.firstChild! as HTMLElement
|
|
260
|
-
).getBoundingClientRect();
|
|
261
|
-
|
|
262
|
-
this.horizontalPosAnchor = editorBoundingBox.x;
|
|
263
|
-
|
|
264
|
-
// Gets block at mouse cursor's vertical position.
|
|
265
|
-
const coords = {
|
|
266
|
-
left: editorBoundingBox.left + editorBoundingBox.width / 2, // take middle of editor
|
|
267
|
-
top: event.clientY,
|
|
268
|
-
};
|
|
269
|
-
const block = getDraggableBlockFromCoords(coords, this.editor.view);
|
|
270
|
-
|
|
271
|
-
// Closes the menu if the mouse cursor is beyond the editor vertically.
|
|
272
|
-
if (!block) {
|
|
273
|
-
if (this.menuOpen) {
|
|
274
|
-
this.menuOpen = false;
|
|
275
|
-
this.blockMenu.hide();
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Doesn't update if the menu is already open and the mouse cursor is still hovering the same block.
|
|
282
|
-
if (
|
|
283
|
-
this.menuOpen &&
|
|
284
|
-
this.hoveredBlockContent?.hasAttribute("data-id") &&
|
|
285
|
-
this.hoveredBlockContent?.getAttribute("data-id") === block.id
|
|
286
|
-
) {
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Gets the block's content node, which lets to ignore child blocks when determining the block menu's position.
|
|
291
|
-
const blockContent = block.node.firstChild as HTMLElement;
|
|
292
|
-
this.hoveredBlockContent = blockContent;
|
|
293
|
-
|
|
294
|
-
if (!blockContent) {
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Shows or updates elements.
|
|
299
|
-
if (!this.menuOpen) {
|
|
300
|
-
this.menuOpen = true;
|
|
301
|
-
this.blockMenu.render(this.getDynamicParams(), true);
|
|
302
|
-
} else {
|
|
303
|
-
this.blockMenu.render(this.getDynamicParams(), false);
|
|
304
|
-
}
|
|
305
|
-
},
|
|
306
|
-
true
|
|
307
|
-
);
|
|
254
|
+
document.body.addEventListener("mousemove", this.onMouseMove, true);
|
|
308
255
|
|
|
309
256
|
// Hides and unfreezes the menu whenever the user selects the editor with the mouse or presses a key.
|
|
310
257
|
// TODO: Better integration with suggestions menu and only editor scope?
|
|
311
|
-
document.body.addEventListener(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (this.blockMenu.element?.contains(event.target as HTMLElement)) {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (this.menuOpen) {
|
|
319
|
-
this.menuOpen = false;
|
|
320
|
-
this.blockMenu.hide();
|
|
321
|
-
}
|
|
258
|
+
document.body.addEventListener("mousedown", this.onMouseDown, true);
|
|
259
|
+
document.body.addEventListener("keydown", this.onKeyDown, true);
|
|
260
|
+
}
|
|
322
261
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
262
|
+
/**
|
|
263
|
+
* If the event is outside of the editor contents,
|
|
264
|
+
* we dispatch a fake event, so that we can still drop the content
|
|
265
|
+
* when dragging / dropping to the side of the editor
|
|
266
|
+
*/
|
|
267
|
+
onDrop = (event: DragEvent) => {
|
|
268
|
+
if ((event as any).synthetic) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
let pos = this.editor.view.posAtCoords({
|
|
272
|
+
left: event.clientX,
|
|
273
|
+
top: event.clientY,
|
|
274
|
+
});
|
|
334
275
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
276
|
+
if (!pos || pos.inside === -1) {
|
|
277
|
+
const evt = new Event("drop", event) as any;
|
|
278
|
+
const editorBoundingBox = (
|
|
279
|
+
this.editor.view.dom.firstChild! as HTMLElement
|
|
280
|
+
).getBoundingClientRect();
|
|
281
|
+
evt.clientX = editorBoundingBox.left + editorBoundingBox.width / 2;
|
|
282
|
+
evt.clientY = event.clientY;
|
|
283
|
+
evt.dataTransfer = event.dataTransfer;
|
|
284
|
+
evt.preventDefault = () => event.preventDefault();
|
|
285
|
+
evt.synthetic = true; // prevent recursion
|
|
286
|
+
// console.log("dispatch fake drop");
|
|
287
|
+
this.editor.view.dom.dispatchEvent(evt);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* If the event is outside of the editor contents,
|
|
293
|
+
* we dispatch a fake event, so that we can still drop the content
|
|
294
|
+
* when dragging / dropping to the side of the editor
|
|
295
|
+
*/
|
|
296
|
+
onDragOver = (event: DragEvent) => {
|
|
297
|
+
if ((event as any).synthetic) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
let pos = this.editor.view.posAtCoords({
|
|
301
|
+
left: event.clientX,
|
|
302
|
+
top: event.clientY,
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
if (!pos || pos.inside === -1) {
|
|
306
|
+
const evt = new Event("dragover", event) as any;
|
|
307
|
+
const editorBoundingBox = (
|
|
308
|
+
this.editor.view.dom.firstChild! as HTMLElement
|
|
309
|
+
).getBoundingClientRect();
|
|
310
|
+
evt.clientX = editorBoundingBox.left + editorBoundingBox.width / 2;
|
|
311
|
+
evt.clientY = event.clientY;
|
|
312
|
+
evt.dataTransfer = event.dataTransfer;
|
|
313
|
+
evt.preventDefault = () => event.preventDefault();
|
|
314
|
+
evt.synthetic = true; // prevent recursion
|
|
315
|
+
// console.log("dispatch fake dragover");
|
|
316
|
+
this.editor.view.dom.dispatchEvent(evt);
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
onKeyDown = (_event: KeyboardEvent) => {
|
|
321
|
+
if (this.menuOpen) {
|
|
322
|
+
this.menuOpen = false;
|
|
323
|
+
this.blockMenu.hide();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
this.menuFrozen = false;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
onMouseDown = (event: MouseEvent) => {
|
|
330
|
+
if (this.blockMenu.element?.contains(event.target as HTMLElement)) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (this.menuOpen) {
|
|
335
|
+
this.menuOpen = false;
|
|
336
|
+
this.blockMenu.hide();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
this.menuFrozen = false;
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
onMouseMove = (event: MouseEvent) => {
|
|
343
|
+
if (this.menuFrozen) {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Editor itself may have padding or other styling which affects size/position, so we get the boundingRect of
|
|
348
|
+
// the first child (i.e. the blockGroup that wraps all blocks in the editor) for a more accurate bounding box.
|
|
349
|
+
const editorBoundingBox = (
|
|
350
|
+
this.editor.view.dom.firstChild! as HTMLElement
|
|
351
|
+
).getBoundingClientRect();
|
|
352
|
+
|
|
353
|
+
this.horizontalPosAnchor = editorBoundingBox.x;
|
|
354
|
+
|
|
355
|
+
// Gets block at mouse cursor's vertical position.
|
|
356
|
+
const coords = {
|
|
357
|
+
left: editorBoundingBox.left + editorBoundingBox.width / 2, // take middle of editor
|
|
358
|
+
top: event.clientY,
|
|
359
|
+
};
|
|
360
|
+
const block = getDraggableBlockFromCoords(coords, this.editor.view);
|
|
361
|
+
|
|
362
|
+
// Closes the menu if the mouse cursor is beyond the editor vertically.
|
|
363
|
+
if (!block) {
|
|
364
|
+
if (this.menuOpen) {
|
|
365
|
+
this.menuOpen = false;
|
|
366
|
+
this.blockMenu.hide();
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Doesn't update if the menu is already open and the mouse cursor is still hovering the same block.
|
|
373
|
+
if (
|
|
374
|
+
this.menuOpen &&
|
|
375
|
+
this.hoveredBlockContent?.hasAttribute("data-id") &&
|
|
376
|
+
this.hoveredBlockContent?.getAttribute("data-id") === block.id
|
|
377
|
+
) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Gets the block's content node, which lets to ignore child blocks when determining the block menu's position.
|
|
382
|
+
const blockContent = block.node.firstChild as HTMLElement;
|
|
383
|
+
this.hoveredBlockContent = blockContent;
|
|
384
|
+
|
|
385
|
+
if (!blockContent) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Shows or updates elements.
|
|
390
|
+
if (!this.menuOpen) {
|
|
391
|
+
this.menuOpen = true;
|
|
392
|
+
this.blockMenu.render(this.getDynamicParams(), true);
|
|
393
|
+
} else {
|
|
394
|
+
this.blockMenu.render(this.getDynamicParams(), false);
|
|
395
|
+
}
|
|
396
|
+
};
|
|
340
397
|
|
|
341
398
|
destroy() {
|
|
342
399
|
if (this.menuOpen) {
|
|
343
400
|
this.menuOpen = false;
|
|
344
401
|
this.blockMenu.hide();
|
|
345
402
|
}
|
|
403
|
+
document.body.removeEventListener("mousemove", this.onMouseMove);
|
|
404
|
+
document.body.removeEventListener("dragover", this.onDragOver);
|
|
405
|
+
document.body.removeEventListener("drop", this.onDrop);
|
|
406
|
+
document.body.removeEventListener("mousedown", this.onMouseDown);
|
|
407
|
+
document.body.removeEventListener("keydown", this.onKeyDown);
|
|
346
408
|
}
|
|
347
409
|
|
|
348
410
|
addBlock() {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { SuggestionItem } from "../../shared/plugins/suggestion/SuggestionItem";
|
|
2
|
+
import { BlockNoteEditor } from "../../BlockNoteEditor";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A class that defines a slash command (/<command>).
|
|
6
|
+
*
|
|
7
|
+
* (Not to be confused with ProseMirror commands nor TipTap commands.)
|
|
8
|
+
*/
|
|
9
|
+
export class BaseSlashMenuItem extends SuggestionItem {
|
|
10
|
+
/**
|
|
11
|
+
* Constructs a new slash-command.
|
|
12
|
+
*
|
|
13
|
+
* @param name The name of the command
|
|
14
|
+
* @param execute The callback for creating a new node
|
|
15
|
+
* @param aliases Aliases for this command
|
|
16
|
+
*/
|
|
17
|
+
constructor(
|
|
18
|
+
public readonly name: string,
|
|
19
|
+
public readonly execute: (editor: BlockNoteEditor) => void,
|
|
20
|
+
public readonly aliases: string[] = []
|
|
21
|
+
) {
|
|
22
|
+
super(name, (query: string): boolean => {
|
|
23
|
+
return (
|
|
24
|
+
this.name.toLowerCase().startsWith(query.toLowerCase()) ||
|
|
25
|
+
this.aliases.filter((alias) =>
|
|
26
|
+
alias.toLowerCase().startsWith(query.toLowerCase())
|
|
27
|
+
).length !== 0
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -2,10 +2,12 @@ import { Extension } from "@tiptap/core";
|
|
|
2
2
|
import { PluginKey } from "prosemirror-state";
|
|
3
3
|
import { createSuggestionPlugin } from "../../shared/plugins/suggestion/SuggestionPlugin";
|
|
4
4
|
import { SuggestionsMenuFactory } from "../../shared/plugins/suggestion/SuggestionsMenuFactoryTypes";
|
|
5
|
-
import {
|
|
5
|
+
import { BaseSlashMenuItem } from "./BaseSlashMenuItem";
|
|
6
|
+
import { BlockNoteEditor } from "../../BlockNoteEditor";
|
|
6
7
|
|
|
7
8
|
export type SlashMenuOptions = {
|
|
8
|
-
|
|
9
|
+
editor: BlockNoteEditor | undefined;
|
|
10
|
+
commands: BaseSlashMenuItem[] | undefined;
|
|
9
11
|
slashMenuFactory: SuggestionsMenuFactory<any> | undefined;
|
|
10
12
|
};
|
|
11
13
|
|
|
@@ -16,6 +18,7 @@ export const SlashMenuExtension = Extension.create<SlashMenuOptions>({
|
|
|
16
18
|
|
|
17
19
|
addOptions() {
|
|
18
20
|
return {
|
|
21
|
+
editor: undefined,
|
|
19
22
|
commands: undefined,
|
|
20
23
|
slashMenuFactory: undefined,
|
|
21
24
|
};
|
|
@@ -29,16 +32,16 @@ export const SlashMenuExtension = Extension.create<SlashMenuOptions>({
|
|
|
29
32
|
const commands = this.options.commands;
|
|
30
33
|
|
|
31
34
|
return [
|
|
32
|
-
createSuggestionPlugin<
|
|
35
|
+
createSuggestionPlugin<BaseSlashMenuItem>({
|
|
33
36
|
pluginKey: SlashMenuPluginKey,
|
|
34
|
-
editor: this.editor
|
|
37
|
+
editor: this.options.editor!,
|
|
35
38
|
defaultTriggerCharacter: "/",
|
|
36
39
|
suggestionsMenuFactory: this.options.slashMenuFactory!,
|
|
37
40
|
items: (query) => {
|
|
38
|
-
return commands.filter((cmd:
|
|
41
|
+
return commands.filter((cmd: BaseSlashMenuItem) => cmd.match(query));
|
|
39
42
|
},
|
|
40
|
-
onSelectItem: ({ item, editor
|
|
41
|
-
item.execute(editor
|
|
43
|
+
onSelectItem: ({ item, editor }) => {
|
|
44
|
+
item.execute(editor);
|
|
42
45
|
},
|
|
43
46
|
}),
|
|
44
47
|
];
|
|
@@ -1,135 +1,88 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { BaseSlashMenuItem } from "./BaseSlashMenuItem";
|
|
2
|
+
import { PartialBlock } from "../Blocks/api/blockTypes";
|
|
3
|
+
import { BlockNoteEditor } from "../../BlockNoteEditor";
|
|
4
|
+
|
|
5
|
+
function insertOrUpdateBlock(editor: BlockNoteEditor, block: PartialBlock) {
|
|
6
|
+
const currentBlock = editor.getTextCursorPosition().block;
|
|
7
|
+
|
|
8
|
+
if (
|
|
9
|
+
(currentBlock.content.length === 1 &&
|
|
10
|
+
currentBlock.content[0].type === "text" &&
|
|
11
|
+
currentBlock.content[0].text === "/") ||
|
|
12
|
+
currentBlock.content.length === 0
|
|
13
|
+
) {
|
|
14
|
+
editor.updateBlock(currentBlock, block);
|
|
15
|
+
} else {
|
|
16
|
+
editor.insertBlocks([block], currentBlock, "after");
|
|
17
|
+
editor.setTextCursorPosition(editor.getTextCursorPosition().nextBlock!);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
4
20
|
|
|
5
21
|
/**
|
|
6
22
|
* An array containing commands for creating all default blocks.
|
|
7
23
|
*/
|
|
8
|
-
export const
|
|
24
|
+
export const defaultSlashMenuItems: BaseSlashMenuItem[] = [
|
|
9
25
|
// Command for creating a level 1 heading
|
|
10
|
-
new
|
|
26
|
+
new BaseSlashMenuItem(
|
|
11
27
|
"Heading",
|
|
12
|
-
(editor
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
type: "heading",
|
|
19
|
-
props: {
|
|
20
|
-
level: "1",
|
|
21
|
-
},
|
|
22
|
-
})
|
|
23
|
-
.run();
|
|
24
|
-
},
|
|
25
|
-
["h", "heading1", "h1"],
|
|
26
|
-
"Headings",
|
|
27
|
-
"Used for a top-level heading",
|
|
28
|
-
formatKeyboardShortcut("Mod-Alt-1")
|
|
28
|
+
(editor) =>
|
|
29
|
+
insertOrUpdateBlock(editor, {
|
|
30
|
+
type: "heading",
|
|
31
|
+
props: { level: "1" },
|
|
32
|
+
}),
|
|
33
|
+
["h", "heading1", "h1"]
|
|
29
34
|
),
|
|
30
35
|
|
|
31
36
|
// Command for creating a level 2 heading
|
|
32
|
-
new
|
|
37
|
+
new BaseSlashMenuItem(
|
|
33
38
|
"Heading 2",
|
|
34
|
-
(editor
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
type: "heading",
|
|
41
|
-
props: {
|
|
42
|
-
level: "2",
|
|
43
|
-
},
|
|
44
|
-
})
|
|
45
|
-
.run();
|
|
46
|
-
},
|
|
47
|
-
["h2", "heading2", "subheading"],
|
|
48
|
-
"Headings",
|
|
49
|
-
"Used for key sections",
|
|
50
|
-
formatKeyboardShortcut("Mod-Alt-2")
|
|
39
|
+
(editor) =>
|
|
40
|
+
insertOrUpdateBlock(editor, {
|
|
41
|
+
type: "heading",
|
|
42
|
+
props: { level: "2" },
|
|
43
|
+
}),
|
|
44
|
+
["h2", "heading2", "subheading"]
|
|
51
45
|
),
|
|
52
46
|
|
|
53
47
|
// Command for creating a level 3 heading
|
|
54
|
-
new
|
|
48
|
+
new BaseSlashMenuItem(
|
|
55
49
|
"Heading 3",
|
|
56
|
-
(editor
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
type: "heading",
|
|
63
|
-
props: {
|
|
64
|
-
level: "3",
|
|
65
|
-
},
|
|
66
|
-
})
|
|
67
|
-
.run();
|
|
68
|
-
},
|
|
69
|
-
["h3", "heading3", "subheading"],
|
|
70
|
-
"Headings",
|
|
71
|
-
"Used for subsections and group headings",
|
|
72
|
-
formatKeyboardShortcut("Mod-Alt-3")
|
|
50
|
+
(editor) =>
|
|
51
|
+
insertOrUpdateBlock(editor, {
|
|
52
|
+
type: "heading",
|
|
53
|
+
props: { level: "3" },
|
|
54
|
+
}),
|
|
55
|
+
["h3", "heading3", "subheading"]
|
|
73
56
|
),
|
|
74
57
|
|
|
75
58
|
// Command for creating an ordered list
|
|
76
|
-
new
|
|
59
|
+
new BaseSlashMenuItem(
|
|
77
60
|
"Numbered List",
|
|
78
|
-
(editor
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
.BNCreateOrUpdateBlock(range.from, {
|
|
84
|
-
type: "numberedListItem",
|
|
85
|
-
props: {},
|
|
86
|
-
})
|
|
87
|
-
.run();
|
|
88
|
-
},
|
|
89
|
-
["li", "list", "numberedlist", "numbered list"],
|
|
90
|
-
"Basic blocks",
|
|
91
|
-
"Used to display a numbered list",
|
|
92
|
-
"Mod-Alt-7"
|
|
61
|
+
(editor) =>
|
|
62
|
+
insertOrUpdateBlock(editor, {
|
|
63
|
+
type: "numberedListItem",
|
|
64
|
+
}),
|
|
65
|
+
["li", "list", "numberedlist", "numbered list"]
|
|
93
66
|
),
|
|
94
67
|
|
|
95
68
|
// Command for creating a bullet list
|
|
96
|
-
new
|
|
69
|
+
new BaseSlashMenuItem(
|
|
97
70
|
"Bullet List",
|
|
98
|
-
(editor
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
.BNCreateOrUpdateBlock(range.from, {
|
|
104
|
-
type: "bulletListItem",
|
|
105
|
-
props: {},
|
|
106
|
-
})
|
|
107
|
-
.run();
|
|
108
|
-
},
|
|
109
|
-
["ul", "list", "bulletlist", "bullet list"],
|
|
110
|
-
"Basic blocks",
|
|
111
|
-
"Used to display an unordered list",
|
|
112
|
-
"Mod-Alt-9"
|
|
71
|
+
(editor) =>
|
|
72
|
+
insertOrUpdateBlock(editor, {
|
|
73
|
+
type: "bulletListItem",
|
|
74
|
+
}),
|
|
75
|
+
["ul", "list", "bulletlist", "bullet list"]
|
|
113
76
|
),
|
|
114
77
|
|
|
115
78
|
// Command for creating a paragraph (pretty useless)
|
|
116
|
-
new
|
|
79
|
+
new BaseSlashMenuItem(
|
|
117
80
|
"Paragraph",
|
|
118
|
-
(editor
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
.BNCreateOrUpdateBlock(range.from, {
|
|
124
|
-
type: "paragraph",
|
|
125
|
-
props: {},
|
|
126
|
-
})
|
|
127
|
-
.run();
|
|
128
|
-
},
|
|
129
|
-
["p"],
|
|
130
|
-
"Basic blocks",
|
|
131
|
-
"Used for the body of your document",
|
|
132
|
-
"Mod-Alt-0"
|
|
81
|
+
(editor) =>
|
|
82
|
+
insertOrUpdateBlock(editor, {
|
|
83
|
+
type: "paragraph",
|
|
84
|
+
}),
|
|
85
|
+
["p"]
|
|
133
86
|
),
|
|
134
87
|
|
|
135
88
|
// replaceRangeWithNode(editor, range, node);
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defaultSlashMenuItems } from "./defaultSlashMenuItems";
|
|
2
2
|
import { SlashMenuExtension } from "./SlashMenuExtension";
|
|
3
|
-
import {
|
|
3
|
+
import { BaseSlashMenuItem } from "./BaseSlashMenuItem";
|
|
4
4
|
|
|
5
|
-
export {
|
|
6
|
-
defaultSlashCommands,
|
|
7
|
-
SlashMenuItem as SlashCommand,
|
|
8
|
-
SlashMenuExtension,
|
|
9
|
-
};
|
|
5
|
+
export { defaultSlashMenuItems, BaseSlashMenuItem, SlashMenuExtension };
|
package/src/index.ts
CHANGED
|
@@ -4,9 +4,8 @@ export * from "./extensions/Blocks/api/blockTypes";
|
|
|
4
4
|
export * from "./extensions/DraggableBlocks/BlockSideMenuFactoryTypes";
|
|
5
5
|
export * from "./extensions/FormattingToolbar/FormattingToolbarFactoryTypes";
|
|
6
6
|
export * from "./extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes";
|
|
7
|
-
export {
|
|
8
|
-
export * from "./extensions/SlashMenu/
|
|
7
|
+
export { defaultSlashMenuItems } from "./extensions/SlashMenu/defaultSlashMenuItems";
|
|
8
|
+
export * from "./extensions/SlashMenu/BaseSlashMenuItem";
|
|
9
9
|
export * from "./shared/EditorElement";
|
|
10
10
|
export type { SuggestionItem } from "./shared/plugins/suggestion/SuggestionItem";
|
|
11
11
|
export * from "./shared/plugins/suggestion/SuggestionsMenuFactoryTypes";
|
|
12
|
-
export * from "../src/api/Editor";
|
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A generic interface used in all suggestion menus (slash menu, mentions, etc)
|
|
3
3
|
*/
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
* The name of the item
|
|
7
|
-
*/
|
|
8
|
-
name: string;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* This function matches this item against a query string, the function should return **true** if the item
|
|
12
|
-
* matches the query or **false** otherwise.
|
|
13
|
-
*
|
|
14
|
-
* @param query the query string
|
|
15
|
-
*/
|
|
16
|
-
match(query: string): boolean;
|
|
4
|
+
export class SuggestionItem {
|
|
5
|
+
constructor(public name: string, public match: (query: string) => boolean) {}
|
|
17
6
|
}
|