@blocknote/core 0.13.4 → 0.14.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.js +526 -477
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +5 -5
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +29 -29
- package/src/api/blockManipulation/__snapshots__/blockManipulation.test.ts.snap +98 -0
- package/src/api/blockManipulation/blockManipulation.test.ts +86 -7
- package/src/api/exporters/html/__snapshots__/customBlock/basic/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/customBlock/basic/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/customParagraph/lineBreaks/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/customParagraph/lineBreaks/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/paragraph/lineBreaks/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/paragraph/lineBreaks/internal.html +1 -0
- package/src/api/exporters/markdown/__snapshots__/customBlock/basic/markdown.md +5 -0
- package/src/api/exporters/markdown/__snapshots__/customParagraph/lineBreaks/markdown.md +1 -0
- package/src/api/exporters/markdown/__snapshots__/paragraph/lineBreaks/markdown.md +2 -0
- package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +566 -0
- package/src/api/nodeConversions/nodeConversions.test.ts +2 -0
- package/src/api/nodeConversions/nodeConversions.ts +2 -4
- package/src/api/parsers/html/__snapshots__/paste/parse-image-in-paragraph.json +16 -0
- package/src/api/parsers/html/parseHTML.test.ts +8 -0
- package/src/api/parsers/pasteExtension.ts +5 -0
- package/src/api/testUtil/cases/customBlocks.ts +9 -0
- package/src/api/testUtil/cases/defaultSchema.ts +9 -0
- package/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts +6 -6
- package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +7 -2
- package/src/blocks/TableBlockContent/TableBlockContent.ts +23 -1
- package/src/editor/Block.css +2 -3
- package/src/editor/BlockNoteEditor.ts +7 -6
- package/src/editor/BlockNoteExtensions.ts +10 -1
- package/src/editor/BlockNoteTipTapEditor.ts +1 -0
- package/src/extensions/FilePanel/FilePanelPlugin.ts +16 -12
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +12 -15
- package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +6 -2
- package/src/extensions/Placeholder/PlaceholderPlugin.ts +5 -1
- package/src/extensions/SideMenu/SideMenuPlugin.ts +157 -118
- package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +5 -2
- package/src/extensions/TableHandles/TableHandlesPlugin.ts +7 -4
- package/src/i18n/locales/pt.ts +1 -1
- package/src/i18n/locales/zh.ts +1 -1
- package/src/pm-nodes/BlockContainer.ts +11 -7
- package/src/schema/blocks/createSpec.ts +2 -2
- package/src/schema/inlineContent/createSpec.ts +2 -2
- package/types/src/editor/BlockNoteEditor.d.ts +1 -1
- package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +5 -5
- package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +0 -1
- package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +9 -8
- package/types/src/schema/blocks/createSpec.d.ts +2 -2
- package/types/src/schema/inlineContent/createSpec.d.ts +2 -2
|
@@ -26,46 +26,26 @@ export type SideMenuState<
|
|
|
26
26
|
block: Block<BSchema, I, S>;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
export function
|
|
30
|
-
|
|
29
|
+
export function getDraggableBlockFromElement(
|
|
30
|
+
element: Element,
|
|
31
31
|
view: EditorView
|
|
32
32
|
) {
|
|
33
|
-
if (!view.dom.isConnected) {
|
|
34
|
-
// view is not connected to the DOM, this can cause posAtCoords to fail
|
|
35
|
-
// (Cannot read properties of null (reading 'nearestDesc'), https://github.com/TypeCellOS/BlockNote/issues/123)
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const pos = view.posAtCoords(coords);
|
|
40
|
-
if (!pos) {
|
|
41
|
-
return undefined;
|
|
42
|
-
}
|
|
43
|
-
let node = view.domAtPos(pos.pos).node as HTMLElement;
|
|
44
|
-
|
|
45
|
-
if (node === view.dom) {
|
|
46
|
-
// mouse over root
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
33
|
while (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
!
|
|
34
|
+
element &&
|
|
35
|
+
element.parentElement &&
|
|
36
|
+
element.parentElement !== view.dom &&
|
|
37
|
+
!element.hasAttribute?.("data-id")
|
|
55
38
|
) {
|
|
56
|
-
|
|
39
|
+
element = element.parentElement;
|
|
57
40
|
}
|
|
58
|
-
if (!
|
|
41
|
+
if (!element.hasAttribute("data-id")) {
|
|
59
42
|
return undefined;
|
|
60
43
|
}
|
|
61
|
-
return { node, id:
|
|
44
|
+
return { node: element as HTMLElement, id: element.getAttribute("data-id")! };
|
|
62
45
|
}
|
|
63
46
|
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
view: EditorView
|
|
67
|
-
) {
|
|
68
|
-
const block = getDraggableBlockFromCoords(coords, view);
|
|
47
|
+
function blockPositionFromElement(element: Element, view: EditorView) {
|
|
48
|
+
const block = getDraggableBlockFromElement(element, view);
|
|
69
49
|
|
|
70
50
|
if (block && block.node.nodeType === 1) {
|
|
71
51
|
// TODO: this uses undocumented PM APIs? do we need this / let's add docs?
|
|
@@ -197,7 +177,21 @@ function dragStart<
|
|
|
197
177
|
top: e.clientY,
|
|
198
178
|
};
|
|
199
179
|
|
|
200
|
-
const
|
|
180
|
+
const elements = document.elementsFromPoint(coords.left, coords.top);
|
|
181
|
+
let blockEl = undefined;
|
|
182
|
+
|
|
183
|
+
for (const element of elements) {
|
|
184
|
+
if (view.dom.contains(element)) {
|
|
185
|
+
blockEl = getDraggableBlockFromElement(element, view);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!blockEl) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const pos = blockPositionFromElement(blockEl.node, view);
|
|
201
195
|
if (pos != null) {
|
|
202
196
|
const selection = view.state.selection;
|
|
203
197
|
const doc = view.state.doc;
|
|
@@ -252,8 +246,11 @@ export class SideMenuView<
|
|
|
252
246
|
S extends StyleSchema
|
|
253
247
|
> implements PluginView
|
|
254
248
|
{
|
|
255
|
-
|
|
256
|
-
|
|
249
|
+
public state?: SideMenuState<BSchema, I, S>;
|
|
250
|
+
public readonly emitUpdate: (state: SideMenuState<BSchema, I, S>) => void;
|
|
251
|
+
|
|
252
|
+
private needUpdate = false;
|
|
253
|
+
private mousePos: { x: number; y: number } | undefined;
|
|
257
254
|
|
|
258
255
|
// When true, the drag handle with be anchored at the same level as root elements
|
|
259
256
|
// When false, the drag handle with be just to the left of the element
|
|
@@ -293,15 +290,98 @@ export class SideMenuView<
|
|
|
293
290
|
// Shows or updates menu position whenever the cursor moves, if the menu isn't frozen.
|
|
294
291
|
document.body.addEventListener("mousemove", this.onMouseMove, true);
|
|
295
292
|
|
|
296
|
-
//
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
// Unfreezes the menu whenever the user clicks anywhere.
|
|
300
|
-
document.body.addEventListener("mousedown", this.onMouseDown, true);
|
|
293
|
+
// Unfreezes the menu whenever the user clicks.
|
|
294
|
+
this.pmView.dom.addEventListener("mousedown", this.onMouseDown);
|
|
301
295
|
// Hides and unfreezes the menu whenever the user presses a key.
|
|
302
296
|
document.body.addEventListener("keydown", this.onKeyDown, true);
|
|
297
|
+
|
|
298
|
+
// Setting capture=true ensures that any parent container of the editor that
|
|
299
|
+
// gets scrolled will trigger the scroll event. Scroll events do not bubble
|
|
300
|
+
// and so won't propagate to the document by default.
|
|
301
|
+
document.addEventListener("scroll", this.onScroll, true);
|
|
303
302
|
}
|
|
304
303
|
|
|
304
|
+
updateState = () => {
|
|
305
|
+
if (this.menuFrozen || !this.mousePos) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Editor itself may have padding or other styling which affects
|
|
310
|
+
// size/position, so we get the boundingRect of the first child (i.e. the
|
|
311
|
+
// blockGroup that wraps all blocks in the editor) for more accurate side
|
|
312
|
+
// menu placement.
|
|
313
|
+
const editorBoundingBox = (
|
|
314
|
+
this.pmView.dom.firstChild! as HTMLElement
|
|
315
|
+
).getBoundingClientRect();
|
|
316
|
+
|
|
317
|
+
this.horizontalPosAnchor = editorBoundingBox.x;
|
|
318
|
+
|
|
319
|
+
// Gets block at mouse cursor's vertical position.
|
|
320
|
+
const coords = {
|
|
321
|
+
left: editorBoundingBox.left + editorBoundingBox.width / 2, // take middle of editor
|
|
322
|
+
top: this.mousePos.y,
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const elements = document.elementsFromPoint(coords.left, coords.top);
|
|
326
|
+
let block = undefined;
|
|
327
|
+
|
|
328
|
+
for (const element of elements) {
|
|
329
|
+
if (this.pmView.dom.contains(element)) {
|
|
330
|
+
block = getDraggableBlockFromElement(element, this.pmView);
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Closes the menu if the mouse cursor is beyond the editor vertically.
|
|
336
|
+
if (!block || !this.editor.isEditable) {
|
|
337
|
+
if (this.state?.show) {
|
|
338
|
+
this.state.show = false;
|
|
339
|
+
this.needUpdate = true;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Doesn't update if the menu is already open and the mouse cursor is still hovering the same block.
|
|
346
|
+
if (
|
|
347
|
+
this.state?.show &&
|
|
348
|
+
this.hoveredBlock?.hasAttribute("data-id") &&
|
|
349
|
+
this.hoveredBlock?.getAttribute("data-id") === block.id
|
|
350
|
+
) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
this.hoveredBlock = block.node;
|
|
355
|
+
|
|
356
|
+
// Gets the block's content node, which lets to ignore child blocks when determining the block menu's position.
|
|
357
|
+
const blockContent = block.node.firstChild as HTMLElement;
|
|
358
|
+
|
|
359
|
+
if (!blockContent) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Shows or updates elements.
|
|
364
|
+
if (this.editor.isEditable) {
|
|
365
|
+
const blockContentBoundingBox = blockContent.getBoundingClientRect();
|
|
366
|
+
|
|
367
|
+
this.state = {
|
|
368
|
+
show: true,
|
|
369
|
+
referencePos: new DOMRect(
|
|
370
|
+
this.horizontalPosAnchoredAtRoot
|
|
371
|
+
? this.horizontalPosAnchor
|
|
372
|
+
: blockContentBoundingBox.x,
|
|
373
|
+
blockContentBoundingBox.y,
|
|
374
|
+
blockContentBoundingBox.width,
|
|
375
|
+
blockContentBoundingBox.height
|
|
376
|
+
),
|
|
377
|
+
block: this.editor.getBlock(
|
|
378
|
+
this.hoveredBlock!.getAttribute("data-id")!
|
|
379
|
+
)!,
|
|
380
|
+
};
|
|
381
|
+
this.needUpdate = true;
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
305
385
|
/**
|
|
306
386
|
* Sets isDragging when dragging text.
|
|
307
387
|
*/
|
|
@@ -381,34 +461,25 @@ export class SideMenuView<
|
|
|
381
461
|
}
|
|
382
462
|
};
|
|
383
463
|
|
|
384
|
-
onMouseDown = (
|
|
385
|
-
if (this.state &&
|
|
386
|
-
this.
|
|
464
|
+
onMouseDown = () => {
|
|
465
|
+
if (this.state && this.state.show && this.menuFrozen) {
|
|
466
|
+
this.menuFrozen = false;
|
|
467
|
+
this.state.show = false;
|
|
387
468
|
this.emitUpdate(this.state);
|
|
388
469
|
}
|
|
389
|
-
this.menuFrozen = false;
|
|
390
470
|
};
|
|
391
471
|
|
|
392
472
|
onMouseMove = (event: MouseEvent) => {
|
|
393
|
-
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
473
|
+
this.mousePos = { x: event.clientX, y: event.clientY };
|
|
396
474
|
|
|
397
|
-
// Editor itself may have padding or other styling which affects
|
|
398
|
-
// size/position, so we get the boundingRect of the first child (i.e. the
|
|
399
|
-
// blockGroup that wraps all blocks in the editor) for more accurate side
|
|
400
|
-
// menu placement.
|
|
401
|
-
const editorBoundingBox = (
|
|
402
|
-
this.pmView.dom.firstChild! as HTMLElement
|
|
403
|
-
).getBoundingClientRect();
|
|
404
475
|
// We want the full area of the editor to check if the cursor is hovering
|
|
405
476
|
// above it though.
|
|
406
477
|
const editorOuterBoundingBox = this.pmView.dom.getBoundingClientRect();
|
|
407
478
|
const cursorWithinEditor =
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
479
|
+
this.mousePos.x > editorOuterBoundingBox.left &&
|
|
480
|
+
this.mousePos.x < editorOuterBoundingBox.right &&
|
|
481
|
+
this.mousePos.y > editorOuterBoundingBox.top &&
|
|
482
|
+
this.mousePos.y < editorOuterBoundingBox.bottom;
|
|
412
483
|
|
|
413
484
|
const editorWrapper = this.pmView.dom.parentElement!;
|
|
414
485
|
|
|
@@ -434,63 +505,11 @@ export class SideMenuView<
|
|
|
434
505
|
return;
|
|
435
506
|
}
|
|
436
507
|
|
|
437
|
-
this.
|
|
438
|
-
|
|
439
|
-
// Gets block at mouse cursor's vertical position.
|
|
440
|
-
const coords = {
|
|
441
|
-
left: editorBoundingBox.left + editorBoundingBox.width / 2, // take middle of editor
|
|
442
|
-
top: event.clientY,
|
|
443
|
-
};
|
|
444
|
-
const block = getDraggableBlockFromCoords(coords, this.pmView);
|
|
445
|
-
|
|
446
|
-
// Closes the menu if the mouse cursor is beyond the editor vertically.
|
|
447
|
-
if (!block || !this.editor.isEditable) {
|
|
448
|
-
if (this.state?.show) {
|
|
449
|
-
this.state.show = false;
|
|
450
|
-
this.emitUpdate(this.state);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// Doesn't update if the menu is already open and the mouse cursor is still hovering the same block.
|
|
457
|
-
if (
|
|
458
|
-
this.state?.show &&
|
|
459
|
-
this.hoveredBlock?.hasAttribute("data-id") &&
|
|
460
|
-
this.hoveredBlock?.getAttribute("data-id") === block.id
|
|
461
|
-
) {
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
this.hoveredBlock = block.node;
|
|
466
|
-
|
|
467
|
-
// Gets the block's content node, which lets to ignore child blocks when determining the block menu's position.
|
|
468
|
-
const blockContent = block.node.firstChild as HTMLElement;
|
|
469
|
-
|
|
470
|
-
if (!blockContent) {
|
|
471
|
-
return;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// Shows or updates elements.
|
|
475
|
-
if (this.editor.isEditable) {
|
|
476
|
-
const blockContentBoundingBox = blockContent.getBoundingClientRect();
|
|
508
|
+
this.updateState();
|
|
477
509
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
this.horizontalPosAnchoredAtRoot
|
|
482
|
-
? this.horizontalPosAnchor
|
|
483
|
-
: blockContentBoundingBox.x,
|
|
484
|
-
blockContentBoundingBox.y,
|
|
485
|
-
blockContentBoundingBox.width,
|
|
486
|
-
blockContentBoundingBox.height
|
|
487
|
-
),
|
|
488
|
-
block: this.editor.getBlock(
|
|
489
|
-
this.hoveredBlock!.getAttribute("data-id")!
|
|
490
|
-
)!,
|
|
491
|
-
};
|
|
492
|
-
|
|
493
|
-
this.emitUpdate(this.state);
|
|
510
|
+
if (this.needUpdate) {
|
|
511
|
+
this.emitUpdate(this.state!);
|
|
512
|
+
this.needUpdate = false;
|
|
494
513
|
}
|
|
495
514
|
};
|
|
496
515
|
|
|
@@ -511,6 +530,24 @@ export class SideMenuView<
|
|
|
511
530
|
}
|
|
512
531
|
};
|
|
513
532
|
|
|
533
|
+
// Needed in cases where the editor state updates without the mouse cursor
|
|
534
|
+
// moving, as some state updates can require a side menu update. For example,
|
|
535
|
+
// adding a button to the side menu which removes the block can cause the
|
|
536
|
+
// block below to jump up into the place of the removed block when clicked,
|
|
537
|
+
// allowing the user to click the button again without moving the cursor. This
|
|
538
|
+
// would otherwise not update the side menu, and so clicking the button again
|
|
539
|
+
// would attempt to remove the same block again, causing an error.
|
|
540
|
+
update() {
|
|
541
|
+
const prevBlockId = this.state?.block.id;
|
|
542
|
+
|
|
543
|
+
this.updateState();
|
|
544
|
+
|
|
545
|
+
if (this.needUpdate && this.state && prevBlockId !== this.state.block.id) {
|
|
546
|
+
this.emitUpdate(this.state);
|
|
547
|
+
this.needUpdate = false;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
514
551
|
destroy() {
|
|
515
552
|
if (this.state?.show) {
|
|
516
553
|
this.state.show = false;
|
|
@@ -520,8 +557,8 @@ export class SideMenuView<
|
|
|
520
557
|
document.body.removeEventListener("dragover", this.onDragOver);
|
|
521
558
|
this.pmView.dom.removeEventListener("dragstart", this.onDragStart);
|
|
522
559
|
document.body.removeEventListener("drop", this.onDrop, true);
|
|
523
|
-
document.removeEventListener("scroll", this.onScroll);
|
|
524
|
-
|
|
560
|
+
document.removeEventListener("scroll", this.onScroll, true);
|
|
561
|
+
this.pmView.dom.removeEventListener("mousedown", this.onMouseDown);
|
|
525
562
|
document.body.removeEventListener("keydown", this.onKeyDown, true);
|
|
526
563
|
}
|
|
527
564
|
|
|
@@ -531,8 +568,6 @@ export class SideMenuView<
|
|
|
531
568
|
this.emitUpdate(this.state);
|
|
532
569
|
}
|
|
533
570
|
|
|
534
|
-
this.menuFrozen = true;
|
|
535
|
-
|
|
536
571
|
const blockContent = this.hoveredBlock!.firstChild! as HTMLElement;
|
|
537
572
|
const blockContentBoundingBox = blockContent.getBoundingClientRect();
|
|
538
573
|
|
|
@@ -642,5 +677,9 @@ export class SideMenuProsemirrorPlugin<
|
|
|
642
677
|
* attached to the same block regardless of which block is hovered by the
|
|
643
678
|
* mouse cursor.
|
|
644
679
|
*/
|
|
645
|
-
unfreezeMenu = () =>
|
|
680
|
+
unfreezeMenu = () => {
|
|
681
|
+
this.view!.menuFrozen = false;
|
|
682
|
+
this.view!.state!.show = false;
|
|
683
|
+
this.view!.emitUpdate(this.view!.state!);
|
|
684
|
+
};
|
|
646
685
|
}
|
|
@@ -37,7 +37,10 @@ class SuggestionMenuView<
|
|
|
37
37
|
emitUpdate(menuName, this.state);
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
// Setting capture=true ensures that any parent container of the editor that
|
|
41
|
+
// gets scrolled will trigger the scroll event. Scroll events do not bubble
|
|
42
|
+
// and so won't propagate to the document by default.
|
|
43
|
+
document.addEventListener("scroll", this.handleScroll, true);
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
handleScroll = () => {
|
|
@@ -92,7 +95,7 @@ class SuggestionMenuView<
|
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
destroy() {
|
|
95
|
-
document.removeEventListener("scroll", this.handleScroll);
|
|
98
|
+
document.removeEventListener("scroll", this.handleScroll, true);
|
|
96
99
|
}
|
|
97
100
|
|
|
98
101
|
closeMenu = () => {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "../../schema";
|
|
12
12
|
import { checkBlockIsDefaultType } from "../../blocks/defaultBlockTypeGuards";
|
|
13
13
|
import { EventEmitter } from "../../util/EventEmitter";
|
|
14
|
-
import {
|
|
14
|
+
import { getDraggableBlockFromElement } from "../SideMenu/SideMenuPlugin";
|
|
15
15
|
|
|
16
16
|
let dragImageElement: HTMLElement | undefined;
|
|
17
17
|
|
|
@@ -119,7 +119,10 @@ export class TableHandlesView<
|
|
|
119
119
|
document.addEventListener("dragover", this.dragOverHandler);
|
|
120
120
|
document.addEventListener("drop", this.dropHandler);
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
// Setting capture=true ensures that any parent container of the editor that
|
|
123
|
+
// gets scrolled will trigger the scroll event. Scroll events do not bubble
|
|
124
|
+
// and so won't propagate to the document by default.
|
|
125
|
+
document.addEventListener("scroll", this.scrollHandler, true);
|
|
123
126
|
}
|
|
124
127
|
|
|
125
128
|
mouseMoveHandler = (event: MouseEvent) => {
|
|
@@ -143,7 +146,7 @@ export class TableHandlesView<
|
|
|
143
146
|
const tableRect =
|
|
144
147
|
target.parentElement!.parentElement!.getBoundingClientRect();
|
|
145
148
|
|
|
146
|
-
const blockEl =
|
|
149
|
+
const blockEl = getDraggableBlockFromElement(target, this.pmView);
|
|
147
150
|
if (!blockEl) {
|
|
148
151
|
return;
|
|
149
152
|
}
|
|
@@ -361,7 +364,7 @@ export class TableHandlesView<
|
|
|
361
364
|
document.removeEventListener("dragover", this.dragOverHandler);
|
|
362
365
|
document.removeEventListener("drop", this.dropHandler);
|
|
363
366
|
|
|
364
|
-
document.removeEventListener("scroll", this.scrollHandler);
|
|
367
|
+
document.removeEventListener("scroll", this.scrollHandler, true);
|
|
365
368
|
}
|
|
366
369
|
}
|
|
367
370
|
|
package/src/i18n/locales/pt.ts
CHANGED
package/src/i18n/locales/zh.ts
CHANGED
|
@@ -220,7 +220,11 @@ export const BlockContainer = Node.create<{
|
|
|
220
220
|
if (block.content) {
|
|
221
221
|
if (typeof block.content === "string") {
|
|
222
222
|
// Adds a single text node with no marks to the content.
|
|
223
|
-
content =
|
|
223
|
+
content = inlineContentToNodes(
|
|
224
|
+
[block.content],
|
|
225
|
+
state.schema,
|
|
226
|
+
this.options.editor.schema.styleSchema
|
|
227
|
+
);
|
|
224
228
|
} else if (Array.isArray(block.content)) {
|
|
225
229
|
// Adds a text node with the provided styles converted into marks to the content,
|
|
226
230
|
// for each InlineContent object.
|
|
@@ -611,7 +615,7 @@ export const BlockContainer = Node.create<{
|
|
|
611
615
|
// of the block.
|
|
612
616
|
() =>
|
|
613
617
|
commands.command(({ state }) => {
|
|
614
|
-
const {
|
|
618
|
+
const { contentNode, depth } = getBlockInfoFromPos(
|
|
615
619
|
state.doc,
|
|
616
620
|
state.selection.from
|
|
617
621
|
)!;
|
|
@@ -620,7 +624,7 @@ export const BlockContainer = Node.create<{
|
|
|
620
624
|
state.selection.$anchor.parentOffset === 0;
|
|
621
625
|
const selectionEmpty =
|
|
622
626
|
state.selection.anchor === state.selection.head;
|
|
623
|
-
const blockEmpty =
|
|
627
|
+
const blockEmpty = contentNode.childCount === 0;
|
|
624
628
|
const blockIndented = depth > 2;
|
|
625
629
|
|
|
626
630
|
if (
|
|
@@ -638,7 +642,7 @@ export const BlockContainer = Node.create<{
|
|
|
638
642
|
// empty & at the start of the block.
|
|
639
643
|
() =>
|
|
640
644
|
commands.command(({ state, chain }) => {
|
|
641
|
-
const {
|
|
645
|
+
const { contentNode, endPos } = getBlockInfoFromPos(
|
|
642
646
|
state.doc,
|
|
643
647
|
state.selection.from
|
|
644
648
|
)!;
|
|
@@ -647,7 +651,7 @@ export const BlockContainer = Node.create<{
|
|
|
647
651
|
state.selection.$anchor.parentOffset === 0;
|
|
648
652
|
const selectionEmpty =
|
|
649
653
|
state.selection.anchor === state.selection.head;
|
|
650
|
-
const blockEmpty =
|
|
654
|
+
const blockEmpty = contentNode.childCount === 0;
|
|
651
655
|
|
|
652
656
|
if (selectionAtBlockStart && selectionEmpty && blockEmpty) {
|
|
653
657
|
const newBlockInsertionPos = endPos + 1;
|
|
@@ -667,14 +671,14 @@ export const BlockContainer = Node.create<{
|
|
|
667
671
|
// deletes the selection beforehand, if it's not empty.
|
|
668
672
|
() =>
|
|
669
673
|
commands.command(({ state, chain }) => {
|
|
670
|
-
const {
|
|
674
|
+
const { contentNode } = getBlockInfoFromPos(
|
|
671
675
|
state.doc,
|
|
672
676
|
state.selection.from
|
|
673
677
|
)!;
|
|
674
678
|
|
|
675
679
|
const selectionAtBlockStart =
|
|
676
680
|
state.selection.$anchor.parentOffset === 0;
|
|
677
|
-
const blockEmpty =
|
|
681
|
+
const blockEmpty = contentNode.childCount === 0;
|
|
678
682
|
|
|
679
683
|
if (!blockEmpty) {
|
|
680
684
|
chain()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TagParseRule } from "@tiptap/pm/model";
|
|
2
2
|
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
|
|
3
3
|
import { InlineContentSchema } from "../inlineContent/types";
|
|
4
4
|
import { StyleSchema } from "../styles/types";
|
|
@@ -68,7 +68,7 @@ export function getParseRules(
|
|
|
68
68
|
config: BlockConfig,
|
|
69
69
|
customParseFunction: CustomBlockImplementation<any, any, any>["parse"]
|
|
70
70
|
) {
|
|
71
|
-
const rules:
|
|
71
|
+
const rules: TagParseRule[] = [
|
|
72
72
|
{
|
|
73
73
|
tag: "[data-content-type=" + config.type + "]",
|
|
74
74
|
contentElement: "[data-editable]",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Node } from "@tiptap/core";
|
|
2
|
-
import {
|
|
2
|
+
import { TagParseRule } from "@tiptap/pm/model";
|
|
3
3
|
import { nodeToCustomInlineContent } from "../../api/nodeConversions/nodeConversions";
|
|
4
4
|
import { propsToAttributes } from "../blocks/internal";
|
|
5
5
|
import { Props } from "../propTypes";
|
|
@@ -46,7 +46,7 @@ export type CustomInlineContentImplementation<
|
|
|
46
46
|
|
|
47
47
|
export function getInlineContentParseRules(
|
|
48
48
|
config: CustomInlineContentConfig
|
|
49
|
-
):
|
|
49
|
+
): TagParseRule[] {
|
|
50
50
|
return [
|
|
51
51
|
{
|
|
52
52
|
tag: `[data-inline-content-type="${config.type}"]`,
|
|
@@ -98,7 +98,7 @@ export declare class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockS
|
|
|
98
98
|
readonly linkToolbar: LinkToolbarProsemirrorPlugin<BSchema, ISchema, SSchema>;
|
|
99
99
|
readonly sideMenu: SideMenuProsemirrorPlugin<BSchema, ISchema, SSchema>;
|
|
100
100
|
readonly suggestionMenus: SuggestionMenuProseMirrorPlugin<BSchema, ISchema, SSchema>;
|
|
101
|
-
readonly filePanel?: FilePanelProsemirrorPlugin<
|
|
101
|
+
readonly filePanel?: FilePanelProsemirrorPlugin<ISchema, SSchema>;
|
|
102
102
|
readonly tableHandles?: TableHandlesProsemirrorPlugin<ISchema, SSchema>;
|
|
103
103
|
readonly uploadFile: ((file: File) => Promise<string | Record<string, any>>) | undefined;
|
|
104
104
|
readonly resolveFileUrl: (url: string) => Promise<string>;
|
|
@@ -2,18 +2,18 @@ import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
|
|
|
2
2
|
import { EditorView } from "prosemirror-view";
|
|
3
3
|
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
|
|
4
4
|
import { UiElementPosition } from "../../extensions-shared/UiElementPosition";
|
|
5
|
-
import type { BlockFromConfig,
|
|
5
|
+
import type { BlockFromConfig, FileBlockConfig, InlineContentSchema, StyleSchema } from "../../schema";
|
|
6
6
|
import { EventEmitter } from "../../util/EventEmitter";
|
|
7
7
|
export type FilePanelState<I extends InlineContentSchema, S extends StyleSchema> = UiElementPosition & {
|
|
8
8
|
block: BlockFromConfig<FileBlockConfig, I, S>;
|
|
9
9
|
};
|
|
10
10
|
export declare class FilePanelView<I extends InlineContentSchema, S extends StyleSchema> implements PluginView {
|
|
11
|
+
private readonly editor;
|
|
11
12
|
private readonly pluginKey;
|
|
12
13
|
private readonly pmView;
|
|
13
14
|
state?: FilePanelState<I, S>;
|
|
14
15
|
emitUpdate: () => void;
|
|
15
|
-
|
|
16
|
-
constructor(pluginKey: PluginKey, pmView: EditorView, emitUpdate: (state: FilePanelState<I, S>) => void);
|
|
16
|
+
constructor(editor: BlockNoteEditor<Record<string, FileBlockConfig>, I, S>, pluginKey: PluginKey, pmView: EditorView, emitUpdate: (state: FilePanelState<I, S>) => void);
|
|
17
17
|
mouseDownHandler: () => void;
|
|
18
18
|
dragstartHandler: () => void;
|
|
19
19
|
scrollHandler: () => void;
|
|
@@ -21,10 +21,10 @@ export declare class FilePanelView<I extends InlineContentSchema, S extends Styl
|
|
|
21
21
|
closeMenu: () => void;
|
|
22
22
|
destroy(): void;
|
|
23
23
|
}
|
|
24
|
-
export declare class FilePanelProsemirrorPlugin<
|
|
24
|
+
export declare class FilePanelProsemirrorPlugin<I extends InlineContentSchema, S extends StyleSchema> extends EventEmitter<any> {
|
|
25
25
|
private view;
|
|
26
26
|
readonly plugin: Plugin;
|
|
27
|
-
constructor(
|
|
27
|
+
constructor(editor: BlockNoteEditor<Record<string, FileBlockConfig>, I, S>);
|
|
28
28
|
get shown(): boolean;
|
|
29
29
|
onUpdate(callback: (state: FilePanelState<I, S>) => void): () => void;
|
|
30
30
|
closeMenu: () => void;
|
|
@@ -9,24 +9,24 @@ import { EventEmitter } from "../../util/EventEmitter";
|
|
|
9
9
|
export type SideMenuState<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema> = UiElementPosition & {
|
|
10
10
|
block: Block<BSchema, I, S>;
|
|
11
11
|
};
|
|
12
|
-
export declare function
|
|
13
|
-
left: number;
|
|
14
|
-
top: number;
|
|
15
|
-
}, view: EditorView): {
|
|
12
|
+
export declare function getDraggableBlockFromElement(element: Element, view: EditorView): {
|
|
16
13
|
node: HTMLElement;
|
|
17
14
|
id: string;
|
|
18
15
|
} | undefined;
|
|
19
16
|
export declare class SideMenuView<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema> implements PluginView {
|
|
20
17
|
private readonly editor;
|
|
21
18
|
private readonly pmView;
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
state?: SideMenuState<BSchema, I, S>;
|
|
20
|
+
readonly emitUpdate: (state: SideMenuState<BSchema, I, S>) => void;
|
|
21
|
+
private needUpdate;
|
|
22
|
+
private mousePos;
|
|
24
23
|
private horizontalPosAnchoredAtRoot;
|
|
25
24
|
private horizontalPosAnchor;
|
|
26
25
|
private hoveredBlock;
|
|
27
26
|
isDragging: boolean;
|
|
28
27
|
menuFrozen: boolean;
|
|
29
28
|
constructor(editor: BlockNoteEditor<BSchema, I, S>, pmView: EditorView, emitUpdate: (state: SideMenuState<BSchema, I, S>) => void);
|
|
29
|
+
updateState: () => void;
|
|
30
30
|
/**
|
|
31
31
|
* Sets isDragging when dragging text.
|
|
32
32
|
*/
|
|
@@ -44,9 +44,10 @@ export declare class SideMenuView<BSchema extends BlockSchema, I extends InlineC
|
|
|
44
44
|
*/
|
|
45
45
|
onDragOver: (event: DragEvent) => void;
|
|
46
46
|
onKeyDown: (_event: KeyboardEvent) => void;
|
|
47
|
-
onMouseDown: (
|
|
47
|
+
onMouseDown: () => void;
|
|
48
48
|
onMouseMove: (event: MouseEvent) => void;
|
|
49
49
|
onScroll: () => void;
|
|
50
|
+
update(): void;
|
|
50
51
|
destroy(): void;
|
|
51
52
|
addBlock(): void;
|
|
52
53
|
}
|
|
@@ -84,5 +85,5 @@ export declare class SideMenuProsemirrorPlugin<BSchema extends BlockSchema, I ex
|
|
|
84
85
|
* attached to the same block regardless of which block is hovered by the
|
|
85
86
|
* mouse cursor.
|
|
86
87
|
*/
|
|
87
|
-
unfreezeMenu: () =>
|
|
88
|
+
unfreezeMenu: () => void;
|
|
88
89
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TagParseRule } from "@tiptap/pm/model";
|
|
2
2
|
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
|
|
3
3
|
import { InlineContentSchema } from "../inlineContent/types";
|
|
4
4
|
import { StyleSchema } from "../styles/types";
|
|
@@ -28,7 +28,7 @@ export type CustomBlockImplementation<T extends CustomBlockConfig, I extends Inl
|
|
|
28
28
|
};
|
|
29
29
|
parse?: (el: HTMLElement) => PartialBlockFromConfig<T, I, S>["props"] | undefined;
|
|
30
30
|
};
|
|
31
|
-
export declare function getParseRules(config: BlockConfig, customParseFunction: CustomBlockImplementation<any, any, any>["parse"]):
|
|
31
|
+
export declare function getParseRules(config: BlockConfig, customParseFunction: CustomBlockImplementation<any, any, any>["parse"]): TagParseRule[];
|
|
32
32
|
export declare function createBlockSpec<T extends CustomBlockConfig, I extends InlineContentSchema, S extends StyleSchema>(blockConfig: T, blockImplementation: CustomBlockImplementation<T, I, S>): {
|
|
33
33
|
config: T;
|
|
34
34
|
implementation: import("./types").TiptapBlockImplementation<T, any, InlineContentSchema, StyleSchema>;
|