@blocknote/core 0.2.2 → 0.2.4-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/blocknote.js +1061 -936
  2. package/dist/blocknote.js.map +1 -1
  3. package/dist/blocknote.umd.cjs +1 -1
  4. package/dist/blocknote.umd.cjs.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/package.json +22 -29
  7. package/src/BlockNoteExtensions.ts +11 -10
  8. package/src/extensions/BackgroundColor/BackgroundColorExtension.ts +61 -0
  9. package/src/extensions/BackgroundColor/BackgroundColorMark.ts +62 -0
  10. package/src/extensions/Blocks/PreviousBlockTypePlugin.ts +112 -106
  11. package/src/extensions/Blocks/apiTypes.ts +48 -0
  12. package/src/extensions/Blocks/helpers/findBlock.ts +3 -1
  13. package/src/extensions/Blocks/helpers/getBlockInfoFromPos.ts +1 -1
  14. package/src/extensions/Blocks/index.ts +10 -8
  15. package/src/extensions/Blocks/nodes/Block.module.css +122 -35
  16. package/src/extensions/Blocks/{BlockAttributes.ts → nodes/BlockAttributes.ts} +0 -0
  17. package/src/extensions/Blocks/nodes/{Block.ts → BlockContainer.ts} +113 -119
  18. package/src/extensions/Blocks/nodes/{BlockTypes/HeadingBlock/HeadingContent.ts → BlockContent/HeadingBlockContent/HeadingBlockContent.ts} +16 -24
  19. package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +76 -0
  20. package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/ListItemKeyboardShortcuts.ts +47 -0
  21. package/src/extensions/Blocks/nodes/{BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.ts → BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts} +10 -14
  22. package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +95 -0
  23. package/src/extensions/Blocks/nodes/{BlockTypes/TextBlock/TextContent.ts → BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts} +7 -12
  24. package/src/extensions/Blocks/nodes/BlockGroup.ts +4 -4
  25. package/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.ts +9 -1
  26. package/src/extensions/DraggableBlocks/DraggableBlocksPlugin.ts +87 -42
  27. package/src/extensions/{Blocks → DraggableBlocks}/MultipleNodeSelection.ts +0 -0
  28. package/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.ts +20 -7
  29. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +51 -12
  30. package/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.ts +1 -1
  31. package/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.ts +3 -1
  32. package/src/extensions/Placeholder/PlaceholderExtension.ts +1 -1
  33. package/src/extensions/SlashMenu/SlashMenuExtension.ts +1 -1
  34. package/src/extensions/SlashMenu/SlashMenuItem.ts +3 -28
  35. package/src/extensions/SlashMenu/defaultCommands.tsx +36 -55
  36. package/src/extensions/SlashMenu/index.ts +1 -6
  37. package/src/extensions/TextAlignment/TextAlignmentExtension.ts +75 -0
  38. package/src/extensions/TextColor/TextColorExtension.ts +54 -0
  39. package/src/extensions/TextColor/TextColorMark.ts +62 -0
  40. package/src/extensions/TrailingNode/TrailingNodeExtension.ts +4 -4
  41. package/src/extensions/UniqueID/UniqueID.ts +6 -0
  42. package/src/index.ts +2 -1
  43. package/src/shared/EditorElement.ts +12 -6
  44. package/src/shared/plugins/suggestion/SuggestionItem.ts +0 -9
  45. package/src/shared/plugins/suggestion/SuggestionPlugin.ts +191 -228
  46. package/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.ts +2 -2
  47. package/types/src/BlockNoteEditor.d.ts +1 -1
  48. package/types/src/BlockNoteExtensions.d.ts +1 -3
  49. package/types/src/api/Document.d.ts +5 -0
  50. package/types/src/extensions/BackgroundColor/BackgroundColorExtension.d.ts +9 -0
  51. package/types/src/extensions/BackgroundColor/BackgroundColorMark.d.ts +9 -0
  52. package/types/src/extensions/Blocks/PreviousBlockTypePlugin.d.ts +3 -2
  53. package/types/src/extensions/Blocks/apiTypes.d.ts +16 -0
  54. package/types/src/extensions/Blocks/helpers/getBlockInfoFromPos.d.ts +1 -1
  55. package/types/src/extensions/Blocks/nodes/BlockAttributes.d.ts +2 -0
  56. package/types/src/extensions/Blocks/nodes/BlockContainer.d.ts +21 -0
  57. package/types/src/extensions/Blocks/nodes/BlockContent/BlockContentTypes.d.ts +4 -0
  58. package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContent.d.ts +2 -0
  59. package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContentTypes.d.ts +4 -0
  60. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +2 -0
  61. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContentTypes.d.ts +2 -0
  62. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/ListItemKeyboardShortcuts.d.ts +2 -0
  63. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.d.ts +2 -0
  64. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +2 -0
  65. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContentTypes.d.ts +2 -0
  66. package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.d.ts +2 -0
  67. package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContentTypes.d.ts +2 -0
  68. package/types/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.d.ts +9 -5
  69. package/types/src/extensions/DraggableBlocks/DraggableBlocksExtension.d.ts +1 -1
  70. package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +6 -11
  71. package/types/src/extensions/DraggableBlocks/MultipleNodeSelection.d.ts +24 -0
  72. package/types/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.d.ts +18 -8
  73. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +1 -1
  74. package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.d.ts +5 -5
  75. package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.d.ts +2 -2
  76. package/types/src/extensions/SlashMenu/SlashMenuExtension.d.ts +1 -1
  77. package/types/src/extensions/SlashMenu/SlashMenuItem.d.ts +2 -19
  78. package/types/src/extensions/SlashMenu/defaultSlashCommands.d.ts +5 -0
  79. package/types/src/extensions/SlashMenu/index.d.ts +1 -2
  80. package/types/src/extensions/TextAlignment/TextAlignmentExtension.d.ts +9 -0
  81. package/types/src/extensions/TextColor/TextColorExtension.d.ts +9 -0
  82. package/types/src/extensions/TextColor/TextColorMark.d.ts +9 -0
  83. package/types/src/index.d.ts +2 -1
  84. package/types/src/shared/EditorElement.d.ts +6 -2
  85. package/types/src/shared/plugins/suggestion/SuggestionItem.d.ts +0 -6
  86. package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +11 -25
  87. package/types/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.d.ts +6 -6
  88. package/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.ts +0 -177
  89. package/src/extensions/Paragraph/FixedParagraph.ts +0 -12
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "homepage": "https://github.com/yousefed/blocknote",
4
4
  "private": false,
5
5
  "license": "MPL-2.0",
6
- "version": "0.2.2",
6
+ "version": "0.2.4-alpha.7",
7
7
  "files": [
8
8
  "dist",
9
9
  "types",
@@ -49,35 +49,28 @@
49
49
  "@emotion/cache": "^11.10.5",
50
50
  "@emotion/serialize": "^1.1.1",
51
51
  "@emotion/utils": "^1.2.0",
52
- "@tiptap/core": "2.0.0-beta.209",
53
- "@tiptap/extension-bold": "2.0.0-beta.209",
54
- "@tiptap/extension-code": "2.0.0-beta.209",
55
- "@tiptap/extension-collaboration": "2.0.0-beta.209",
56
- "@tiptap/extension-collaboration-cursor": "2.0.0-beta.209",
57
- "@tiptap/extension-dropcursor": "2.0.0-beta.209",
58
- "@tiptap/extension-gapcursor": "2.0.0-beta.209",
59
- "@tiptap/extension-hard-break": "2.0.0-beta.209",
60
- "@tiptap/extension-history": "2.0.0-beta.209",
61
- "@tiptap/extension-horizontal-rule": "2.0.0-beta.209",
62
- "@tiptap/extension-italic": "2.0.0-beta.209",
63
- "@tiptap/extension-link": "2.0.0-beta.209",
64
- "@tiptap/extension-paragraph": "2.0.0-beta.209",
65
- "@tiptap/extension-strike": "2.0.0-beta.209",
66
- "@tiptap/extension-text": "2.0.0-beta.209",
67
- "@tiptap/extension-underline": "2.0.0-beta.209",
52
+ "@tiptap/core": "2.0.0-beta.217",
53
+ "@tiptap/extension-bold": "2.0.0-beta.217",
54
+ "@tiptap/extension-code": "2.0.0-beta.217",
55
+ "@tiptap/extension-collaboration": "2.0.0-beta.217",
56
+ "@tiptap/extension-collaboration-cursor": "2.0.0-beta.217",
57
+ "@tiptap/extension-dropcursor": "2.0.0-beta.217",
58
+ "@tiptap/extension-gapcursor": "2.0.0-beta.217",
59
+ "@tiptap/extension-hard-break": "2.0.0-beta.217",
60
+ "@tiptap/extension-history": "2.0.0-beta.217",
61
+ "@tiptap/extension-horizontal-rule": "2.0.0-beta.217",
62
+ "@tiptap/extension-italic": "2.0.0-beta.217",
63
+ "@tiptap/extension-link": "2.0.0-beta.217",
64
+ "@tiptap/extension-paragraph": "2.0.0-beta.217",
65
+ "@tiptap/extension-strike": "2.0.0-beta.217",
66
+ "@tiptap/extension-text": "2.0.0-beta.217",
67
+ "@tiptap/extension-underline": "2.0.0-beta.217",
68
+ "@tiptap/pm": "2.0.0-beta.217",
68
69
  "lodash": "^4.17.21",
69
- "prosemirror-commands": "^1.5.0",
70
- "prosemirror-dropcursor": "1.5.0",
71
- "prosemirror-gapcursor": "^1.3.1",
72
- "prosemirror-history": "^1.3.0",
73
- "prosemirror-keymap": "^1.2.0",
74
- "prosemirror-model": "~1.18.3",
75
- "prosemirror-schema-list": "^1.2.2",
76
- "prosemirror-state": "^1.4.2",
77
- "prosemirror-transform": "^1.7.1",
78
- "prosemirror-view": "^1.30.0",
79
70
  "uuid": "^8.3.2",
80
- "y-prosemirror": "1.0.20"
71
+ "y-prosemirror": "1.0.20",
72
+ "y-protocols": "1.0.5",
73
+ "yjs": "13.5.44"
81
74
  },
82
75
  "devDependencies": {
83
76
  "@types/lodash": "^4.14.179",
@@ -102,5 +95,5 @@
102
95
  "access": "public",
103
96
  "registry": "https://registry.npmjs.org/"
104
97
  },
105
- "gitHead": "839c9554e572d416e79eb5f95eb70943096d2403"
98
+ "gitHead": "d1d1756de514dedd5ef9c0d297b45db9b5ba8d02"
106
99
  }
@@ -1,6 +1,5 @@
1
1
  import { Extensions, extensions } from "@tiptap/core";
2
2
 
3
- import { Node } from "@tiptap/core";
4
3
  import Bold from "@tiptap/extension-bold";
5
4
  import Code from "@tiptap/extension-code";
6
5
  import DropCursor from "@tiptap/extension-dropcursor";
@@ -16,7 +15,6 @@ import blockStyles from "./extensions/Blocks/nodes/Block.module.css";
16
15
  import { FormattingToolbarExtension } from "./extensions/FormattingToolbar/FormattingToolbarExtension";
17
16
  import { DraggableBlocksExtension } from "./extensions/DraggableBlocks/DraggableBlocksExtension";
18
17
  import HyperlinkMark from "./extensions/HyperlinkToolbar/HyperlinkMark";
19
- import { FixedParagraph } from "./extensions/Paragraph/FixedParagraph";
20
18
  import { Placeholder } from "./extensions/Placeholder/PlaceholderExtension";
21
19
  import SlashMenuExtension from "./extensions/SlashMenu";
22
20
  import { TrailingNode } from "./extensions/TrailingNode/TrailingNodeExtension";
@@ -27,12 +25,11 @@ import { SuggestionsMenuFactory } from "./shared/plugins/suggestion/SuggestionsM
27
25
  import { BlockSideMenuFactory } from "./extensions/DraggableBlocks/BlockSideMenuFactoryTypes";
28
26
  import { Link } from "@tiptap/extension-link";
29
27
  import { SlashMenuItem } from "./extensions/SlashMenu/SlashMenuItem";
30
-
31
- export const Document = Node.create({
32
- name: "doc",
33
- topNode: true,
34
- content: "block+",
35
- });
28
+ import { BackgroundColorMark } from "./extensions/BackgroundColor/BackgroundColorMark";
29
+ import { TextColorMark } from "./extensions/TextColor/TextColorMark";
30
+ import { BackgroundColorExtension } from "./extensions/BackgroundColor/BackgroundColorExtension";
31
+ import { TextColorExtension } from "./extensions/TextColor/TextColorExtension";
32
+ import { TextAlignmentExtension } from "./extensions/TextAlignment/TextAlignmentExtension";
36
33
 
37
34
  export type UiFactories = Partial<{
38
35
  formattingToolbarFactory: FormattingToolbarFactory;
@@ -64,7 +61,7 @@ export const getBlockNoteExtensions = (uiFactories: UiFactories) => {
64
61
  showOnlyCurrent: false,
65
62
  }),
66
63
  UniqueID.configure({
67
- types: ["block"],
64
+ types: ["blockContainer"],
68
65
  }),
69
66
  HardBreak,
70
67
  // Comments,
@@ -78,7 +75,11 @@ export const getBlockNoteExtensions = (uiFactories: UiFactories) => {
78
75
  Italic,
79
76
  Strike,
80
77
  Underline,
81
- FixedParagraph,
78
+ TextColorMark,
79
+ TextColorExtension,
80
+ BackgroundColorMark,
81
+ BackgroundColorExtension,
82
+ TextAlignmentExtension,
82
83
 
83
84
  // custom blocks:
84
85
  ...blocks,
@@ -0,0 +1,61 @@
1
+ import { Extension } from "@tiptap/core";
2
+ import { getBlockInfoFromPos } from "../Blocks/helpers/getBlockInfoFromPos";
3
+
4
+ declare module "@tiptap/core" {
5
+ interface Commands<ReturnType> {
6
+ blockBackgroundColor: {
7
+ setBlockBackgroundColor: (
8
+ posInBlock: number,
9
+ color: string
10
+ ) => ReturnType;
11
+ };
12
+ }
13
+ }
14
+
15
+ export const BackgroundColorExtension = Extension.create({
16
+ name: "blockBackgroundColor",
17
+
18
+ addGlobalAttributes() {
19
+ return [
20
+ {
21
+ types: ["blockContainer"],
22
+ attributes: {
23
+ backgroundColor: {
24
+ default: "default",
25
+ parseHTML: (element) =>
26
+ element.hasAttribute("data-background-color")
27
+ ? element.getAttribute("data-background-color")
28
+ : "default",
29
+ renderHTML: (attributes) =>
30
+ attributes.backgroundColor !== "default" && {
31
+ "data-background-color": attributes.backgroundColor,
32
+ },
33
+ },
34
+ },
35
+ },
36
+ ];
37
+ },
38
+
39
+ addCommands() {
40
+ return {
41
+ setBlockBackgroundColor:
42
+ (posInBlock, color) =>
43
+ ({ state, view }) => {
44
+ const blockInfo = getBlockInfoFromPos(state.doc, posInBlock);
45
+ if (blockInfo === undefined) {
46
+ return false;
47
+ }
48
+
49
+ state.tr.setNodeAttribute(
50
+ blockInfo.startPos - 1,
51
+ "backgroundColor",
52
+ color
53
+ );
54
+
55
+ view.focus();
56
+
57
+ return true;
58
+ },
59
+ };
60
+ },
61
+ });
@@ -0,0 +1,62 @@
1
+ import { Mark } from "@tiptap/core";
2
+
3
+ declare module "@tiptap/core" {
4
+ interface Commands<ReturnType> {
5
+ backgroundColor: {
6
+ setBackgroundColor: (color: string) => ReturnType;
7
+ };
8
+ }
9
+ }
10
+
11
+ export const BackgroundColorMark = Mark.create({
12
+ name: "backgroundColor",
13
+
14
+ addAttributes() {
15
+ return {
16
+ color: {
17
+ default: undefined,
18
+ parseHTML: (element) => element.getAttribute("data-background-color"),
19
+ renderHTML: (attributes) => ({
20
+ "data-background-color": attributes.color,
21
+ }),
22
+ },
23
+ };
24
+ },
25
+
26
+ parseHTML() {
27
+ return [
28
+ {
29
+ tag: "span",
30
+ getAttrs: (element) => {
31
+ if (typeof element === "string") {
32
+ return false;
33
+ }
34
+
35
+ if (element.hasAttribute("data-background-color")) {
36
+ return { color: element.getAttribute("data-background-color") };
37
+ }
38
+
39
+ return false;
40
+ },
41
+ },
42
+ ];
43
+ },
44
+
45
+ renderHTML({ HTMLAttributes }) {
46
+ return ["span", HTMLAttributes, 0];
47
+ },
48
+
49
+ addCommands() {
50
+ return {
51
+ setBackgroundColor:
52
+ (color) =>
53
+ ({ commands }) => {
54
+ if (color !== "default") {
55
+ return commands.setMark(this.name, { color: color });
56
+ }
57
+
58
+ return commands.unsetMark(this.name);
59
+ },
60
+ };
61
+ },
62
+ });
@@ -1,17 +1,15 @@
1
- import {
2
- combineTransactionSteps,
3
- findChildren,
4
- getChangedRanges,
5
- } from "@tiptap/core";
1
+ import { findChildren } from "@tiptap/core";
6
2
  import { Plugin, PluginKey } from "prosemirror-state";
7
3
  import { Decoration, DecorationSet } from "prosemirror-view";
8
4
 
9
5
  const PLUGIN_KEY = new PluginKey(`previous-blocks`);
10
6
 
11
7
  const nodeAttributes: Record<string, string> = {
12
- listItemType: "list-item-type",
13
- listItemIndex: "list-item-index",
14
- headingLevel: "heading-level",
8
+ // Numbered List Items
9
+ index: "index",
10
+ // Headings
11
+ level: "level",
12
+ // All Blocks
15
13
  type: "type",
16
14
  depth: "depth",
17
15
  "depth-change": "depth-change",
@@ -31,7 +29,7 @@ export const PreviousBlockTypePlugin = () => {
31
29
  view(_editorView) {
32
30
  return {
33
31
  update: async (view, _prevState) => {
34
- if (this.key?.getState(view.state).needsUpdate) {
32
+ if (this.key?.getState(view.state).updatedBlocks.size > 0) {
35
33
  // use setTimeout 0 to clear the decorations so that at least
36
34
  // for one DOM-render the decorations have been applied
37
35
  setTimeout(() => {
@@ -46,114 +44,122 @@ export const PreviousBlockTypePlugin = () => {
46
44
  state: {
47
45
  init() {
48
46
  return {
49
- prevBlockAttrs: {} as any,
50
- needsUpdate: false,
47
+ // Block attributes, by block ID, from just before the previous transaction.
48
+ prevTransactionOldBlockAttrs: {} as any,
49
+ // Block attributes, by block ID, from just before the current transaction.
50
+ currentTransactionOldBlockAttrs: {} as any,
51
+ // Set of IDs of blocks whose attributes changed from the current transaction.
52
+ updatedBlocks: new Set<string>(),
51
53
  };
52
54
  },
53
55
 
54
56
  apply(transaction, prev, oldState, newState) {
55
- prev.needsUpdate = false;
56
- prev.prevBlockAttrs = {};
57
+ prev.currentTransactionOldBlockAttrs = {};
58
+ prev.updatedBlocks.clear();
59
+
57
60
  if (!transaction.docChanged || oldState.doc.eq(newState.doc)) {
58
61
  return prev;
59
62
  }
60
63
 
61
- const transform = combineTransactionSteps(oldState.doc, [transaction]);
62
- // const { mapping } = transform;
63
- const changes = getChangedRanges(transform);
64
-
65
- // TODO: instead of iterating through the entire document, only check nodes affected by the transactions
64
+ // TODO: Instead of iterating through the entire document, only check nodes affected by the transactions. Will
65
+ // also probably require checking nodes affected by the previous transaction too.
66
66
  // We didn't get this to work yet:
67
+ // const transform = combineTransactionSteps(oldState.doc, [transaction]);
68
+ // // const { mapping } = transform;
69
+ // const changes = getChangedRanges(transform);
70
+ //
67
71
  // changes.forEach(({ oldRange, newRange }) => {
68
72
  // const oldNodes = findChildrenInRange(
69
73
  // oldState.doc,
70
74
  // oldRange,
71
75
  // (node) => node.attrs.id
72
76
  // );
73
-
77
+ //
74
78
  // const newNodes = findChildrenInRange(
75
79
  // newState.doc,
76
80
  // newRange,
77
81
  // (node) => node.attrs.id
78
82
  // );
79
83
 
80
- changes.forEach(() => {
81
- const oldNodes = findChildren(oldState.doc, (node) => node.attrs.id);
82
- const oldNodesById = new Map(
83
- oldNodes.map((node) => [node.node.attrs.id, node])
84
- );
85
-
86
- const newNodes = findChildren(newState.doc, (node) => node.attrs.id);
87
-
88
- for (let node of newNodes) {
89
- const oldNode = oldNodesById.get(node.node.attrs.id);
90
- const oldContentNode = oldNode?.node.firstChild;
91
- const newContentNode = node.node.firstChild;
92
- if (oldNode && oldContentNode && newContentNode) {
93
- const newAttrs = {
94
- listItemType: newContentNode.attrs.listItemType,
95
- listItemIndex: newContentNode.attrs.listItemIndex,
96
- headingLevel: newContentNode.attrs.headingLevel,
97
- type: newContentNode.type.name,
98
- depth: newState.doc.resolve(node.pos).depth,
99
- };
100
-
101
- const oldAttrs = {
102
- listItemType: oldContentNode.attrs.listItemType,
103
- listItemIndex: oldContentNode.attrs.listItemIndex,
104
- headingLevel: oldContentNode.attrs.headingLevel,
105
- type: oldContentNode.type.name,
106
- depth: oldState.doc.resolve(oldNode.pos).depth,
107
- };
108
-
109
- // Hacky fix to avoid processing certain transactions created by ordered list indexing plugin.
110
-
111
- // True when an existing ordered list item is assigned an index for the first time, which happens
112
- // immediately after it's created. Using this condition to start an animation ensures it's not
113
- // immediately overridden by a different transaction created by the ordered list indexing plugin.
114
- const indexInitialized =
115
- oldAttrs.listItemIndex === null &&
116
- newAttrs.listItemIndex !== null;
117
-
118
- // True when an existing ordered list item changes nesting levels, before its index is updated by the
119
- // ordered list indexing plugin. This condition ensures that animations for indentation still work with
120
- // ordered list items, while preventing unnecessary animations being done when dragging/dropping them.
121
- const depthChanged =
122
- oldAttrs.listItemIndex !== null &&
123
- newAttrs.listItemIndex !== null &&
124
- oldAttrs.listItemIndex === newAttrs.listItemIndex;
125
-
126
- // Only false for transactions in which the block remains an ordered list item before & after, but neither
127
- // of the previous conditions apply.
128
- const shouldUpdate =
129
- oldAttrs.listItemType === "ordered" &&
130
- newAttrs.listItemType === "ordered"
131
- ? indexInitialized || depthChanged
132
- : true;
133
-
134
- if (
135
- JSON.stringify(oldAttrs) !== JSON.stringify(newAttrs) && // TODO: faster deep equal?
136
- shouldUpdate
137
- ) {
138
- (oldAttrs as any)["depth-change"] =
139
- oldAttrs.depth - newAttrs.depth;
140
- prev.prevBlockAttrs[node.node.attrs.id] = oldAttrs;
141
-
142
- // for debugging:
143
- console.log(
144
- "id:",
145
- node.node.attrs.id,
146
- "previousBlockTypePlugin changes detected, oldAttrs",
147
- oldAttrs,
148
- "new",
149
- newAttrs
150
- );
151
-
152
- prev.needsUpdate = true;
84
+ const currentTransactionOriginalOldBlockAttrs = {} as any;
85
+
86
+ const oldNodes = findChildren(oldState.doc, (node) => node.attrs.id);
87
+ const oldNodesById = new Map(
88
+ oldNodes.map((node) => [node.node.attrs.id, node])
89
+ );
90
+ const newNodes = findChildren(newState.doc, (node) => node.attrs.id);
91
+
92
+ // Traverses all block containers in the new editor state.
93
+ for (let node of newNodes) {
94
+ const oldNode = oldNodesById.get(node.node.attrs.id);
95
+
96
+ const oldContentNode = oldNode?.node.firstChild;
97
+ const newContentNode = node.node.firstChild;
98
+
99
+ if (oldNode && oldContentNode && newContentNode) {
100
+ const newAttrs = {
101
+ index: newContentNode.attrs.index,
102
+ level: newContentNode.attrs.level,
103
+ type: newContentNode.type.name,
104
+ depth: newState.doc.resolve(node.pos).depth,
105
+ };
106
+
107
+ let oldAttrs = {
108
+ index: oldContentNode.attrs.index,
109
+ level: oldContentNode.attrs.level,
110
+ type: oldContentNode.type.name,
111
+ depth: oldState.doc.resolve(oldNode.pos).depth,
112
+ };
113
+
114
+ currentTransactionOriginalOldBlockAttrs[node.node.attrs.id] =
115
+ oldAttrs;
116
+
117
+ // Whenever a transaction is appended by the OrderedListItemIndexPlugin, it's given the metadata:
118
+ // { "orderedListIndexing": true }
119
+ // These appended transactions happen immediately after any transaction which causes ordered list item
120
+ // indices to require updating, including those which trigger animations. Therefore, these animations are
121
+ // immediately overridden when the PreviousBlockTypePlugin processes the appended transaction, despite only
122
+ // the listItemIndex attribute changing. To solve this, oldAttrs must be edited for transactions with the
123
+ // "orderedListIndexing" metadata, so the correct animation can be re-triggered.
124
+ if (transaction.getMeta("numberedListIndexing")) {
125
+ // If the block existed before the transaction, gets the attributes from before the previous transaction
126
+ // (i.e. the transaction that caused list item indices to need updating).
127
+ if (node.node.attrs.id in prev.prevTransactionOldBlockAttrs) {
128
+ oldAttrs =
129
+ prev.prevTransactionOldBlockAttrs[node.node.attrs.id];
130
+ }
131
+
132
+ // Stops list item indices themselves being animated (looks smoother), unless the block's content type is
133
+ // changing from a numbered list item to something else.
134
+ if (newAttrs.type === "numberedListItem") {
135
+ oldAttrs.index = newAttrs.index;
153
136
  }
154
137
  }
138
+
139
+ prev.currentTransactionOldBlockAttrs[node.node.attrs.id] = oldAttrs;
140
+
141
+ // TODO: faster deep equal?
142
+ if (JSON.stringify(oldAttrs) !== JSON.stringify(newAttrs)) {
143
+ (oldAttrs as any)["depth-change"] =
144
+ oldAttrs.depth - newAttrs.depth;
145
+
146
+ // for debugging:
147
+ // console.log(
148
+ // "id:",
149
+ // node.node.attrs.id,
150
+ // "previousBlockTypePlugin changes detected, oldAttrs",
151
+ // oldAttrs,
152
+ // "new",
153
+ // newAttrs
154
+ // );
155
+
156
+ prev.updatedBlocks.add(node.node.attrs.id);
157
+ }
155
158
  }
156
- });
159
+ }
160
+
161
+ prev.prevTransactionOldBlockAttrs =
162
+ currentTransactionOriginalOldBlockAttrs;
157
163
 
158
164
  return prev;
159
165
  },
@@ -161,8 +167,7 @@ export const PreviousBlockTypePlugin = () => {
161
167
  props: {
162
168
  decorations(state) {
163
169
  const pluginState = (this as Plugin).getState(state);
164
- if (!pluginState.needsUpdate) {
165
- // console.log("0");
170
+ if (pluginState.updatedBlocks.size === 0) {
166
171
  return undefined;
167
172
  }
168
173
 
@@ -170,29 +175,30 @@ export const PreviousBlockTypePlugin = () => {
170
175
 
171
176
  state.doc.descendants((node, pos) => {
172
177
  if (!node.attrs.id) {
173
- // console.log("1");
174
178
  return;
175
179
  }
176
- const prevAttrs = pluginState.prevBlockAttrs[node.attrs.id];
177
- if (!prevAttrs) {
178
- // console.log("2");
180
+
181
+ if (!pluginState.updatedBlocks.has(node.attrs.id)) {
179
182
  return;
180
183
  }
181
184
 
182
- const decorationAttributes: any = {};
185
+ const prevAttrs =
186
+ pluginState.currentTransactionOldBlockAttrs[node.attrs.id];
187
+ const decorationAttrs: any = {};
188
+
183
189
  for (let [nodeAttr, val] of Object.entries(prevAttrs)) {
184
- decorationAttributes["data-prev-" + nodeAttributes[nodeAttr]] =
190
+ decorationAttrs["data-prev-" + nodeAttributes[nodeAttr]] =
185
191
  val || "none";
186
192
  }
187
193
 
188
194
  // for debugging:
189
- console.log(
190
- "previousBlockTypePlugin committing decorations",
191
- decorationAttributes
192
- );
195
+ // console.log(
196
+ // "previousBlockTypePlugin committing decorations",
197
+ // decorationAttrs
198
+ // );
193
199
 
194
200
  const decoration = Decoration.node(pos, pos + node.nodeSize, {
195
- ...decorationAttributes,
201
+ ...decorationAttrs,
196
202
  });
197
203
 
198
204
  decorations.push(decoration);
@@ -0,0 +1,48 @@
1
+ export type BlockSpec<
2
+ // Type of the block.
3
+ // Examples might include: "paragraph", "heading", or "bulletListItem".
4
+ Type extends string,
5
+ // Changeable props which affect the block's behaviour or appearance.
6
+ // An example might be: { textAlignment: "left" | "right" | "center" | "justify" } for a paragraph block.
7
+ Props extends Record<string, string>
8
+ > = {
9
+ type: Type;
10
+ props: Props;
11
+ };
12
+
13
+ export type BlockSpecUpdate<Spec> = Spec extends BlockSpec<
14
+ infer Type,
15
+ infer Props
16
+ >
17
+ ? {
18
+ type: Type;
19
+ props?: Partial<Props>;
20
+ }
21
+ : never;
22
+
23
+ export type NumberedListItemBlock = BlockSpec<"numberedListItem", {}>;
24
+
25
+ export type BulletListItemBlock = BlockSpec<"bulletListItem", {}>;
26
+
27
+ export type HeadingBlock = BlockSpec<
28
+ "heading",
29
+ {
30
+ level: "1" | "2" | "3";
31
+ }
32
+ >;
33
+
34
+ export type ParagraphBlock = BlockSpec<"paragraph", {}>;
35
+
36
+ export type Block =
37
+ | ParagraphBlock
38
+ | HeadingBlock
39
+ | BulletListItemBlock
40
+ | NumberedListItemBlock;
41
+
42
+ export type BlockUpdate = BlockSpecUpdate<Block>;
43
+
44
+ /*
45
+ TODO:
46
+ 1) guard read / writes (now we just pass on internal node attrs)
47
+ 2) where to locate this code / types
48
+ */
@@ -1,3 +1,5 @@
1
1
  import { findParentNode } from "@tiptap/core";
2
2
 
3
- export const findBlock = findParentNode((node) => node.type.name === "block");
3
+ export const findBlock = findParentNode(
4
+ (node) => node.type.name === "blockContainer"
5
+ );
@@ -37,7 +37,7 @@ export function getBlockInfoFromPos(
37
37
  if (depth === 0) {
38
38
  return undefined;
39
39
  }
40
- if (node.type.name === "block") {
40
+ if (node.type.name === "blockContainer") {
41
41
  break;
42
42
  }
43
43
 
@@ -1,15 +1,17 @@
1
1
  import { Node } from "@tiptap/core";
2
- import { Block } from "./nodes/Block";
2
+ import { BlockContainer } from "./nodes/BlockContainer";
3
3
  import { BlockGroup } from "./nodes/BlockGroup";
4
- import { TextContent } from "./nodes/BlockTypes/TextBlock/TextContent";
5
- import { HeadingContent } from "./nodes/BlockTypes/HeadingBlock/HeadingContent";
6
- import { ListItemContent } from "./nodes/BlockTypes/ListItemBlock/ListItemContent";
4
+ import { ParagraphBlockContent } from "./nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent";
5
+ import { HeadingBlockContent } from "./nodes/BlockContent/HeadingBlockContent/HeadingBlockContent";
6
+ import { BulletListItemBlockContent } from "./nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent";
7
+ import { NumberedListItemBlockContent } from "./nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent";
7
8
 
8
9
  export const blocks: any[] = [
9
- TextContent,
10
- HeadingContent,
11
- ListItemContent,
12
- Block,
10
+ ParagraphBlockContent,
11
+ HeadingBlockContent,
12
+ BulletListItemBlockContent,
13
+ NumberedListItemBlockContent,
14
+ BlockContainer,
13
15
  BlockGroup,
14
16
  Node.create({
15
17
  name: "doc",