@blocknote/core 0.24.1 → 0.25.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 +12 -0
- package/dist/blocknote.cjs.map +1 -0
- package/dist/blocknote.js +5028 -3444
- package/dist/blocknote.js.map +1 -1
- package/dist/comments.cjs +2 -0
- package/dist/comments.cjs.map +1 -0
- package/dist/comments.js +593 -0
- package/dist/comments.js.map +1 -0
- package/dist/style.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +39 -26
- package/src/api/blockManipulation/commands/insertBlocks/__snapshots__/insertBlocks.test.ts.snap +1022 -378
- package/src/api/blockManipulation/commands/mergeBlocks/__snapshots__/mergeBlocks.test.ts.snap +730 -270
- package/src/api/blockManipulation/commands/moveBlocks/__snapshots__/moveBlocks.test.ts.snap +3100 -1260
- package/src/api/blockManipulation/commands/removeBlocks/__snapshots__/removeBlocks.test.ts.snap +438 -162
- package/src/api/blockManipulation/commands/replaceBlocks/__snapshots__/replaceBlocks.test.ts.snap +1168 -432
- package/src/api/blockManipulation/commands/splitBlock/__snapshots__/splitBlock.test.ts.snap +930 -378
- package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +2485 -1015
- package/src/api/blockManipulation/commands/updateBlock/updateBlock.test.ts +28 -1
- package/src/api/blockManipulation/commands/updateBlock/updateBlock.ts +1 -1
- package/src/api/blockManipulation/selections/__snapshots__/selection.test.ts.snap +292 -108
- package/src/api/blockManipulation/setupTestEnv.ts +14 -1
- package/src/api/blockManipulation/tables/tables.test.ts +1987 -0
- package/src/api/blockManipulation/tables/tables.ts +887 -0
- package/src/api/clipboard/__snapshots__/external/pasteEndOfParagraph.html +66 -24
- package/src/api/clipboard/__snapshots__/external/pasteEndOfParagraphText.html +66 -24
- package/src/api/clipboard/__snapshots__/external/pasteImage.html +66 -24
- package/src/api/clipboard/__snapshots__/external/pasteParagraphInCustomBlock.html +66 -24
- package/src/api/clipboard/__snapshots__/external/pasteTable.html +132 -48
- package/src/api/clipboard/__snapshots__/external/pasteTableInExistingTable.html +136 -44
- package/src/api/clipboard/fromClipboard/handleFileInsertion.ts +36 -14
- package/src/api/clipboard/toClipboard/copyExtension.ts +2 -3
- package/src/api/exporters/html/__snapshots__/table/headerCols/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/table/headerCols/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/table/headerRows/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/table/headerRows/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/table/headersRows/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/table/headersRows/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/table/mixedCellColors/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/table/mixedCellColors/internal.html +1 -0
- package/src/api/exporters/html/__snapshots__/table/mixedRowspansAndColspans/external.html +1 -0
- package/src/api/exporters/html/__snapshots__/table/mixedRowspansAndColspans/internal.html +1 -0
- package/src/api/exporters/markdown/__snapshots__/table/headerCols/markdown.md +4 -0
- package/src/api/exporters/markdown/__snapshots__/table/headerRows/markdown.md +4 -0
- package/src/api/exporters/markdown/__snapshots__/table/mixedCellColors/markdown.md +5 -0
- package/src/api/exporters/markdown/__snapshots__/table/mixedRowspansAndColspans/markdown.md +5 -0
- package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +985 -20
- package/src/api/nodeConversions/blockToNode.ts +63 -20
- package/src/api/nodeConversions/nodeToBlock.ts +75 -13
- package/src/api/parsers/html/__snapshots__/parse-notion-html.json +145 -54
- package/src/api/testUtil/cases/defaultSchema.ts +782 -9
- package/src/api/testUtil/partialBlockTestUtil.ts +39 -4
- package/src/blocks/TableBlockContent/TableBlockContent.ts +11 -5
- package/src/blocks/defaultBlockTypeGuards.ts +8 -0
- package/src/comments/index.ts +9 -0
- package/src/comments/models/User.ts +8 -0
- package/src/comments/threadstore/DefaultThreadStoreAuth.ts +106 -0
- package/src/comments/threadstore/ThreadStore.ts +134 -0
- package/src/comments/threadstore/ThreadStoreAuth.ts +13 -0
- package/src/comments/threadstore/TipTapThreadStore.ts +292 -0
- package/src/comments/threadstore/yjs/RESTYjsThreadStore.ts +144 -0
- package/src/comments/threadstore/yjs/YjsThreadStore.test.ts +294 -0
- package/src/comments/threadstore/yjs/YjsThreadStore.ts +340 -0
- package/src/comments/threadstore/yjs/YjsThreadStoreBase.ts +48 -0
- package/src/comments/threadstore/yjs/yjsHelpers.ts +121 -0
- package/src/comments/types.ts +117 -0
- package/src/editor/Block.css +16 -8
- package/src/editor/BlockNoteEditor.ts +269 -92
- package/src/editor/BlockNoteExtensions.ts +24 -1
- package/src/editor/BlockNoteTipTapEditor.ts +5 -1
- package/src/editor/editor.css +17 -0
- package/src/extensions/BackgroundColor/BackgroundColorExtension.ts +1 -1
- package/src/extensions/Comments/CommentMark.ts +61 -0
- package/src/extensions/Comments/CommentsPlugin.ts +301 -0
- package/src/extensions/Comments/userstore/UserStore.ts +72 -0
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +16 -10
- package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +3 -3
- package/src/extensions/ShowSelection/ShowSelectionPlugin.ts +52 -0
- package/src/extensions/SideMenu/SideMenuPlugin.ts +22 -9
- package/src/extensions/TableHandles/TableHandlesPlugin.ts +409 -57
- package/src/extensions/TextAlignment/TextAlignmentExtension.ts +2 -0
- package/src/extensions/TextColor/TextColorExtension.ts +1 -1
- package/src/extensions/UniqueID/UniqueID.ts +8 -3
- package/src/i18n/locales/ar.ts +23 -0
- package/src/i18n/locales/de.ts +15 -0
- package/src/i18n/locales/en.ts +25 -1
- package/src/i18n/locales/es.ts +16 -1
- package/src/i18n/locales/fr.ts +23 -0
- package/src/i18n/locales/hr.ts +18 -0
- package/src/i18n/locales/index.ts +1 -0
- package/src/i18n/locales/is.ts +24 -1
- package/src/i18n/locales/it.ts +21 -0
- package/src/i18n/locales/ja.ts +23 -0
- package/src/i18n/locales/ko.ts +23 -0
- package/src/i18n/locales/nl.ts +23 -0
- package/src/i18n/locales/no.ts +346 -0
- package/src/i18n/locales/pl.ts +23 -0
- package/src/i18n/locales/pt.ts +23 -0
- package/src/i18n/locales/ru.ts +23 -0
- package/src/i18n/locales/uk.ts +23 -0
- package/src/i18n/locales/vi.ts +23 -0
- package/src/i18n/locales/zh.ts +23 -0
- package/src/index.ts +6 -4
- package/src/schema/blocks/types.ts +32 -2
- package/src/util/browser.ts +1 -1
- package/src/util/table.ts +107 -0
- package/types/src/api/blockManipulation/tables/tables.d.ts +343 -0
- package/types/src/api/blockManipulation/tables/tables.test.d.ts +1 -0
- package/types/src/api/clipboard/toClipboard/copyExtension.d.ts +1 -1
- package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +1 -2
- package/types/src/blocks/defaultBlockTypeGuards.d.ts +3 -0
- package/types/src/comments/index.d.ts +9 -0
- package/types/src/comments/models/User.d.ts +8 -0
- package/types/src/comments/threadstore/DefaultThreadStoreAuth.d.ts +47 -0
- package/types/src/comments/threadstore/ThreadStore.d.ts +121 -0
- package/types/src/comments/threadstore/ThreadStoreAuth.d.ts +12 -0
- package/types/src/comments/threadstore/TipTapThreadStore.d.ts +97 -0
- package/types/src/comments/threadstore/yjs/RESTYjsThreadStore.d.ts +83 -0
- package/types/src/comments/threadstore/yjs/YjsThreadStore.d.ts +79 -0
- package/types/src/comments/threadstore/yjs/YjsThreadStore.test.d.ts +1 -0
- package/types/src/comments/threadstore/yjs/YjsThreadStoreBase.d.ts +15 -0
- package/types/src/comments/threadstore/yjs/yjsHelpers.d.ts +13 -0
- package/types/src/comments/types.d.ts +109 -0
- package/types/src/editor/BlockNoteEditor.d.ts +146 -66
- package/types/src/editor/BlockNoteExtensions.d.ts +4 -0
- package/types/src/extensions/Collaboration/createCollaborationExtensions.d.ts +1 -1
- package/types/src/extensions/Comments/CommentMark.d.ts +2 -0
- package/types/src/extensions/Comments/CommentsPlugin.d.ts +49 -0
- package/types/src/extensions/Comments/userstore/UserStore.d.ts +31 -0
- package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +1 -1
- package/types/src/extensions/ShowSelection/ShowSelectionPlugin.d.ts +15 -0
- package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +1 -0
- package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +66 -1
- package/types/src/i18n/locales/de.d.ts +15 -0
- package/types/src/i18n/locales/en.d.ts +20 -0
- package/types/src/i18n/locales/es.d.ts +15 -0
- package/types/src/i18n/locales/hr.d.ts +18 -0
- package/types/src/i18n/locales/index.d.ts +1 -0
- package/types/src/i18n/locales/it.d.ts +21 -0
- package/types/src/i18n/locales/no.d.ts +2 -0
- package/types/src/index.d.ts +5 -4
- package/types/src/pm-nodes/BlockContainer.d.ts +2 -2
- package/types/src/pm-nodes/BlockGroup.d.ts +2 -2
- package/types/src/schema/blocks/types.d.ts +23 -2
- package/types/src/util/browser.d.ts +1 -1
- package/types/src/util/table.d.ts +12 -0
- package/dist/blocknote.umd.cjs +0 -11
- package/dist/blocknote.umd.cjs.map +0 -1
|
@@ -1,8 +1,35 @@
|
|
|
1
1
|
import { Plugin, PluginKey, PluginView } from "prosemirror-state";
|
|
2
|
+
import {
|
|
3
|
+
addColumnAfter,
|
|
4
|
+
addColumnBefore,
|
|
5
|
+
addRowAfter,
|
|
6
|
+
addRowBefore,
|
|
7
|
+
CellSelection,
|
|
8
|
+
deleteColumn,
|
|
9
|
+
deleteRow,
|
|
10
|
+
mergeCells,
|
|
11
|
+
splitCell,
|
|
12
|
+
} from "prosemirror-tables";
|
|
2
13
|
import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
|
|
14
|
+
import {
|
|
15
|
+
addRowsOrColumns,
|
|
16
|
+
areInSameColumn,
|
|
17
|
+
canColumnBeDraggedInto,
|
|
18
|
+
canRowBeDraggedInto,
|
|
19
|
+
cropEmptyRowsOrColumns,
|
|
20
|
+
getCellsAtColumnHandle,
|
|
21
|
+
getCellsAtRowHandle,
|
|
22
|
+
getDimensionsOfTable,
|
|
23
|
+
moveColumn,
|
|
24
|
+
moveRow,
|
|
25
|
+
RelativeCellIndices,
|
|
26
|
+
} from "../../api/blockManipulation/tables/tables.js";
|
|
3
27
|
import { nodeToBlock } from "../../api/nodeConversions/nodeToBlock.js";
|
|
4
28
|
import { getNodeById } from "../../api/nodeUtil.js";
|
|
5
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
checkBlockIsDefaultType,
|
|
31
|
+
isTableCellSelection,
|
|
32
|
+
} from "../../blocks/defaultBlockTypeGuards.js";
|
|
6
33
|
import { DefaultBlockSchema } from "../../blocks/defaultBlocks.js";
|
|
7
34
|
import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
|
|
8
35
|
import {
|
|
@@ -16,6 +43,7 @@ import { getDraggableBlockFromElement } from "../getDraggableBlockFromElement.js
|
|
|
16
43
|
|
|
17
44
|
let dragImageElement: HTMLElement | undefined;
|
|
18
45
|
|
|
46
|
+
// TODO consider switching this to jotai, it is a bit messy and noisy
|
|
19
47
|
export type TableHandlesState<
|
|
20
48
|
I extends InlineContentSchema,
|
|
21
49
|
S extends StyleSchema
|
|
@@ -159,7 +187,10 @@ export class TableHandlesView<
|
|
|
159
187
|
"dragover",
|
|
160
188
|
this.dragOverHandler as EventListener
|
|
161
189
|
);
|
|
162
|
-
pmView.root.addEventListener(
|
|
190
|
+
pmView.root.addEventListener(
|
|
191
|
+
"drop",
|
|
192
|
+
this.dropHandler as unknown as EventListener
|
|
193
|
+
);
|
|
163
194
|
}
|
|
164
195
|
|
|
165
196
|
viewMousedownHandler = () => {
|
|
@@ -284,11 +315,11 @@ export class TableHandlesView<
|
|
|
284
315
|
referencePosTable: tableRect,
|
|
285
316
|
block: tableBlock,
|
|
286
317
|
widgetContainer,
|
|
287
|
-
colIndex: hideHandles ? undefined : this.state
|
|
288
|
-
rowIndex: hideHandles ? undefined : this.state
|
|
318
|
+
colIndex: hideHandles ? undefined : this.state?.colIndex,
|
|
319
|
+
rowIndex: hideHandles ? undefined : this.state?.rowIndex,
|
|
289
320
|
referencePosCell: hideHandles
|
|
290
321
|
? undefined
|
|
291
|
-
: this.state
|
|
322
|
+
: this.state?.referencePosCell,
|
|
292
323
|
};
|
|
293
324
|
} else {
|
|
294
325
|
const colIndex = getChildIndex(target.domNode);
|
|
@@ -427,7 +458,7 @@ export class TableHandlesView<
|
|
|
427
458
|
dropHandler = (event: DragEvent) => {
|
|
428
459
|
this.mouseState = "up";
|
|
429
460
|
if (this.state === undefined || this.state.draggingState === undefined) {
|
|
430
|
-
return;
|
|
461
|
+
return false;
|
|
431
462
|
}
|
|
432
463
|
|
|
433
464
|
if (
|
|
@@ -443,33 +474,64 @@ export class TableHandlesView<
|
|
|
443
474
|
|
|
444
475
|
const { draggingState, colIndex, rowIndex } = this.state;
|
|
445
476
|
|
|
446
|
-
const
|
|
477
|
+
const columnWidths = this.state.block.content.columnWidths;
|
|
447
478
|
|
|
448
479
|
if (draggingState.draggedCellOrientation === "row") {
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
480
|
+
if (
|
|
481
|
+
!canRowBeDraggedInto(
|
|
482
|
+
this.state.block,
|
|
483
|
+
draggingState.originalIndex,
|
|
484
|
+
rowIndex
|
|
485
|
+
)
|
|
486
|
+
) {
|
|
487
|
+
// If the target row is invalid, don't move the row
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
const newTable = moveRow(
|
|
491
|
+
this.state.block,
|
|
492
|
+
draggingState.originalIndex,
|
|
493
|
+
rowIndex
|
|
494
|
+
);
|
|
495
|
+
this.editor.updateBlock(this.state.block, {
|
|
496
|
+
type: "table",
|
|
497
|
+
content: {
|
|
498
|
+
...this.state.block.content,
|
|
499
|
+
rows: newTable as any,
|
|
500
|
+
},
|
|
501
|
+
});
|
|
452
502
|
} else {
|
|
453
|
-
|
|
454
|
-
(
|
|
503
|
+
if (
|
|
504
|
+
!canColumnBeDraggedInto(
|
|
505
|
+
this.state.block,
|
|
506
|
+
draggingState.originalIndex,
|
|
507
|
+
colIndex
|
|
508
|
+
)
|
|
509
|
+
) {
|
|
510
|
+
// If the target column is invalid, don't move the column
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
const newTable = moveColumn(
|
|
514
|
+
this.state.block,
|
|
515
|
+
draggingState.originalIndex,
|
|
516
|
+
colIndex
|
|
455
517
|
);
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
518
|
+
const [columnWidth] = columnWidths.splice(draggingState.originalIndex, 1);
|
|
519
|
+
columnWidths.splice(colIndex, 0, columnWidth);
|
|
520
|
+
this.editor.updateBlock(this.state.block, {
|
|
521
|
+
type: "table",
|
|
522
|
+
content: {
|
|
523
|
+
...this.state.block.content,
|
|
524
|
+
columnWidths,
|
|
525
|
+
rows: newTable as any,
|
|
526
|
+
},
|
|
459
527
|
});
|
|
460
528
|
}
|
|
461
529
|
|
|
462
|
-
this.editor.updateBlock(this.state.block, {
|
|
463
|
-
type: "table",
|
|
464
|
-
content: {
|
|
465
|
-
type: "tableContent",
|
|
466
|
-
rows: rows,
|
|
467
|
-
},
|
|
468
|
-
});
|
|
469
|
-
|
|
470
530
|
// Have to reset text cursor position to the block as `updateBlock` moves
|
|
471
531
|
// the existing selection out of the block.
|
|
472
532
|
this.editor.setTextCursorPosition(this.state.block.id);
|
|
533
|
+
|
|
534
|
+
return true;
|
|
473
535
|
};
|
|
474
536
|
// Updates drag handles when the table is modified or removed.
|
|
475
537
|
update() {
|
|
@@ -488,8 +550,9 @@ export class TableHandlesView<
|
|
|
488
550
|
return;
|
|
489
551
|
}
|
|
490
552
|
|
|
491
|
-
const rowCount =
|
|
492
|
-
|
|
553
|
+
const { height: rowCount, width: colCount } = getDimensionsOfTable(
|
|
554
|
+
this.state.block
|
|
555
|
+
);
|
|
493
556
|
|
|
494
557
|
if (
|
|
495
558
|
this.state.rowIndex !== undefined &&
|
|
@@ -520,8 +583,12 @@ export class TableHandlesView<
|
|
|
520
583
|
) {
|
|
521
584
|
const row = tableBody.children[this.state.rowIndex];
|
|
522
585
|
const cell = row.children[this.state.colIndex];
|
|
523
|
-
|
|
524
|
-
|
|
586
|
+
if (cell) {
|
|
587
|
+
this.state.referencePosCell = cell.getBoundingClientRect();
|
|
588
|
+
} else {
|
|
589
|
+
this.state.rowIndex = undefined;
|
|
590
|
+
this.state.colIndex = undefined;
|
|
591
|
+
}
|
|
525
592
|
}
|
|
526
593
|
this.state.referencePosTable = tableBody.getBoundingClientRect();
|
|
527
594
|
|
|
@@ -538,7 +605,7 @@ export class TableHandlesView<
|
|
|
538
605
|
);
|
|
539
606
|
this.pmView.root.removeEventListener(
|
|
540
607
|
"drop",
|
|
541
|
-
this.dropHandler as EventListener
|
|
608
|
+
this.dropHandler as unknown as EventListener
|
|
542
609
|
);
|
|
543
610
|
}
|
|
544
611
|
}
|
|
@@ -594,35 +661,53 @@ export class TableHandlesProsemirrorPlugin<
|
|
|
594
661
|
|
|
595
662
|
if (newIndex === this.view.state.draggingState.originalIndex) {
|
|
596
663
|
return DecorationSet.create(state.doc, decorations);
|
|
664
|
+
} else if (
|
|
665
|
+
this.view.state.draggingState.draggedCellOrientation === "row" &&
|
|
666
|
+
!canRowBeDraggedInto(
|
|
667
|
+
this.view.state.block,
|
|
668
|
+
this.view.state.draggingState.originalIndex,
|
|
669
|
+
newIndex
|
|
670
|
+
)
|
|
671
|
+
) {
|
|
672
|
+
return DecorationSet.create(state.doc, decorations);
|
|
673
|
+
} else if (
|
|
674
|
+
this.view.state.draggingState.draggedCellOrientation === "col" &&
|
|
675
|
+
!canColumnBeDraggedInto(
|
|
676
|
+
this.view.state.block,
|
|
677
|
+
this.view.state.draggingState.originalIndex,
|
|
678
|
+
newIndex
|
|
679
|
+
)
|
|
680
|
+
) {
|
|
681
|
+
return DecorationSet.create(state.doc, decorations);
|
|
597
682
|
}
|
|
598
683
|
|
|
599
684
|
// Gets the table to show the drop cursor in.
|
|
600
685
|
const tableResolvedPos = state.doc.resolve(this.view.tablePos + 1);
|
|
601
|
-
const
|
|
686
|
+
const originalIndex = this.view.state.draggingState.originalIndex;
|
|
602
687
|
|
|
603
688
|
if (this.view.state.draggingState.draggedCellOrientation === "row") {
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
689
|
+
const cellsInRow = getCellsAtRowHandle(
|
|
690
|
+
this.view.state.block,
|
|
691
|
+
newIndex
|
|
607
692
|
);
|
|
608
|
-
const rowNode = rowResolvedPos.node();
|
|
609
693
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
694
|
+
cellsInRow.forEach(({ row, col }) => {
|
|
695
|
+
// Gets each row in the table.
|
|
696
|
+
const rowResolvedPos = state.doc.resolve(
|
|
697
|
+
tableResolvedPos.posAtIndex(row) + 1
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
// Gets the cell within the row.
|
|
613
701
|
const cellResolvedPos = state.doc.resolve(
|
|
614
|
-
rowResolvedPos.posAtIndex(
|
|
702
|
+
rowResolvedPos.posAtIndex(col) + 1
|
|
615
703
|
);
|
|
616
704
|
const cellNode = cellResolvedPos.node();
|
|
617
|
-
|
|
618
705
|
// Creates a decoration at the start or end of each cell,
|
|
619
706
|
// depending on whether the new index is before or after the
|
|
620
707
|
// original index.
|
|
621
708
|
const decorationPos =
|
|
622
709
|
cellResolvedPos.pos +
|
|
623
|
-
(newIndex >
|
|
624
|
-
? cellNode.nodeSize - 2
|
|
625
|
-
: 0);
|
|
710
|
+
(newIndex > originalIndex ? cellNode.nodeSize - 2 : 0);
|
|
626
711
|
decorations.push(
|
|
627
712
|
// The widget is a small bar which spans the width of the cell.
|
|
628
713
|
Decoration.widget(decorationPos, () => {
|
|
@@ -635,9 +720,7 @@ export class TableHandlesProsemirrorPlugin<
|
|
|
635
720
|
// table cells is an odd number of pixels. So this makes the
|
|
636
721
|
// positioning slightly more consistent regardless of where
|
|
637
722
|
// the row is being dropped.
|
|
638
|
-
if (
|
|
639
|
-
newIndex > this.view!.state!.draggingState!.originalIndex
|
|
640
|
-
) {
|
|
723
|
+
if (newIndex > originalIndex) {
|
|
641
724
|
widget.style.bottom = "-2px";
|
|
642
725
|
} else {
|
|
643
726
|
widget.style.top = "-3px";
|
|
@@ -647,18 +730,22 @@ export class TableHandlesProsemirrorPlugin<
|
|
|
647
730
|
return widget;
|
|
648
731
|
})
|
|
649
732
|
);
|
|
650
|
-
}
|
|
733
|
+
});
|
|
651
734
|
} else {
|
|
652
|
-
|
|
653
|
-
|
|
735
|
+
const cellsInColumn = getCellsAtColumnHandle(
|
|
736
|
+
this.view.state.block,
|
|
737
|
+
newIndex
|
|
738
|
+
);
|
|
739
|
+
|
|
740
|
+
cellsInColumn.forEach(({ row, col }) => {
|
|
654
741
|
// Gets each row in the table.
|
|
655
742
|
const rowResolvedPos = state.doc.resolve(
|
|
656
|
-
tableResolvedPos.posAtIndex(
|
|
743
|
+
tableResolvedPos.posAtIndex(row) + 1
|
|
657
744
|
);
|
|
658
745
|
|
|
659
|
-
// Gets the cell
|
|
746
|
+
// Gets the cell within the row.
|
|
660
747
|
const cellResolvedPos = state.doc.resolve(
|
|
661
|
-
rowResolvedPos.posAtIndex(
|
|
748
|
+
rowResolvedPos.posAtIndex(col) + 1
|
|
662
749
|
);
|
|
663
750
|
const cellNode = cellResolvedPos.node();
|
|
664
751
|
|
|
@@ -667,9 +754,7 @@ export class TableHandlesProsemirrorPlugin<
|
|
|
667
754
|
// original index.
|
|
668
755
|
const decorationPos =
|
|
669
756
|
cellResolvedPos.pos +
|
|
670
|
-
(newIndex >
|
|
671
|
-
? cellNode.nodeSize - 2
|
|
672
|
-
: 0);
|
|
757
|
+
(newIndex > originalIndex ? cellNode.nodeSize - 2 : 0);
|
|
673
758
|
|
|
674
759
|
decorations.push(
|
|
675
760
|
// The widget is a small bar which spans the height of the cell.
|
|
@@ -683,9 +768,7 @@ export class TableHandlesProsemirrorPlugin<
|
|
|
683
768
|
// table cells is an odd number of pixels. So this makes the
|
|
684
769
|
// positioning slightly more consistent regardless of where
|
|
685
770
|
// the column is being dropped.
|
|
686
|
-
if (
|
|
687
|
-
newIndex > this.view!.state!.draggingState!.originalIndex
|
|
688
|
-
) {
|
|
771
|
+
if (newIndex > originalIndex) {
|
|
689
772
|
widget.style.right = "-2px";
|
|
690
773
|
} else {
|
|
691
774
|
widget.style.left = "-3px";
|
|
@@ -695,7 +778,7 @@ export class TableHandlesProsemirrorPlugin<
|
|
|
695
778
|
return widget;
|
|
696
779
|
})
|
|
697
780
|
);
|
|
698
|
-
}
|
|
781
|
+
});
|
|
699
782
|
}
|
|
700
783
|
|
|
701
784
|
return DecorationSet.create(state.doc, decorations);
|
|
@@ -834,4 +917,273 @@ export class TableHandlesProsemirrorPlugin<
|
|
|
834
917
|
unfreezeHandles = () => {
|
|
835
918
|
this.view!.menuFrozen = false;
|
|
836
919
|
};
|
|
920
|
+
|
|
921
|
+
getCellsAtRowHandle = (
|
|
922
|
+
block: BlockFromConfigNoChildren<DefaultBlockSchema["table"], any, any>,
|
|
923
|
+
relativeRowIndex: RelativeCellIndices["row"]
|
|
924
|
+
) => {
|
|
925
|
+
return getCellsAtRowHandle(block, relativeRowIndex);
|
|
926
|
+
};
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Get all the cells in a column of the table block.
|
|
930
|
+
*/
|
|
931
|
+
getCellsAtColumnHandle = (
|
|
932
|
+
block: BlockFromConfigNoChildren<DefaultBlockSchema["table"], any, any>,
|
|
933
|
+
relativeColumnIndex: RelativeCellIndices["col"]
|
|
934
|
+
) => {
|
|
935
|
+
return getCellsAtColumnHandle(block, relativeColumnIndex);
|
|
936
|
+
};
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* Sets the selection to the given cell or a range of cells.
|
|
940
|
+
* @returns The new state after the selection has been set.
|
|
941
|
+
*/
|
|
942
|
+
private setCellSelection = (
|
|
943
|
+
relativeStartCell: RelativeCellIndices,
|
|
944
|
+
relativeEndCell: RelativeCellIndices = relativeStartCell
|
|
945
|
+
) => {
|
|
946
|
+
const view = this.view;
|
|
947
|
+
|
|
948
|
+
if (!view) {
|
|
949
|
+
throw new Error("Table handles view not initialized");
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
const state = this.editor.prosemirrorState;
|
|
953
|
+
const tableResolvedPos = state.doc.resolve(view.tablePos! + 1);
|
|
954
|
+
const startRowResolvedPos = state.doc.resolve(
|
|
955
|
+
tableResolvedPos.posAtIndex(relativeStartCell.row) + 1
|
|
956
|
+
);
|
|
957
|
+
const startCellResolvedPos = state.doc.resolve(
|
|
958
|
+
// No need for +1, since CellSelection expects the position before the cell
|
|
959
|
+
startRowResolvedPos.posAtIndex(relativeStartCell.col)
|
|
960
|
+
);
|
|
961
|
+
const endRowResolvedPos = state.doc.resolve(
|
|
962
|
+
tableResolvedPos.posAtIndex(relativeEndCell.row) + 1
|
|
963
|
+
);
|
|
964
|
+
const endCellResolvedPos = state.doc.resolve(
|
|
965
|
+
// No need for +1, since CellSelection expects the position before the cell
|
|
966
|
+
endRowResolvedPos.posAtIndex(relativeEndCell.col)
|
|
967
|
+
);
|
|
968
|
+
|
|
969
|
+
// Begin a new transaction to set the selection
|
|
970
|
+
const tr = state.tr;
|
|
971
|
+
|
|
972
|
+
// Set the selection to the given cell or a range of cells
|
|
973
|
+
tr.setSelection(
|
|
974
|
+
new CellSelection(startCellResolvedPos, endCellResolvedPos)
|
|
975
|
+
);
|
|
976
|
+
|
|
977
|
+
// Quickly apply the transaction to get the new state to update the selection before splitting the cell
|
|
978
|
+
return state.apply(tr);
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
/**
|
|
982
|
+
* Adds a row or column to the table using prosemirror-table commands
|
|
983
|
+
*/
|
|
984
|
+
addRowOrColumn = (
|
|
985
|
+
index: RelativeCellIndices["row"] | RelativeCellIndices["col"],
|
|
986
|
+
direction:
|
|
987
|
+
| { orientation: "row"; side: "above" | "below" }
|
|
988
|
+
| { orientation: "column"; side: "left" | "right" }
|
|
989
|
+
) => {
|
|
990
|
+
const state = this.setCellSelection(
|
|
991
|
+
direction.orientation === "row"
|
|
992
|
+
? { row: index, col: 0 }
|
|
993
|
+
: { row: 0, col: index }
|
|
994
|
+
);
|
|
995
|
+
if (direction.orientation === "row") {
|
|
996
|
+
if (direction.side === "above") {
|
|
997
|
+
return addRowBefore(state, this.editor.dispatch);
|
|
998
|
+
} else {
|
|
999
|
+
return addRowAfter(state, this.editor.dispatch);
|
|
1000
|
+
}
|
|
1001
|
+
} else {
|
|
1002
|
+
if (direction.side === "left") {
|
|
1003
|
+
return addColumnBefore(state, this.editor.dispatch);
|
|
1004
|
+
} else {
|
|
1005
|
+
return addColumnAfter(state, this.editor.dispatch);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
|
|
1010
|
+
/**
|
|
1011
|
+
* Removes a row or column from the table using prosemirror-table commands
|
|
1012
|
+
*/
|
|
1013
|
+
removeRowOrColumn = (
|
|
1014
|
+
index: RelativeCellIndices["row"] | RelativeCellIndices["col"],
|
|
1015
|
+
direction: "row" | "column"
|
|
1016
|
+
) => {
|
|
1017
|
+
const state = this.setCellSelection(
|
|
1018
|
+
direction === "row" ? { row: index, col: 0 } : { row: 0, col: index }
|
|
1019
|
+
);
|
|
1020
|
+
|
|
1021
|
+
if (direction === "row") {
|
|
1022
|
+
return deleteRow(state, this.editor.dispatch);
|
|
1023
|
+
} else {
|
|
1024
|
+
return deleteColumn(state, this.editor.dispatch);
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* Merges the cells in the table block.
|
|
1030
|
+
*/
|
|
1031
|
+
mergeCells = (cellsToMerge?: {
|
|
1032
|
+
relativeStartCell: RelativeCellIndices;
|
|
1033
|
+
relativeEndCell: RelativeCellIndices;
|
|
1034
|
+
}) => {
|
|
1035
|
+
const state = cellsToMerge
|
|
1036
|
+
? this.setCellSelection(
|
|
1037
|
+
cellsToMerge.relativeStartCell,
|
|
1038
|
+
cellsToMerge.relativeEndCell
|
|
1039
|
+
)
|
|
1040
|
+
: this.editor.prosemirrorState;
|
|
1041
|
+
|
|
1042
|
+
return mergeCells(state, this.editor.dispatch);
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
* Splits the cell in the table block.
|
|
1047
|
+
* If no cell is provided, the current cell selected will be split.
|
|
1048
|
+
*/
|
|
1049
|
+
splitCell = (relativeCellToSplit?: RelativeCellIndices) => {
|
|
1050
|
+
const state = relativeCellToSplit
|
|
1051
|
+
? this.setCellSelection(relativeCellToSplit)
|
|
1052
|
+
: this.editor.prosemirrorState;
|
|
1053
|
+
|
|
1054
|
+
return splitCell(state, this.editor.dispatch);
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* Gets the start and end cells of the current cell selection.
|
|
1059
|
+
* @returns The start and end cells of the current cell selection.
|
|
1060
|
+
*/
|
|
1061
|
+
getCellSelection = ():
|
|
1062
|
+
| undefined
|
|
1063
|
+
| {
|
|
1064
|
+
from: RelativeCellIndices;
|
|
1065
|
+
to: RelativeCellIndices;
|
|
1066
|
+
/**
|
|
1067
|
+
* All of the cells that are within the selected range.
|
|
1068
|
+
*/
|
|
1069
|
+
cells: RelativeCellIndices[];
|
|
1070
|
+
} => {
|
|
1071
|
+
// Based on the current selection, find the table cells that are within the selected range
|
|
1072
|
+
const state = this.editor.prosemirrorState;
|
|
1073
|
+
const selection = state.selection;
|
|
1074
|
+
|
|
1075
|
+
let $fromCell = selection.$from;
|
|
1076
|
+
let $toCell = selection.$to;
|
|
1077
|
+
if (isTableCellSelection(selection)) {
|
|
1078
|
+
// When the selection is a table cell selection, we can find the
|
|
1079
|
+
// from and to cells by iterating over the ranges in the selection
|
|
1080
|
+
const { ranges } = selection;
|
|
1081
|
+
ranges.forEach((range) => {
|
|
1082
|
+
$fromCell = range.$from.min($fromCell ?? range.$from);
|
|
1083
|
+
$toCell = range.$to.max($toCell ?? range.$to);
|
|
1084
|
+
});
|
|
1085
|
+
} else {
|
|
1086
|
+
// When the selection is a normal text selection
|
|
1087
|
+
// Assumes we are within a tableParagraph
|
|
1088
|
+
// And find the from and to cells by resolving the positions
|
|
1089
|
+
$fromCell = state.doc.resolve(
|
|
1090
|
+
selection.$from.pos - selection.$from.parentOffset - 1
|
|
1091
|
+
);
|
|
1092
|
+
$toCell = state.doc.resolve(
|
|
1093
|
+
selection.$to.pos - selection.$to.parentOffset - 1
|
|
1094
|
+
);
|
|
1095
|
+
|
|
1096
|
+
// Opt-out when the selection is not pointing into cells
|
|
1097
|
+
if ($fromCell.pos === 0 || $toCell.pos === 0) {
|
|
1098
|
+
return undefined;
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// Find the row and table that the from and to cells are in
|
|
1103
|
+
const $fromRow = state.doc.resolve(
|
|
1104
|
+
$fromCell.pos - $fromCell.parentOffset - 1
|
|
1105
|
+
);
|
|
1106
|
+
const $toRow = state.doc.resolve($toCell.pos - $toCell.parentOffset - 1);
|
|
1107
|
+
|
|
1108
|
+
// Find the table
|
|
1109
|
+
const $table = state.doc.resolve($fromRow.pos - $fromRow.parentOffset - 1);
|
|
1110
|
+
|
|
1111
|
+
// Find the column and row indices of the from and to cells
|
|
1112
|
+
const fromColIndex = $fromCell.index($fromRow.depth);
|
|
1113
|
+
const fromRowIndex = $fromRow.index($table.depth);
|
|
1114
|
+
const toColIndex = $toCell.index($toRow.depth);
|
|
1115
|
+
const toRowIndex = $toRow.index($table.depth);
|
|
1116
|
+
|
|
1117
|
+
const cells: RelativeCellIndices[] = [];
|
|
1118
|
+
for (let row = fromRowIndex; row <= toRowIndex; row++) {
|
|
1119
|
+
for (let col = fromColIndex; col <= toColIndex; col++) {
|
|
1120
|
+
cells.push({ row, col });
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
return {
|
|
1125
|
+
from: {
|
|
1126
|
+
row: fromRowIndex,
|
|
1127
|
+
col: fromColIndex,
|
|
1128
|
+
},
|
|
1129
|
+
to: {
|
|
1130
|
+
row: toRowIndex,
|
|
1131
|
+
col: toColIndex,
|
|
1132
|
+
},
|
|
1133
|
+
cells,
|
|
1134
|
+
};
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Gets the direction of the merge based on the current cell selection.
|
|
1139
|
+
*
|
|
1140
|
+
* Returns undefined when there is no cell selection, or the selection is not within a table.
|
|
1141
|
+
*/
|
|
1142
|
+
getMergeDirection = (
|
|
1143
|
+
block:
|
|
1144
|
+
| BlockFromConfigNoChildren<DefaultBlockSchema["table"], any, any>
|
|
1145
|
+
| undefined
|
|
1146
|
+
) => {
|
|
1147
|
+
const isSelectingTableCells = isTableCellSelection(
|
|
1148
|
+
this.editor.prosemirrorState.selection
|
|
1149
|
+
)
|
|
1150
|
+
? this.editor.prosemirrorState.selection
|
|
1151
|
+
: undefined;
|
|
1152
|
+
|
|
1153
|
+
if (
|
|
1154
|
+
!isSelectingTableCells ||
|
|
1155
|
+
!block ||
|
|
1156
|
+
// Only offer the merge button if there is more than one cell selected.
|
|
1157
|
+
isSelectingTableCells.ranges.length <= 1
|
|
1158
|
+
) {
|
|
1159
|
+
return undefined;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
const cellSelection = this.getCellSelection();
|
|
1163
|
+
|
|
1164
|
+
if (!cellSelection) {
|
|
1165
|
+
return undefined;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
if (areInSameColumn(cellSelection.from, cellSelection.to, block)) {
|
|
1169
|
+
return "vertical";
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
return "horizontal";
|
|
1173
|
+
};
|
|
1174
|
+
|
|
1175
|
+
cropEmptyRowsOrColumns = (
|
|
1176
|
+
block: BlockFromConfigNoChildren<DefaultBlockSchema["table"], any, any>,
|
|
1177
|
+
removeEmpty: "columns" | "rows"
|
|
1178
|
+
) => {
|
|
1179
|
+
return cropEmptyRowsOrColumns(block, removeEmpty);
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1182
|
+
addRowsOrColumns = (
|
|
1183
|
+
block: BlockFromConfigNoChildren<DefaultBlockSchema["table"], any, any>,
|
|
1184
|
+
addType: "columns" | "rows",
|
|
1185
|
+
numToAdd: number
|
|
1186
|
+
) => {
|
|
1187
|
+
return addRowsOrColumns(block, addType, numToAdd);
|
|
1188
|
+
};
|
|
837
1189
|
}
|
|
@@ -252,8 +252,9 @@ const UniqueID = Extension.create({
|
|
|
252
252
|
};
|
|
253
253
|
},
|
|
254
254
|
props: {
|
|
255
|
-
// `handleDOMEvents` is called before `transformPasted`
|
|
256
|
-
//
|
|
255
|
+
// `handleDOMEvents` is called before `transformPasted` so we can do
|
|
256
|
+
// some checks before. However, `transformPasted` only runs when
|
|
257
|
+
// editor content is pasted - not external content.
|
|
257
258
|
handleDOMEvents: {
|
|
258
259
|
// only create new ids for dropped content while holding `alt`
|
|
259
260
|
// or content is dragged from another editor
|
|
@@ -265,9 +266,13 @@ const UniqueID = Extension.create({
|
|
|
265
266
|
? void 0
|
|
266
267
|
: _a.effectAllowed) === "copy"
|
|
267
268
|
) {
|
|
268
|
-
dragSourceElement = null;
|
|
269
269
|
transformPasted = true;
|
|
270
|
+
} else {
|
|
271
|
+
transformPasted = false;
|
|
270
272
|
}
|
|
273
|
+
|
|
274
|
+
dragSourceElement = null;
|
|
275
|
+
|
|
271
276
|
return false;
|
|
272
277
|
},
|
|
273
278
|
// always create new ids on pasted content
|