@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.
- package/dist/blocknote.js +696 -691
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +1 -1
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +6 -4
- package/src/BlockNoteExtensions.ts +1 -10
- package/src/extensions/Blocks/PreviousBlockTypePlugin.ts +22 -26
- package/src/extensions/Blocks/apiTypes.ts +48 -0
- package/src/extensions/Blocks/helpers/findBlock.ts +3 -1
- package/src/extensions/Blocks/helpers/getBlockInfoFromPos.ts +1 -1
- package/src/extensions/Blocks/index.ts +10 -8
- package/src/extensions/Blocks/nodes/Block.module.css +31 -29
- package/src/extensions/Blocks/{BlockAttributes.ts → nodes/BlockAttributes.ts} +0 -0
- package/src/extensions/Blocks/nodes/{Block.ts → BlockContainer.ts} +75 -94
- package/src/extensions/Blocks/nodes/{BlockTypes/HeadingBlock/HeadingContent.ts → BlockContent/HeadingBlockContent/HeadingBlockContent.ts} +16 -24
- package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +76 -0
- package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/ListItemKeyboardShortcuts.ts +47 -0
- package/src/extensions/Blocks/nodes/{BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.ts → BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts} +10 -14
- package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +95 -0
- package/src/extensions/Blocks/nodes/{BlockTypes/TextBlock/TextContent.ts → BlockContent/ParagraphBlockContent/ParagraphBlockContent.ts} +3 -8
- package/src/extensions/Blocks/nodes/BlockGroup.ts +4 -4
- package/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.ts +1 -1
- package/src/extensions/DraggableBlocks/DraggableBlocksPlugin.ts +7 -7
- package/src/extensions/{Blocks → DraggableBlocks}/MultipleNodeSelection.ts +0 -0
- package/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.ts +4 -7
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +17 -9
- package/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.ts +1 -1
- package/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.ts +3 -1
- package/src/extensions/SlashMenu/defaultCommands.tsx +22 -23
- package/src/extensions/TrailingNode/TrailingNodeExtension.ts +4 -4
- package/src/extensions/UniqueID/UniqueID.ts +6 -0
- package/src/index.ts +2 -1
- package/src/shared/EditorElement.ts +12 -6
- package/src/shared/plugins/suggestion/SuggestionPlugin.ts +2 -2
- package/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.ts +1 -1
- package/types/src/BlockNoteEditor.d.ts +1 -1
- package/types/src/BlockNoteExtensions.d.ts +1 -3
- package/types/src/extensions/Blocks/apiTypes.d.ts +16 -0
- package/types/src/extensions/Blocks/helpers/getBlockInfoFromPos.d.ts +1 -1
- package/types/src/extensions/Blocks/nodes/BlockAttributes.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContainer.d.ts +21 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/BlockContentTypes.d.ts +4 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContent.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/HeadingBlockContent/HeadingBlockContentTypes.d.ts +4 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContentTypes.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/ListItemKeyboardShortcuts.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContentTypes.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContent.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockContent/ParagraphBlockContent/ParagraphBlockContentTypes.d.ts +2 -0
- package/types/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.d.ts +5 -5
- package/types/src/extensions/DraggableBlocks/DraggableBlocksExtension.d.ts +1 -1
- package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +2 -2
- package/types/src/extensions/DraggableBlocks/MultipleNodeSelection.d.ts +24 -0
- package/types/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.d.ts +8 -8
- package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +1 -1
- package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.d.ts +5 -5
- package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.d.ts +2 -2
- package/types/src/extensions/SlashMenu/SlashMenuExtension.d.ts +1 -1
- package/types/src/extensions/SlashMenu/SlashMenuItem.d.ts +1 -1
- package/types/src/index.d.ts +2 -1
- package/types/src/shared/EditorElement.d.ts +6 -2
- package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +3 -3
- package/types/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.d.ts +5 -5
- package/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.ts +0 -177
- 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
|
|
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
|
|
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
|
-
|
|
22
|
+
BNUpdateBlock: (
|
|
29
23
|
posInBlock: number,
|
|
30
|
-
|
|
24
|
+
blockUpdate: BlockUpdate
|
|
31
25
|
) => ReturnType;
|
|
32
|
-
|
|
26
|
+
BNCreateOrUpdateBlock: (
|
|
33
27
|
posInBlock: number,
|
|
34
|
-
|
|
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
|
|
44
|
-
name: "
|
|
45
|
-
group: "
|
|
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") === "
|
|
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(
|
|
92
|
+
mergeAttributes(HTMLAttributes, {
|
|
107
93
|
class: styles.blockOuter,
|
|
108
94
|
"data-node-type": "block-outer",
|
|
109
95
|
}),
|
|
110
96
|
[
|
|
111
97
|
"div",
|
|
112
|
-
mergeAttributes(
|
|
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 =
|
|
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
|
-
"
|
|
166
|
+
"blockContainer";
|
|
180
167
|
const prevNodeIsBlock =
|
|
181
168
|
state.doc.resolve(posBetweenBlocks - 1).node().type.name ===
|
|
182
|
-
"
|
|
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 =
|
|
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
|
-
|
|
287
|
-
(posInBlock,
|
|
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
|
|
301
|
-
|
|
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
|
-
|
|
310
|
-
(posInBlock,
|
|
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
|
-
.
|
|
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
|
-
.
|
|
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
|
-
//
|
|
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
|
|
353
|
+
const isParagraph = contentType.name === "paragraph";
|
|
363
354
|
|
|
364
|
-
if (selectionAtBlockStart && !
|
|
365
|
-
return commands.
|
|
366
|
-
|
|
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("
|
|
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("
|
|
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
|
-
|
|
500
|
-
|
|
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.
|
|
507
|
-
|
|
508
|
-
{
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
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.
|
|
517
|
-
|
|
518
|
-
{
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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.
|
|
527
|
-
|
|
528
|
-
{
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
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.
|
|
537
|
-
|
|
538
|
-
{
|
|
539
|
-
|
|
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.
|
|
547
|
-
|
|
548
|
-
{
|
|
549
|
-
|
|
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
|
|
5
|
-
name: "
|
|
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
|
-
|
|
11
|
+
level: {
|
|
19
12
|
default: "1",
|
|
20
13
|
// instead of "level" attributes, use "data-level"
|
|
21
|
-
parseHTML: (element) => element.getAttribute("data-
|
|
14
|
+
parseHTML: (element) => element.getAttribute("data-level"),
|
|
22
15
|
renderHTML: (attributes) => {
|
|
23
16
|
return {
|
|
24
|
-
"data-
|
|
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
|
-
.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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: {
|
|
58
|
-
node: "
|
|
50
|
+
attrs: { level: "1" },
|
|
51
|
+
node: "blockContainer",
|
|
59
52
|
},
|
|
60
53
|
{
|
|
61
54
|
tag: "h2",
|
|
62
|
-
attrs: {
|
|
63
|
-
node: "
|
|
55
|
+
attrs: { level: "2" },
|
|
56
|
+
node: "blockContainer",
|
|
64
57
|
},
|
|
65
58
|
{
|
|
66
59
|
tag: "h3",
|
|
67
|
-
attrs: {
|
|
68
|
-
node: "
|
|
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
|
|
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
|
+
});
|
package/src/extensions/Blocks/nodes/BlockContent/ListItemBlockContent/ListItemKeyboardShortcuts.ts
ADDED
|
@@ -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 "
|
|
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(`
|
|
6
|
-
export const
|
|
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("
|
|
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 === "
|
|
21
|
-
node.firstChild!.type.name === "
|
|
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 === "
|
|
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["
|
|
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
|
-
|
|
68
|
-
listItemIndex: newIndex,
|
|
64
|
+
index: newIndex,
|
|
69
65
|
});
|
|
70
66
|
}
|
|
71
67
|
}
|