@blocknote/core 0.2.2-alpha.0 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/blocknote.js +696 -691
  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 +6 -4
  7. package/src/BlockNoteExtensions.ts +1 -10
  8. package/src/extensions/Blocks/PreviousBlockTypePlugin.ts +22 -26
  9. package/src/extensions/Blocks/apiTypes.ts +48 -0
  10. package/src/extensions/Blocks/helpers/findBlock.ts +3 -1
  11. package/src/extensions/Blocks/helpers/getBlockInfoFromPos.ts +1 -1
  12. package/src/extensions/Blocks/index.ts +10 -8
  13. package/src/extensions/Blocks/nodes/Block.module.css +31 -29
  14. package/src/extensions/Blocks/{BlockAttributes.ts → nodes/BlockAttributes.ts} +0 -0
  15. package/src/extensions/Blocks/nodes/{Block.ts → BlockContainer.ts} +75 -94
  16. package/src/extensions/Blocks/nodes/{BlockTypes/HeadingBlock/HeadingContent.ts → BlockContent/HeadingBlockContent/HeadingBlockContent.ts} +16 -24
  17. package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +76 -0
  18. package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/ListItemKeyboardShortcuts.ts +47 -0
  19. package/src/extensions/Blocks/nodes/{BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.ts → BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts} +10 -14
  20. package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +95 -0
  21. package/src/extensions/Blocks/nodes/{BlockTypes/TextBlock/TextContent.ts → BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts} +3 -8
  22. package/src/extensions/Blocks/nodes/BlockGroup.ts +4 -4
  23. package/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.ts +1 -1
  24. package/src/extensions/DraggableBlocks/DraggableBlocksPlugin.ts +7 -7
  25. package/src/extensions/{Blocks → DraggableBlocks}/MultipleNodeSelection.ts +0 -0
  26. package/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.ts +4 -7
  27. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +17 -9
  28. package/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.ts +1 -1
  29. package/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.ts +3 -1
  30. package/src/extensions/SlashMenu/defaultCommands.tsx +22 -23
  31. package/src/extensions/TrailingNode/TrailingNodeExtension.ts +4 -4
  32. package/src/extensions/UniqueID/UniqueID.ts +6 -0
  33. package/src/index.ts +2 -1
  34. package/src/shared/EditorElement.ts +12 -6
  35. package/src/shared/plugins/suggestion/SuggestionPlugin.ts +2 -2
  36. package/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.ts +1 -1
  37. package/types/src/BlockNoteEditor.d.ts +1 -1
  38. package/types/src/BlockNoteExtensions.d.ts +1 -3
  39. package/types/src/extensions/Blocks/apiTypes.d.ts +16 -0
  40. package/types/src/extensions/Blocks/helpers/getBlockInfoFromPos.d.ts +1 -1
  41. package/types/src/extensions/Blocks/nodes/BlockAttributes.d.ts +2 -0
  42. package/types/src/extensions/Blocks/nodes/BlockContainer.d.ts +21 -0
  43. package/types/src/extensions/Blocks/nodes/BlockContent/BlockContentTypes.d.ts +4 -0
  44. package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContent.d.ts +2 -0
  45. package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContentTypes.d.ts +4 -0
  46. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +2 -0
  47. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContentTypes.d.ts +2 -0
  48. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/ListItemKeyboardShortcuts.d.ts +2 -0
  49. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.d.ts +2 -0
  50. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +2 -0
  51. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContentTypes.d.ts +2 -0
  52. package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.d.ts +2 -0
  53. package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContentTypes.d.ts +2 -0
  54. package/types/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.d.ts +5 -5
  55. package/types/src/extensions/DraggableBlocks/DraggableBlocksExtension.d.ts +1 -1
  56. package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +2 -2
  57. package/types/src/extensions/DraggableBlocks/MultipleNodeSelection.d.ts +24 -0
  58. package/types/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.d.ts +8 -8
  59. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +1 -1
  60. package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.d.ts +5 -5
  61. package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.d.ts +2 -2
  62. package/types/src/extensions/SlashMenu/SlashMenuExtension.d.ts +1 -1
  63. package/types/src/extensions/SlashMenu/SlashMenuItem.d.ts +1 -1
  64. package/types/src/index.d.ts +2 -1
  65. package/types/src/shared/EditorElement.d.ts +6 -2
  66. package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +3 -3
  67. package/types/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.d.ts +5 -5
  68. package/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.ts +0 -177
  69. package/src/extensions/Paragraph/FixedParagraph.ts +0 -12
@@ -1,23 +1,17 @@
1
1
  import { mergeAttributes, Node } from "@tiptap/core";
2
2
  import { Slice } from "prosemirror-model";
3
3
  import { TextSelection } from "prosemirror-state";
4
- import BlockAttributes from "../BlockAttributes";
4
+ import { BlockUpdate } from "../apiTypes";
5
5
  import { getBlockInfoFromPos } from "../helpers/getBlockInfoFromPos";
6
6
  import { PreviousBlockTypePlugin } from "../PreviousBlockTypePlugin";
7
7
  import styles from "./Block.module.css";
8
- import { TextContentType } from "./BlockTypes/TextBlock/TextContent";
9
- import { HeadingContentType } from "./BlockTypes/HeadingBlock/HeadingContent";
10
- import { ListItemContentType } from "./BlockTypes/ListItemBlock/ListItemContent";
8
+ import BlockAttributes from "./BlockAttributes";
11
9
 
10
+ // TODO
12
11
  export interface IBlock {
13
12
  HTMLAttributes: Record<string, any>;
14
13
  }
15
14
 
16
- export type BlockContentType =
17
- | TextContentType
18
- | HeadingContentType
19
- | ListItemContentType;
20
-
21
15
  declare module "@tiptap/core" {
22
16
  interface Commands<ReturnType> {
23
17
  block: {
@@ -25,13 +19,13 @@ declare module "@tiptap/core" {
25
19
  BNDeleteBlock: (posInBlock: number) => ReturnType;
26
20
  BNMergeBlocks: (posBetweenBlocks: number) => ReturnType;
27
21
  BNSplitBlock: (posInBlock: number, keepType: boolean) => ReturnType;
28
- BNSetContentType: (
22
+ BNUpdateBlock: (
29
23
  posInBlock: number,
30
- type: BlockContentType
24
+ blockUpdate: BlockUpdate
31
25
  ) => ReturnType;
32
- BNCreateBlockOrSetContentType: (
26
+ BNCreateOrUpdateBlock: (
33
27
  posInBlock: number,
34
- type: BlockContentType
28
+ blockUpdate: BlockUpdate
35
29
  ) => ReturnType;
36
30
  };
37
31
  }
@@ -40,9 +34,9 @@ declare module "@tiptap/core" {
40
34
  /**
41
35
  * The main "Block node" documents consist of
42
36
  */
43
- export const Block = Node.create<IBlock>({
44
- name: "block",
45
- group: "block",
37
+ export const BlockContainer = Node.create<IBlock>({
38
+ name: "blockContainer",
39
+ group: "blockContainer",
46
40
  // A block always contains content, and optionally a blockGroup which contains nested blocks
47
41
  content: "blockContent blockGroup?",
48
42
  // Ensures content-specific keyboard handlers trigger first.
@@ -82,7 +76,7 @@ export const Block = Node.create<IBlock>({
82
76
  }
83
77
  }
84
78
 
85
- if (element.getAttribute("data-node-type") === "block") {
79
+ if (element.getAttribute("data-node-type") === "blockContainer") {
86
80
  return attrs;
87
81
  }
88
82
 
@@ -93,23 +87,15 @@ export const Block = Node.create<IBlock>({
93
87
  },
94
88
 
95
89
  renderHTML({ HTMLAttributes }) {
96
- const attrs: Record<string, string> = {};
97
- for (let [nodeAttr, HTMLAttr] of Object.entries(BlockAttributes)) {
98
- // Ensure falsy values are not misinterpreted.
99
- if (HTMLAttributes[nodeAttr] !== undefined) {
100
- attrs[HTMLAttr] = HTMLAttributes[nodeAttr];
101
- }
102
- }
103
-
104
90
  return [
105
91
  "div",
106
- mergeAttributes(attrs, {
92
+ mergeAttributes(HTMLAttributes, {
107
93
  class: styles.blockOuter,
108
94
  "data-node-type": "block-outer",
109
95
  }),
110
96
  [
111
97
  "div",
112
- mergeAttributes(attrs, {
98
+ mergeAttributes(HTMLAttributes, {
113
99
  // TODO: maybe remove html attributes from inner block
114
100
  class: styles.block,
115
101
  "data-node-type": this.name,
@@ -125,7 +111,8 @@ export const Block = Node.create<IBlock>({
125
111
  BNCreateBlock:
126
112
  (pos) =>
127
113
  ({ state, dispatch }) => {
128
- const newBlock = state.schema.nodes["block"].createAndFill()!;
114
+ const newBlock =
115
+ state.schema.nodes["blockContainer"].createAndFill()!;
129
116
 
130
117
  if (dispatch) {
131
118
  state.tr.insert(pos, newBlock);
@@ -176,10 +163,10 @@ export const Block = Node.create<IBlock>({
176
163
  ({ state, dispatch }) => {
177
164
  const nextNodeIsBlock =
178
165
  state.doc.resolve(posBetweenBlocks + 1).node().type.name ===
179
- "block";
166
+ "blockContainer";
180
167
  const prevNodeIsBlock =
181
168
  state.doc.resolve(posBetweenBlocks - 1).node().type.name ===
182
- "block";
169
+ "blockContainer";
183
170
 
184
171
  if (!nextNodeIsBlock || !prevNodeIsBlock) {
185
172
  return false;
@@ -252,7 +239,8 @@ export const Block = Node.create<IBlock>({
252
239
  // Only text content is transferred to the new block.
253
240
  const secondBlockContent = state.doc.textBetween(posInBlock, endPos);
254
241
 
255
- const newBlock = state.schema.nodes["block"].createAndFill()!;
242
+ const newBlock =
243
+ state.schema.nodes["blockContainer"].createAndFill()!;
256
244
  const newBlockContentPos = newBlockInsertionPos + 2;
257
245
 
258
246
  if (dispatch) {
@@ -283,22 +271,25 @@ export const Block = Node.create<IBlock>({
283
271
  return true;
284
272
  },
285
273
  // Changes the content of a block at a given position to a given type.
286
- BNSetContentType:
287
- (posInBlock, type) =>
274
+ BNUpdateBlock:
275
+ (posInBlock, blockUpdate) =>
288
276
  ({ state, dispatch }) => {
289
277
  const blockInfo = getBlockInfoFromPos(state.doc, posInBlock);
290
278
  if (blockInfo === undefined) {
291
279
  return false;
292
280
  }
293
281
 
294
- const { startPos, contentNode } = blockInfo;
282
+ const { node, startPos, contentNode } = blockInfo;
295
283
 
296
284
  if (dispatch) {
297
285
  state.tr.setBlockType(
298
286
  startPos + 1,
299
287
  startPos + contentNode.nodeSize + 1,
300
- state.schema.node(type.name).type,
301
- type.attrs
288
+ state.schema.node(blockUpdate.type).type,
289
+ {
290
+ ...node.attrs,
291
+ ...blockUpdate.props,
292
+ }
302
293
  );
303
294
  }
304
295
 
@@ -306,8 +297,8 @@ export const Block = Node.create<IBlock>({
306
297
  },
307
298
  // Changes the block at a given position to a given content type if it's empty, otherwise creates a new block of
308
299
  // that type below it.
309
- BNCreateBlockOrSetContentType:
310
- (posInBlock, type) =>
300
+ BNCreateOrUpdateBlock:
301
+ (posInBlock, blockUpdate) =>
311
302
  ({ state, chain }) => {
312
303
  const blockInfo = getBlockInfoFromPos(state.doc, posInBlock);
313
304
  if (blockInfo === undefined) {
@@ -320,7 +311,7 @@ export const Block = Node.create<IBlock>({
320
311
  const oldBlockContentPos = startPos + 1;
321
312
 
322
313
  return chain()
323
- .BNSetContentType(posInBlock, type)
314
+ .BNUpdateBlock(posInBlock, blockUpdate)
324
315
  .setTextSelection(oldBlockContentPos)
325
316
  .run();
326
317
  } else {
@@ -329,7 +320,7 @@ export const Block = Node.create<IBlock>({
329
320
 
330
321
  return chain()
331
322
  .BNCreateBlock(newBlockInsertionPos)
332
- .BNSetContentType(newBlockContentPos, type)
323
+ .BNUpdateBlock(newBlockContentPos, blockUpdate)
333
324
  .setTextSelection(newBlockContentPos)
334
325
  .run();
335
326
  }
@@ -349,7 +340,7 @@ export const Block = Node.create<IBlock>({
349
340
  () => commands.deleteSelection(),
350
341
  // Undoes an input rule if one was triggered in the last editor state change.
351
342
  () => commands.undoInputRule(),
352
- // Changes block type to a text block if it's not already, while the selection is at the start of the block.
343
+ // Reverts block content type to a paragraph if the selection is at the start of the block.
353
344
  () =>
354
345
  commands.command(({ state }) => {
355
346
  const { contentType } = getBlockInfoFromPos(
@@ -359,11 +350,12 @@ export const Block = Node.create<IBlock>({
359
350
 
360
351
  const selectionAtBlockStart =
361
352
  state.selection.$anchor.parentOffset === 0;
362
- const isTextBlock = contentType.name === "textContent";
353
+ const isParagraph = contentType.name === "paragraph";
363
354
 
364
- if (selectionAtBlockStart && !isTextBlock) {
365
- return commands.BNSetContentType(state.selection.from, {
366
- name: "textContent",
355
+ if (selectionAtBlockStart && !isParagraph) {
356
+ return commands.BNUpdateBlock(state.selection.from, {
357
+ type: "paragraph",
358
+ props: {},
367
359
  });
368
360
  }
369
361
 
@@ -376,7 +368,7 @@ export const Block = Node.create<IBlock>({
376
368
  state.selection.$anchor.parentOffset === 0;
377
369
 
378
370
  if (selectionAtBlockStart) {
379
- return commands.liftListItem("block");
371
+ return commands.liftListItem("blockContainer");
380
372
  }
381
373
 
382
374
  return false;
@@ -435,7 +427,7 @@ export const Block = Node.create<IBlock>({
435
427
  blockEmpty &&
436
428
  blockIndented
437
429
  ) {
438
- return commands.liftListItem("block");
430
+ return commands.liftListItem("blockContainer");
439
431
  }
440
432
 
441
433
  return false;
@@ -496,62 +488,51 @@ export const Block = Node.create<IBlock>({
496
488
  return {
497
489
  Backspace: handleBackspace,
498
490
  Enter: handleEnter,
499
- Tab: () => this.editor.commands.sinkListItem("block"),
500
- "Shift-Tab": () => this.editor.commands.liftListItem("block"),
491
+ // Always returning true for tab key presses ensures they're not captured by the browser. Otherwise, they blur the
492
+ // editor since the browser will try to use tab for keyboard navigation.
493
+ Tab: () => {
494
+ this.editor.commands.sinkListItem("blockContainer");
495
+ return true;
496
+ },
497
+ "Shift-Tab": () => {
498
+ this.editor.commands.liftListItem("blockContainer");
499
+ return true;
500
+ },
501
501
  "Mod-Alt-0": () =>
502
502
  this.editor.commands.BNCreateBlock(
503
503
  this.editor.state.selection.anchor + 2
504
504
  ),
505
505
  "Mod-Alt-1": () =>
506
- this.editor.commands.BNSetContentType(
507
- this.editor.state.selection.anchor,
508
- {
509
- name: "headingContent",
510
- attrs: {
511
- headingLevel: "1",
512
- },
513
- }
514
- ),
506
+ this.editor.commands.BNUpdateBlock(this.editor.state.selection.anchor, {
507
+ type: "heading",
508
+ props: {
509
+ level: "1",
510
+ },
511
+ }),
515
512
  "Mod-Alt-2": () =>
516
- this.editor.commands.BNSetContentType(
517
- this.editor.state.selection.anchor,
518
- {
519
- name: "headingContent",
520
- attrs: {
521
- headingLevel: "2",
522
- },
523
- }
524
- ),
513
+ this.editor.commands.BNUpdateBlock(this.editor.state.selection.anchor, {
514
+ type: "heading",
515
+ props: {
516
+ level: "2",
517
+ },
518
+ }),
525
519
  "Mod-Alt-3": () =>
526
- this.editor.commands.BNSetContentType(
527
- this.editor.state.selection.anchor,
528
- {
529
- name: "headingContent",
530
- attrs: {
531
- headingLevel: "3",
532
- },
533
- }
534
- ),
520
+ this.editor.commands.BNUpdateBlock(this.editor.state.selection.anchor, {
521
+ type: "heading",
522
+ props: {
523
+ level: "3",
524
+ },
525
+ }),
535
526
  "Mod-Shift-7": () =>
536
- this.editor.commands.BNSetContentType(
537
- this.editor.state.selection.anchor,
538
- {
539
- name: "listItemContent",
540
- attrs: {
541
- listItemType: "unordered",
542
- },
543
- }
544
- ),
527
+ this.editor.commands.BNUpdateBlock(this.editor.state.selection.anchor, {
528
+ type: "bulletListItem",
529
+ props: {},
530
+ }),
545
531
  "Mod-Shift-8": () =>
546
- this.editor.commands.BNSetContentType(
547
- this.editor.state.selection.anchor,
548
- {
549
- name: "listItemContent",
550
- attrs: {
551
- listItemType: "ordered",
552
- },
553
- }
554
- ),
532
+ this.editor.commands.BNUpdateBlock(this.editor.state.selection.anchor, {
533
+ type: "numberedListItem",
534
+ props: {},
535
+ }),
555
536
  };
556
537
  },
557
538
  });
@@ -1,27 +1,20 @@
1
1
  import { InputRule, mergeAttributes, Node } from "@tiptap/core";
2
2
  import styles from "../../Block.module.css";
3
3
 
4
- export type HeadingContentType = {
5
- name: "headingContent";
6
- attrs?: {
7
- headingLevel: string;
8
- };
9
- };
10
-
11
- export const HeadingContent = Node.create({
12
- name: "headingContent",
4
+ export const HeadingBlockContent = Node.create({
5
+ name: "heading",
13
6
  group: "blockContent",
14
7
  content: "inline*",
15
8
 
16
9
  addAttributes() {
17
10
  return {
18
- headingLevel: {
11
+ level: {
19
12
  default: "1",
20
13
  // instead of "level" attributes, use "data-level"
21
- parseHTML: (element) => element.getAttribute("data-heading-level"),
14
+ parseHTML: (element) => element.getAttribute("data-level"),
22
15
  renderHTML: (attributes) => {
23
16
  return {
24
- "data-heading-level": attributes.headingLevel,
17
+ "data-level": attributes.level,
25
18
  };
26
19
  },
27
20
  },
@@ -36,10 +29,10 @@ export const HeadingContent = Node.create({
36
29
  find: new RegExp(`^(#{${parseInt(level)}})\\s$`),
37
30
  handler: ({ state, chain, range }) => {
38
31
  chain()
39
- .BNSetContentType(state.selection.from, {
40
- name: "headingContent",
41
- attrs: {
42
- headingLevel: level,
32
+ .BNUpdateBlock(state.selection.from, {
33
+ type: "heading",
34
+ props: {
35
+ level: level as "1" | "2" | "3",
43
36
  },
44
37
  })
45
38
  // Removes the "#" character(s) used to set the heading.
@@ -54,31 +47,30 @@ export const HeadingContent = Node.create({
54
47
  return [
55
48
  {
56
49
  tag: "h1",
57
- attrs: { headingLevel: "1" },
58
- node: "block",
50
+ attrs: { level: "1" },
51
+ node: "blockContainer",
59
52
  },
60
53
  {
61
54
  tag: "h2",
62
- attrs: { headingLevel: "2" },
63
- node: "block",
55
+ attrs: { level: "2" },
56
+ node: "blockContainer",
64
57
  },
65
58
  {
66
59
  tag: "h3",
67
- attrs: { headingLevel: "3" },
68
- node: "block",
60
+ attrs: { level: "3" },
61
+ node: "blockContainer",
69
62
  },
70
63
  ];
71
64
  },
72
65
 
73
66
  renderHTML({ node, HTMLAttributes }) {
74
- console.log(node.attrs);
75
67
  return [
76
68
  "div",
77
69
  mergeAttributes(HTMLAttributes, {
78
70
  class: styles.blockContent,
79
71
  "data-content-type": this.name,
80
72
  }),
81
- ["h" + node.attrs["headingLevel"], 0],
73
+ ["h" + node.attrs.level, 0],
82
74
  ];
83
75
  },
84
76
  });
@@ -0,0 +1,76 @@
1
+ import { InputRule, mergeAttributes, Node } from "@tiptap/core";
2
+ import styles from "../../../Block.module.css";
3
+ import { handleEnter } from "../ListItemKeyboardShortcuts";
4
+
5
+ export const BulletListItemBlockContent = Node.create({
6
+ name: "bulletListItem",
7
+ group: "blockContent",
8
+ content: "inline*",
9
+
10
+ addInputRules() {
11
+ return [
12
+ // Creates an unordered list when starting with "-", "+", or "*".
13
+ new InputRule({
14
+ find: new RegExp(`^[-+*]\\s$`),
15
+ handler: ({ state, chain, range }) => {
16
+ chain()
17
+ .BNUpdateBlock(state.selection.from, {
18
+ type: "bulletListItem",
19
+ props: {},
20
+ })
21
+ // Removes the "-", "+", or "*" character used to set the list.
22
+ .deleteRange({ from: range.from, to: range.to });
23
+ },
24
+ }),
25
+ ];
26
+ },
27
+
28
+ addKeyboardShortcuts() {
29
+ return {
30
+ Enter: () => handleEnter(this.editor),
31
+ };
32
+ },
33
+
34
+ parseHTML() {
35
+ return [
36
+ {
37
+ tag: "li",
38
+ getAttrs: (element) => {
39
+ if (typeof element === "string") {
40
+ return false;
41
+ }
42
+
43
+ const parent = element.parentElement;
44
+
45
+ if (parent === null) {
46
+ return false;
47
+ }
48
+
49
+ // Case for BlockNote list structure.
50
+ if (parent.getAttribute("data-content-type") === "bulletListItem") {
51
+ return {};
52
+ }
53
+
54
+ // Case for regular HTML list structure.
55
+ if (parent.tagName === "UL") {
56
+ return {};
57
+ }
58
+
59
+ return false;
60
+ },
61
+ node: "blockContainer",
62
+ },
63
+ ];
64
+ },
65
+
66
+ renderHTML({ HTMLAttributes }) {
67
+ return [
68
+ "div",
69
+ mergeAttributes(HTMLAttributes, {
70
+ class: styles.blockContent,
71
+ "data-content-type": this.name,
72
+ }),
73
+ ["li", 0],
74
+ ];
75
+ },
76
+ });
@@ -0,0 +1,47 @@
1
+ import { Editor } from "@tiptap/core";
2
+ import { getBlockInfoFromPos } from "../../../helpers/getBlockInfoFromPos";
3
+
4
+ export const handleEnter = (editor: Editor) => {
5
+ const { node, contentType } = getBlockInfoFromPos(
6
+ editor.state.doc,
7
+ editor.state.selection.from
8
+ )!;
9
+
10
+ const selectionEmpty =
11
+ editor.state.selection.anchor === editor.state.selection.head;
12
+
13
+ if (!contentType.name.endsWith("ListItem") || !selectionEmpty) {
14
+ return false;
15
+ }
16
+
17
+ return editor.commands.first(({ state, chain, commands }) => [
18
+ () =>
19
+ // Changes list item block to a text block if the content is empty.
20
+ commands.command(() => {
21
+ if (node.textContent.length === 0) {
22
+ return commands.BNUpdateBlock(state.selection.from, {
23
+ type: "paragraph",
24
+ props: {},
25
+ });
26
+ }
27
+
28
+ return false;
29
+ }),
30
+
31
+ () =>
32
+ // Splits the current block, moving content inside that's after the cursor to a new block of the same type
33
+ // below.
34
+ commands.command(() => {
35
+ if (node.textContent.length > 0) {
36
+ chain()
37
+ .deleteSelection()
38
+ .BNSplitBlock(state.selection.from, true)
39
+ .run();
40
+
41
+ return true;
42
+ }
43
+
44
+ return false;
45
+ }),
46
+ ]);
47
+ };
@@ -1,14 +1,14 @@
1
1
  import { Plugin, PluginKey } from "prosemirror-state";
2
- import { getBlockInfoFromPos } from "../../../helpers/getBlockInfoFromPos";
2
+ import { getBlockInfoFromPos } from "../../../../helpers/getBlockInfoFromPos";
3
3
 
4
4
  // ProseMirror Plugin which automatically assigns indices to ordered list items per nesting level.
5
- const PLUGIN_KEY = new PluginKey(`ordered-list-item-index`);
6
- export const OrderedListItemIndexPlugin = () => {
5
+ const PLUGIN_KEY = new PluginKey(`numbered-list-indexing`);
6
+ export const NumberedListIndexingPlugin = () => {
7
7
  return new Plugin({
8
8
  key: PLUGIN_KEY,
9
9
  appendTransaction: (_transactions, _oldState, newState) => {
10
10
  const tr = newState.tr;
11
- tr.setMeta("orderedListIndexing", true);
11
+ tr.setMeta("numberedListIndexing", true);
12
12
 
13
13
  let modified = false;
14
14
 
@@ -17,9 +17,8 @@ export const OrderedListItemIndexPlugin = () => {
17
17
  // index of the previous list item block.
18
18
  newState.doc.descendants((node, pos) => {
19
19
  if (
20
- node.type.name === "block" &&
21
- node.firstChild!.type.name === "listItemContent" &&
22
- node.firstChild!.attrs["listItemType"] === "ordered"
20
+ node.type.name === "blockContainer" &&
21
+ node.firstChild!.type.name === "numberedListItem"
23
22
  ) {
24
23
  let newIndex = "1";
25
24
  const isFirstBlockInDoc = pos === 1;
@@ -45,12 +44,10 @@ export const OrderedListItemIndexPlugin = () => {
45
44
  const prevBlockContentType = prevBlockInfo.contentType;
46
45
 
47
46
  const isPrevBlockOrderedListItem =
48
- prevBlockContentType.name === "listItemContent" &&
49
- prevBlockContentNode.attrs["listItemType"] === "ordered";
47
+ prevBlockContentType.name === "numberedListItem";
50
48
 
51
49
  if (isPrevBlockOrderedListItem) {
52
- const prevBlockIndex =
53
- prevBlockContentNode.attrs["listItemIndex"];
50
+ const prevBlockIndex = prevBlockContentNode.attrs["index"];
54
51
 
55
52
  newIndex = (parseInt(prevBlockIndex) + 1).toString();
56
53
  }
@@ -58,14 +55,13 @@ export const OrderedListItemIndexPlugin = () => {
58
55
  }
59
56
 
60
57
  const contentNode = blockInfo.contentNode;
61
- const index = contentNode.attrs["listItemIndex"];
58
+ const index = contentNode.attrs["index"];
62
59
 
63
60
  if (index !== newIndex) {
64
61
  modified = true;
65
62
 
66
63
  tr.setNodeMarkup(pos + 1, undefined, {
67
- listItemType: "ordered",
68
- listItemIndex: newIndex,
64
+ index: newIndex,
69
65
  });
70
66
  }
71
67
  }