@blocknote/core 0.3.0 → 0.4.2

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 (95) hide show
  1. package/dist/blocknote.js +12698 -1341
  2. package/dist/blocknote.js.map +1 -1
  3. package/dist/blocknote.umd.cjs +50 -1
  4. package/dist/blocknote.umd.cjs.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/package.json +16 -5
  7. package/src/BlockNoteEditor.test.ts +12 -0
  8. package/src/BlockNoteEditor.ts +39 -15
  9. package/src/BlockNoteExtensions.ts +36 -32
  10. package/src/api/Editor.ts +226 -0
  11. package/src/api/blockManipulation/__snapshots__/blockManipulation.test.ts.snap +616 -0
  12. package/src/api/blockManipulation/blockManipulation.test.ts +172 -0
  13. package/src/api/blockManipulation/blockManipulation.ts +125 -0
  14. package/src/api/formatConversions/__snapshots__/formatConversions.test.ts.snap +346 -0
  15. package/src/api/formatConversions/formatConversions.test.ts +766 -0
  16. package/src/api/formatConversions/formatConversions.ts +86 -0
  17. package/src/api/formatConversions/removeUnderlinesRehypePlugin.ts +39 -0
  18. package/src/api/formatConversions/simplifyBlocksRehypePlugin.ts +125 -0
  19. package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +268 -0
  20. package/src/api/nodeConversions/nodeConversions.test.ts +244 -0
  21. package/src/api/nodeConversions/nodeConversions.ts +279 -0
  22. package/src/api/nodeConversions/testUtil.ts +61 -0
  23. package/src/api/util/nodeUtil.ts +38 -0
  24. package/src/editor.module.css +8 -1
  25. package/src/extensions/Blocks/PreviousBlockTypePlugin.ts +7 -1
  26. package/src/extensions/Blocks/api/blockTypes.ts +90 -0
  27. package/src/extensions/Blocks/api/cursorPositionTypes.ts +5 -0
  28. package/src/extensions/Blocks/api/inlineContentTypes.ts +35 -0
  29. package/src/extensions/Blocks/helpers/getBlockInfoFromPos.ts +4 -4
  30. package/src/extensions/Blocks/nodes/Block.module.css +39 -36
  31. package/src/extensions/Blocks/nodes/BlockContainer.ts +74 -23
  32. package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +23 -5
  33. package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +28 -6
  34. package/src/extensions/DraggableBlocks/DraggableBlocksPlugin.ts +149 -87
  35. package/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.ts +2 -2
  36. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +3 -3
  37. package/src/extensions/SlashMenu/SlashMenuExtension.ts +7 -12
  38. package/src/extensions/SlashMenu/SlashMenuItem.ts +4 -1
  39. package/src/extensions/SlashMenu/{defaultCommands.tsx → defaultSlashCommands.tsx} +34 -17
  40. package/src/extensions/SlashMenu/index.ts +7 -4
  41. package/src/extensions/UniqueID/UniqueID.ts +1 -1
  42. package/src/index.ts +4 -2
  43. package/src/shared/utils.ts +6 -0
  44. package/types/src/BlockNoteEditor.d.ts +13 -4
  45. package/types/src/BlockNoteEditor.test.d.ts +1 -0
  46. package/types/src/BlockNoteExtensions.d.ts +7 -3
  47. package/types/src/api/Editor.d.ts +93 -0
  48. package/types/src/api/blockManipulation/blockManipulation.d.ts +6 -0
  49. package/types/src/api/blockManipulation/blockManipulation.test.d.ts +1 -0
  50. package/types/src/api/formatConversions/formatConversions.d.ts +6 -0
  51. package/types/src/api/formatConversions/formatConversions.test.d.ts +1 -0
  52. package/types/src/api/formatConversions/removeUnderlinesRehypePlugin.d.ts +6 -0
  53. package/types/src/api/formatConversions/simplifyBlocksRehypePlugin.d.ts +16 -0
  54. package/types/src/api/nodeConversions/nodeConversions.d.ts +15 -0
  55. package/types/src/api/nodeConversions/nodeConversions.test.d.ts +1 -0
  56. package/types/src/api/nodeConversions/testUtil.d.ts +2 -0
  57. package/types/src/api/util/nodeUtil.d.ts +8 -0
  58. package/types/src/extensions/Blocks/api/blockTypes.d.ts +37 -0
  59. package/types/src/extensions/Blocks/api/cursorPositionTypes.d.ts +4 -0
  60. package/types/src/extensions/Blocks/api/inlineContentTypes.d.ts +29 -0
  61. package/types/src/extensions/Blocks/nodes/BlockContainer.d.ts +3 -3
  62. package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +15 -0
  63. package/types/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.d.ts +2 -2
  64. package/types/src/extensions/SlashMenu/SlashMenuExtension.d.ts +1 -3
  65. package/types/src/extensions/SlashMenu/SlashMenuItem.d.ts +4 -1
  66. package/types/src/extensions/SlashMenu/index.d.ts +3 -3
  67. package/types/src/index.d.ts +4 -2
  68. package/types/src/shared/utils.d.ts +3 -0
  69. package/src/extensions/Blocks/apiTypes.ts +0 -48
  70. package/types/src/EditorElement.d.ts +0 -7
  71. package/types/src/api/Document.d.ts +0 -5
  72. package/types/src/extensions/Blocks/BlockAttributes.d.ts +0 -2
  73. package/types/src/extensions/Blocks/MultipleNodeSelection.d.ts +0 -24
  74. package/types/src/extensions/Blocks/apiTypes.d.ts +0 -16
  75. package/types/src/extensions/Blocks/nodes/Block.d.ts +0 -24
  76. package/types/src/extensions/Blocks/nodes/BlockContent/BlockContentTypes.d.ts +0 -4
  77. package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContentTypes.d.ts +0 -4
  78. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContentTypes.d.ts +0 -2
  79. package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContentTypes.d.ts +0 -2
  80. package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContentTypes.d.ts +0 -2
  81. package/types/src/extensions/Blocks/nodes/BlockTypes/HeadingBlock/HeadingContent.d.ts +0 -8
  82. package/types/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.d.ts +0 -8
  83. package/types/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.d.ts +0 -2
  84. package/types/src/extensions/Blocks/nodes/BlockTypes/TextBlock/TextContent.d.ts +0 -6
  85. package/types/src/extensions/BubbleMenu/BubbleMenuExtension.d.ts +0 -8
  86. package/types/src/extensions/BubbleMenu/BubbleMenuFactoryTypes.d.ts +0 -27
  87. package/types/src/extensions/BubbleMenu/BubbleMenuPlugin.d.ts +0 -44
  88. package/types/src/extensions/DraggableBlocks/BlockMenuFactoryTypes.d.ts +0 -12
  89. package/types/src/extensions/DraggableBlocks/DragMenuFactoryTypes.d.ts +0 -18
  90. package/types/src/extensions/Hyperlinks/HyperlinkMark.d.ts +0 -8
  91. package/types/src/extensions/Hyperlinks/HyperlinkMenuFactoryTypes.d.ts +0 -11
  92. package/types/src/extensions/Hyperlinks/HyperlinkMenuPlugin.d.ts +0 -11
  93. package/types/src/extensions/Paragraph/FixedParagraph.d.ts +0 -1
  94. package/types/src/extensions/SlashMenu/defaultCommands.d.ts +0 -8
  95. package/types/src/utils.d.ts +0 -2
@@ -0,0 +1,90 @@
1
+ /** Define the main block types **/
2
+
3
+ import { InlineContent, PartialInlineContent } from "./inlineContentTypes";
4
+
5
+ export type BlockTemplate<
6
+ // Type of the block.
7
+ // Examples might include: "paragraph", "heading", or "bulletListItem".
8
+ Type extends string,
9
+ // Changeable props which affect the block's behaviour or appearance.
10
+ // An example might be: { textAlignment: "left" | "right" | "center" } for a paragraph block.
11
+ Props extends Record<string, string>
12
+ > = {
13
+ id: string;
14
+ type: Type;
15
+ props: Props;
16
+ content: InlineContent[];
17
+ children: Block[];
18
+ };
19
+
20
+ export type DefaultBlockProps = {
21
+ backgroundColor: string;
22
+ textColor: string;
23
+ textAlignment: "left" | "center" | "right" | "justify";
24
+ };
25
+
26
+ export type NumberedListItemBlock = BlockTemplate<
27
+ "numberedListItem",
28
+ DefaultBlockProps
29
+ >;
30
+
31
+ export type BulletListItemBlock = BlockTemplate<
32
+ "bulletListItem",
33
+ DefaultBlockProps
34
+ >;
35
+
36
+ export type HeadingBlock = BlockTemplate<
37
+ "heading",
38
+ DefaultBlockProps & {
39
+ level: "1" | "2" | "3";
40
+ }
41
+ >;
42
+
43
+ export type ParagraphBlock = BlockTemplate<"paragraph", DefaultBlockProps>;
44
+
45
+ export type Block =
46
+ | ParagraphBlock
47
+ | HeadingBlock
48
+ | BulletListItemBlock
49
+ | NumberedListItemBlock;
50
+
51
+ export type BlockIdentifier = string | Block;
52
+
53
+ /** Define "Partial Blocks", these are for updating or creating blocks */
54
+ export type PartialBlockTemplate<B extends Block> = B extends Block
55
+ ? Partial<Omit<B, "props" | "children" | "content" | "type">> & {
56
+ type?: B["type"];
57
+ props?: Partial<B["props"]>;
58
+ content?: string | PartialInlineContent[];
59
+ children?: PartialBlock[];
60
+ }
61
+ : never;
62
+
63
+ export type PartialBlock = PartialBlockTemplate<Block>;
64
+
65
+ export type BlockPropsTemplate<Props> = Props extends Block["props"]
66
+ ? keyof Props
67
+ : never;
68
+
69
+ /**
70
+ * Expose blockProps. This is currently not very nice, but it's expected this
71
+ * will change anyway once we allow for custom blocks
72
+ */
73
+
74
+ export const globalProps: Array<keyof DefaultBlockProps> = [
75
+ "backgroundColor",
76
+ "textColor",
77
+ "textAlignment",
78
+ ];
79
+
80
+ export const blockProps: Record<Block["type"], Set<string>> = {
81
+ paragraph: new Set<keyof ParagraphBlock["props"]>([...globalProps]),
82
+ heading: new Set<keyof HeadingBlock["props"]>([
83
+ ...globalProps,
84
+ "level" as const,
85
+ ]),
86
+ numberedListItem: new Set<keyof NumberedListItemBlock["props"]>([
87
+ ...globalProps,
88
+ ]),
89
+ bulletListItem: new Set<keyof BulletListItemBlock["props"]>([...globalProps]),
90
+ };
@@ -0,0 +1,5 @@
1
+ import { Block } from "./blockTypes";
2
+
3
+ export type TextCursorPosition = {
4
+ block: Block;
5
+ };
@@ -0,0 +1,35 @@
1
+ export type Styles = {
2
+ bold?: true;
3
+ italic?: true;
4
+ underline?: true;
5
+ strike?: true;
6
+ textColor?: string;
7
+ backgroundColor?: string;
8
+ };
9
+
10
+ export type ToggledStyles = {
11
+ [K in keyof Styles]-?: Required<Styles>[K] extends true ? K : never;
12
+ }[keyof Styles];
13
+
14
+ export type ColorStyles = {
15
+ [K in keyof Styles]-?: Required<Styles>[K] extends string ? K : never;
16
+ }[keyof Styles];
17
+
18
+ export type StyledText = {
19
+ type: "text";
20
+ text: string;
21
+ styles: Styles;
22
+ };
23
+
24
+ export type Link = {
25
+ type: "link";
26
+ href: string;
27
+ content: StyledText[];
28
+ };
29
+
30
+ export type PartialLink = Omit<Link, "content"> & {
31
+ content: string | Link["content"];
32
+ };
33
+
34
+ export type InlineContent = StyledText | Link;
35
+ export type PartialInlineContent = StyledText | PartialLink;
@@ -22,7 +22,7 @@ export function getBlockInfoFromPos(
22
22
  doc: Node,
23
23
  posInBlock: number
24
24
  ): BlockInfo | undefined {
25
- if (posInBlock <= 0 || posInBlock > doc.nodeSize) {
25
+ if (posInBlock < 0 || posInBlock > doc.nodeSize) {
26
26
  return undefined;
27
27
  }
28
28
 
@@ -32,11 +32,11 @@ export function getBlockInfoFromPos(
32
32
  let node = $pos.node(maxDepth);
33
33
  let depth = maxDepth;
34
34
 
35
- while (depth >= 0) {
36
- // If the outermost node is not a block, it means the position does not lie within a block.
37
- if (depth === 0) {
35
+ while (true) {
36
+ if (depth < 0) {
38
37
  return undefined;
39
38
  }
39
+
40
40
  if (node.type.name === "blockContainer") {
41
41
  break;
42
42
  }
@@ -35,6 +35,7 @@ BASIC STYLES
35
35
  .blockContent h2,
36
36
  .blockContent h3,
37
37
  .blockContent li {
38
+ all: unset;
38
39
  margin: 0;
39
40
  padding: 0;
40
41
  font-size: inherit;
@@ -150,75 +151,79 @@ NESTED BLOCKS
150
151
 
151
152
  /* Ordered */
152
153
  [data-content-type="numberedListItem"] {
153
- --index: attr(data-index)
154
+ --index: attr(data-index);
154
155
  }
155
156
 
156
157
  [data-prev-type="numberedListItem"] {
157
- --prev-index: attr(data-prev-index)
158
+ --prev-index: attr(data-prev-index);
158
159
  }
159
160
 
160
161
  .blockOuter[data-prev-type="numberedListItem"]:not([data-prev-index="none"])
161
- > .block
162
- > .blockContent::before {
162
+ > .block
163
+ > .blockContent::before {
163
164
  margin-right: 1.2em;
164
- content: var(--prev-index)".";
165
+ content: var(--prev-index) ".";
165
166
  }
166
167
 
167
168
  .blockOuter:not([data-prev-type])
168
- > .block
169
- > .blockContent[data-content-type="numberedListItem"]::before {
169
+ > .block
170
+ > .blockContent[data-content-type="numberedListItem"]::before {
170
171
  margin-right: 1.2em;
171
- content: var(--index)".";
172
+ content: var(--index) ".";
172
173
  }
173
174
 
174
175
  /* Unordered */
175
176
  /* No list nesting */
176
- .blockOuter[data-prev-type="bulletListItem"]
177
- > .block
178
- > .blockContent::before {
177
+ .blockOuter[data-prev-type="bulletListItem"] > .block > .blockContent::before {
179
178
  margin-right: 1.2em;
180
179
  content: "•";
181
180
  }
182
181
 
183
182
  .blockOuter:not([data-prev-type])
184
- > .block
185
- > .blockContent[data-content-type="bulletListItem"]::before {
183
+ > .block
184
+ > .blockContent[data-content-type="bulletListItem"]::before {
186
185
  margin-right: 1.2em;
187
186
  content: "•";
188
187
  }
189
188
 
190
189
  /* 1 level of list nesting */
191
- [data-content-type="bulletListItem"]~.blockGroup
192
- > .blockOuter[data-prev-type="bulletListItem"]
193
- > .block
194
- > .blockContent::before {
190
+ [data-content-type="bulletListItem"]
191
+ ~ .blockGroup
192
+ > .blockOuter[data-prev-type="bulletListItem"]
193
+ > .block
194
+ > .blockContent::before {
195
195
  margin-right: 1.2em;
196
196
  content: "◦";
197
197
  }
198
198
 
199
- [data-content-type="bulletListItem"]~.blockGroup
200
- > .blockOuter:not([data-prev-type])
201
- > .block
202
- > .blockContent[data-content-type="bulletListItem"]::before {
199
+ [data-content-type="bulletListItem"]
200
+ ~ .blockGroup
201
+ > .blockOuter:not([data-prev-type])
202
+ > .block
203
+ > .blockContent[data-content-type="bulletListItem"]::before {
203
204
  margin-right: 1.2em;
204
205
  content: "◦";
205
206
  }
206
207
 
207
208
  /* 2 levels of list nesting */
208
- [data-content-type="bulletListItem"]~.blockGroup
209
- [data-content-type="bulletListItem"]~.blockGroup
210
- > .blockOuter[data-prev-type="bulletListItem"]
211
- > .block
212
- > .blockContent::before {
209
+ [data-content-type="bulletListItem"]
210
+ ~ .blockGroup
211
+ [data-content-type="bulletListItem"]
212
+ ~ .blockGroup
213
+ > .blockOuter[data-prev-type="bulletListItem"]
214
+ > .block
215
+ > .blockContent::before {
213
216
  margin-right: 1.2em;
214
217
  content: "▪";
215
218
  }
216
219
 
217
- [data-content-type="bulletListItem"]~.blockGroup
218
- [data-content-type="bulletListItem"]~.blockGroup
219
- > .blockOuter:not([data-prev-type])
220
- > .block
221
- > .blockContent[data-content-type="bulletListItem"]::before {
220
+ [data-content-type="bulletListItem"]
221
+ ~ .blockGroup
222
+ [data-content-type="bulletListItem"]
223
+ ~ .blockGroup
224
+ > .blockOuter:not([data-prev-type])
225
+ > .block
226
+ > .blockContent[data-content-type="bulletListItem"]::before {
222
227
  margin-right: 1.2em;
223
228
  content: "▪";
224
229
  }
@@ -251,15 +256,13 @@ NESTED BLOCKS
251
256
  content: "Type to filter";
252
257
  }
253
258
 
254
- .blockContent[data-content-type="heading"].isEmpty
255
- > :first-child::before {
259
+ .blockContent[data-content-type="heading"].isEmpty > :first-child::before {
256
260
  content: "Heading";
257
261
  }
258
262
 
259
- .blockContent[data-content-type="bulletListItem"].isEmpty
260
- > :first-child:before,
263
+ .blockContent[data-content-type="bulletListItem"].isEmpty > :first-child:before,
261
264
  .blockContent[data-content-type="numberedListItem"].isEmpty
262
- > :first-child:before {
265
+ > :first-child:before {
263
266
  content: "List";
264
267
  }
265
268
 
@@ -1,7 +1,11 @@
1
1
  import { mergeAttributes, Node } from "@tiptap/core";
2
- import { Fragment, Slice } from "prosemirror-model";
2
+ import { Fragment, Node as PMNode, Slice } from "prosemirror-model";
3
3
  import { TextSelection } from "prosemirror-state";
4
- import { BlockUpdate } from "../apiTypes";
4
+ import {
5
+ blockToNode,
6
+ inlineContentToNodes,
7
+ } from "../../../api/nodeConversions/nodeConversions";
8
+ import { PartialBlock } from "../api/blockTypes";
5
9
  import { getBlockInfoFromPos } from "../helpers/getBlockInfoFromPos";
6
10
  import { PreviousBlockTypePlugin } from "../PreviousBlockTypePlugin";
7
11
  import styles from "./Block.module.css";
@@ -19,13 +23,10 @@ declare module "@tiptap/core" {
19
23
  BNDeleteBlock: (posInBlock: number) => ReturnType;
20
24
  BNMergeBlocks: (posBetweenBlocks: number) => ReturnType;
21
25
  BNSplitBlock: (posInBlock: number, keepType: boolean) => ReturnType;
22
- BNUpdateBlock: (
23
- posInBlock: number,
24
- blockUpdate: BlockUpdate
25
- ) => ReturnType;
26
+ BNUpdateBlock: (posInBlock: number, block: PartialBlock) => ReturnType;
26
27
  BNCreateOrUpdateBlock: (
27
28
  posInBlock: number,
28
- blockUpdate: BlockUpdate
29
+ block: PartialBlock
29
30
  ) => ReturnType;
30
31
  };
31
32
  }
@@ -197,8 +198,7 @@ export const BlockContainer = Node.create<IBlock>({
197
198
  }
198
199
 
199
200
  // Deletes next block and adds its text content to the nearest previous block.
200
- // TODO: Is there any situation where we need the whole block content, not just text? Implementation for this
201
- // is trickier.
201
+ // TODO: Use slices.
202
202
  if (dispatch) {
203
203
  state.tr.deleteRange(startPos, startPos + contentNode.nodeSize);
204
204
  state.tr.insertText(contentNode.textContent, prevBlockEndPos - 1);
@@ -283,35 +283,86 @@ export const BlockContainer = Node.create<IBlock>({
283
283
 
284
284
  return true;
285
285
  },
286
- // Updates the type and attributes of a block at a given position.
286
+ // Updates a block to the given specification.
287
287
  BNUpdateBlock:
288
- (posInBlock, blockUpdate) =>
288
+ (posInBlock, block) =>
289
289
  ({ state, dispatch }) => {
290
290
  const blockInfo = getBlockInfoFromPos(state.doc, posInBlock);
291
291
  if (blockInfo === undefined) {
292
292
  return false;
293
293
  }
294
294
 
295
- const { node, startPos, contentNode } = blockInfo;
295
+ const { startPos, endPos, node, contentNode } = blockInfo;
296
296
 
297
297
  if (dispatch) {
298
- state.tr.setBlockType(
299
- startPos + 1,
300
- startPos + contentNode.nodeSize + 1,
301
- state.schema.node(blockUpdate.type).type,
298
+ // Adds blockGroup node with child blocks if necessary.
299
+ if (block.children !== undefined) {
300
+ const childNodes = [];
301
+
302
+ // Creates ProseMirror nodes for each child block, including their descendants.
303
+ for (const child of block.children) {
304
+ childNodes.push(blockToNode(child, state.schema));
305
+ }
306
+
307
+ // Checks if a blockGroup node already exists.
308
+ if (node.childCount === 2) {
309
+ // Replaces all child nodes in the existing blockGroup with the ones created earlier.
310
+ state.tr.replace(
311
+ startPos + contentNode.nodeSize + 1,
312
+ endPos - 1,
313
+ new Slice(Fragment.from(childNodes), 0, 0)
314
+ );
315
+ } else {
316
+ // Inserts a new blockGroup containing the child nodes created earlier.
317
+ state.tr.insert(
318
+ startPos + contentNode.nodeSize,
319
+ state.schema.nodes["blockGroup"].create({}, childNodes)
320
+ );
321
+ }
322
+ }
323
+
324
+ // Replaces the blockContent node's content if necessary.
325
+ if (block.content !== undefined) {
326
+ let content: PMNode[] = [];
327
+
328
+ // Checks if the provided content is a string or InlineContent[] type.
329
+ if (typeof block.content === "string") {
330
+ // Adds a single text node with no marks to the content.
331
+ content.push(state.schema.text(block.content));
332
+ } else {
333
+ // Adds a text node with the provided styles converted into marks to the content, for each InlineContent
334
+ // object.
335
+ content = inlineContentToNodes(block.content, state.schema);
336
+ }
337
+
338
+ // Replaces the contents of the blockContent node with the previously created text node(s).
339
+ state.tr.replace(
340
+ startPos + 1,
341
+ startPos + contentNode.nodeSize - 1,
342
+ new Slice(Fragment.from(content), 0, 0)
343
+ );
344
+ }
345
+
346
+ // Changes the block type and adds the provided props as node attributes. Also preserves all existing node
347
+ // attributes that are compatible with the new type.
348
+ state.tr.setNodeMarkup(
349
+ startPos,
350
+ block.type === undefined
351
+ ? undefined
352
+ : state.schema.nodes[block.type],
302
353
  {
303
- ...node.attrs,
304
- ...blockUpdate.props,
354
+ ...contentNode.attrs,
355
+ ...block.props,
305
356
  }
306
357
  );
307
358
  }
308
359
 
309
360
  return true;
310
361
  },
311
- // Changes the block at a given position to a given content type if it's empty, otherwise creates a new block of
312
- // that type below it.
362
+ // Updates a block to the given specification if it's empty, otherwise creates a new block from that specification
363
+ // below it.
313
364
  BNCreateOrUpdateBlock:
314
- (posInBlock, blockUpdate) =>
365
+ (posInBlock, block) =>
315
366
  ({ state, chain }) => {
316
367
  const blockInfo = getBlockInfoFromPos(state.doc, posInBlock);
317
368
  if (blockInfo === undefined) {
@@ -324,7 +375,7 @@ export const BlockContainer = Node.create<IBlock>({
324
375
  const oldBlockContentPos = startPos + 1;
325
376
 
326
377
  return chain()
327
- .BNUpdateBlock(posInBlock, blockUpdate)
378
+ .BNUpdateBlock(posInBlock, block)
328
379
  .setTextSelection(oldBlockContentPos)
329
380
  .run();
330
381
  } else {
@@ -333,7 +384,7 @@ export const BlockContainer = Node.create<IBlock>({
333
384
 
334
385
  return chain()
335
386
  .BNCreateBlock(newBlockInsertionPos)
336
- .BNUpdateBlock(newBlockContentPos, blockUpdate)
387
+ .BNUpdateBlock(newBlockContentPos, block)
337
388
  .setTextSelection(newBlockContentPos)
338
389
  .run();
339
390
  }
@@ -33,6 +33,7 @@ export const BulletListItemBlockContent = Node.create({
33
33
 
34
34
  parseHTML() {
35
35
  return [
36
+ // Case for regular HTML list structure.
36
37
  {
37
38
  tag: "li",
38
39
  getAttrs: (element) => {
@@ -46,18 +47,35 @@ export const BulletListItemBlockContent = Node.create({
46
47
  return false;
47
48
  }
48
49
 
49
- // Case for BlockNote list structure.
50
- if (parent.getAttribute("data-content-type") === "bulletListItem") {
50
+ if (parent.tagName === "UL") {
51
51
  return {};
52
52
  }
53
53
 
54
- // Case for regular HTML list structure.
55
- if (parent.tagName === "UL") {
54
+ return false;
55
+ },
56
+ node: "blockContainer",
57
+ },
58
+ // Case for BlockNote list structure.
59
+ {
60
+ tag: "p",
61
+ getAttrs: (element) => {
62
+ if (typeof element === "string") {
63
+ return false;
64
+ }
65
+
66
+ const parent = element.parentElement;
67
+
68
+ if (parent === null) {
69
+ return false;
70
+ }
71
+
72
+ if (parent.getAttribute("data-content-type") === "bulletListItem") {
56
73
  return {};
57
74
  }
58
75
 
59
76
  return false;
60
77
  },
78
+ priority: 300,
61
79
  node: "blockContainer",
62
80
  },
63
81
  ];
@@ -70,7 +88,7 @@ export const BulletListItemBlockContent = Node.create({
70
88
  class: styles.blockContent,
71
89
  "data-content-type": this.name,
72
90
  }),
73
- ["li", 0],
91
+ ["p", 0],
74
92
  ];
75
93
  },
76
94
  });
@@ -1,7 +1,7 @@
1
1
  import { InputRule, mergeAttributes, Node } from "@tiptap/core";
2
- import { NumberedListIndexingPlugin } from "./NumberedListIndexingPlugin";
3
2
  import styles from "../../../Block.module.css";
4
3
  import { handleEnter } from "../ListItemKeyboardShortcuts";
4
+ import { NumberedListIndexingPlugin } from "./NumberedListIndexingPlugin";
5
5
 
6
6
  export const NumberedListItemBlockContent = Node.create({
7
7
  name: "numberedListItem",
@@ -52,6 +52,8 @@ export const NumberedListItemBlockContent = Node.create({
52
52
 
53
53
  parseHTML() {
54
54
  return [
55
+ // Case for regular HTML list structure.
56
+ // (e.g.: when pasting from other apps)
55
57
  {
56
58
  tag: "li",
57
59
  getAttrs: (element) => {
@@ -65,18 +67,36 @@ export const NumberedListItemBlockContent = Node.create({
65
67
  return false;
66
68
  }
67
69
 
68
- // Case for BlockNote list structure.
69
- if (parent.getAttribute("data-content-type") === "numberedListItem") {
70
+ if (parent.tagName === "OL") {
70
71
  return {};
71
72
  }
72
73
 
73
- // Case for regular HTML list structure.
74
- if (parent.tagName === "OL") {
74
+ return false;
75
+ },
76
+ node: "blockContainer",
77
+ },
78
+ // Case for BlockNote list structure.
79
+ // (e.g.: when pasting from blocknote)
80
+ {
81
+ tag: "p",
82
+ getAttrs: (element) => {
83
+ if (typeof element === "string") {
84
+ return false;
85
+ }
86
+
87
+ const parent = element.parentElement;
88
+
89
+ if (parent === null) {
90
+ return false;
91
+ }
92
+
93
+ if (parent.getAttribute("data-content-type") === "numberedListItem") {
75
94
  return {};
76
95
  }
77
96
 
78
97
  return false;
79
98
  },
99
+ priority: 300,
80
100
  node: "blockContainer",
81
101
  },
82
102
  ];
@@ -89,7 +109,9 @@ export const NumberedListItemBlockContent = Node.create({
89
109
  class: styles.blockContent,
90
110
  "data-content-type": this.name,
91
111
  }),
92
- ["li", 0],
112
+ // we use a <p> tag, because for <li> tags we'd need to add a <ul> parent for around siblings to be semantically correct,
113
+ // which would be quite cumbersome
114
+ ["p", 0],
93
115
  ];
94
116
  },
95
117
  });