@blocknote/core 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -6
- package/dist/blocknote.js +1424 -5109
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +1 -53
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -16
- package/src/BlockNoteEditor.ts +54 -0
- package/src/BlockNoteExtensions.ts +52 -7
- package/src/assets/fonts-inter.css +92 -0
- package/src/editor.module.css +37 -0
- package/src/extensions/Blocks/BlockAttributes.ts +1 -3
- package/src/extensions/Blocks/PreviousBlockTypePlugin.ts +71 -18
- package/src/extensions/Blocks/helpers/getBlockInfoFromPos.ts +66 -0
- package/src/extensions/Blocks/index.ts +7 -3
- package/src/extensions/Blocks/nodes/Block.module.css +116 -74
- package/src/extensions/Blocks/nodes/Block.ts +413 -292
- package/src/extensions/Blocks/nodes/BlockGroup.ts +6 -6
- package/src/extensions/Blocks/nodes/BlockTypes/HeadingBlock/HeadingContent.ts +84 -0
- package/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.ts +177 -0
- package/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.ts +77 -0
- package/src/extensions/Blocks/nodes/BlockTypes/TextBlock/TextContent.ts +34 -0
- package/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.ts +20 -0
- package/src/extensions/DraggableBlocks/DraggableBlocksExtension.ts +27 -9
- package/src/extensions/DraggableBlocks/{DraggableBlocksPlugin.tsx → DraggableBlocksPlugin.ts} +227 -147
- package/src/extensions/FormattingToolbar/FormattingToolbarExtension.ts +29 -0
- package/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.ts +35 -0
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +308 -0
- package/src/extensions/HyperlinkToolbar/HyperlinkMark.ts +28 -0
- package/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.ts +19 -0
- package/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.ts +251 -0
- package/src/extensions/Placeholder/PlaceholderExtension.ts +2 -2
- package/src/extensions/SlashMenu/SlashMenuExtension.ts +9 -1
- package/src/extensions/SlashMenu/SlashMenuItem.ts +1 -3
- package/src/extensions/SlashMenu/defaultCommands.tsx +33 -22
- package/src/extensions/TrailingNode/TrailingNodeExtension.ts +4 -4
- package/src/extensions/UniqueID/UniqueID.ts +14 -1
- package/src/index.ts +8 -4
- package/src/shared/EditorElement.ts +10 -0
- package/src/shared/plugins/suggestion/SuggestionItem.ts +1 -8
- package/src/shared/plugins/suggestion/SuggestionPlugin.ts +222 -101
- package/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.ts +21 -0
- package/src/{utils.ts → shared/utils.ts} +0 -0
- package/types/src/BlockNoteEditor.d.ts +13 -0
- package/types/src/BlockNoteExtensions.d.ts +12 -1
- package/types/src/EditorElement.d.ts +7 -0
- package/types/src/extensions/Blocks/PreviousBlockTypePlugin.d.ts +1 -1
- package/types/src/extensions/Blocks/helpers/getBlockInfoFromPos.d.ts +19 -0
- package/types/src/extensions/Blocks/nodes/Block.d.ts +11 -19
- package/types/src/extensions/Blocks/nodes/BlockTypes/HeadingBlock/HeadingContent.d.ts +8 -0
- package/types/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/ListItemContent.d.ts +8 -0
- package/types/src/extensions/Blocks/nodes/BlockTypes/ListItemBlock/OrderedListItemIndexPlugin.d.ts +2 -0
- package/types/src/extensions/Blocks/nodes/BlockTypes/TextBlock/TextContent.d.ts +6 -0
- package/types/src/extensions/BubbleMenu/BubbleMenuExtension.d.ts +4 -1
- package/types/src/extensions/BubbleMenu/BubbleMenuFactoryTypes.d.ts +27 -0
- package/types/src/extensions/BubbleMenu/BubbleMenuPlugin.d.ts +10 -12
- package/types/src/extensions/DraggableBlocks/BlockMenuFactoryTypes.d.ts +12 -0
- package/types/src/extensions/DraggableBlocks/BlockSideMenuFactoryTypes.d.ts +14 -0
- package/types/src/extensions/DraggableBlocks/DragMenuFactoryTypes.d.ts +18 -0
- package/types/src/extensions/DraggableBlocks/DraggableBlocksExtension.d.ts +9 -3
- package/types/src/extensions/DraggableBlocks/DraggableBlocksPlugin.d.ts +23 -1
- package/types/src/extensions/FormattingToolbar/FormattingToolbarExtension.d.ts +8 -0
- package/types/src/extensions/FormattingToolbar/FormattingToolbarFactoryTypes.d.ts +23 -0
- package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +43 -0
- package/types/src/extensions/HyperlinkToolbar/HyperlinkMark.d.ts +8 -0
- package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarFactoryTypes.d.ts +12 -0
- package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.d.ts +11 -0
- package/types/src/extensions/Hyperlinks/HyperlinkMark.d.ts +2 -1
- package/types/src/extensions/Hyperlinks/HyperlinkMenuFactoryTypes.d.ts +11 -0
- package/types/src/extensions/Hyperlinks/HyperlinkMenuPlugin.d.ts +10 -1
- package/types/src/extensions/SlashMenu/SlashMenuExtension.d.ts +3 -1
- package/types/src/extensions/SlashMenu/SlashMenuItem.d.ts +2 -4
- package/types/src/index.d.ts +8 -3
- package/types/src/shared/EditorElement.d.ts +6 -0
- package/types/src/shared/plugins/suggestion/SuggestionItem.d.ts +1 -6
- package/types/src/shared/plugins/suggestion/SuggestionPlugin.d.ts +15 -10
- package/types/src/shared/plugins/suggestion/SuggestionsMenuFactoryTypes.d.ts +12 -0
- package/types/src/shared/utils.d.ts +2 -0
- package/types/src/utils.d.ts +2 -2
- package/src/BlockNoteTheme.ts +0 -150
- package/src/EditorContent.tsx +0 -2
- package/src/extensions/Blocks/OrderedListPlugin.ts +0 -46
- package/src/extensions/Blocks/commands/joinBackward.ts +0 -274
- package/src/extensions/Blocks/helpers/setBlockHeading.ts +0 -30
- package/src/extensions/Blocks/nodes/Content.ts +0 -63
- package/src/extensions/Blocks/rule.ts +0 -48
- package/src/extensions/BubbleMenu/BubbleMenuExtension.tsx +0 -36
- package/src/extensions/BubbleMenu/BubbleMenuPlugin.ts +0 -245
- package/src/extensions/BubbleMenu/component/BubbleMenu.tsx +0 -240
- package/src/extensions/BubbleMenu/component/LinkToolbarButton.tsx +0 -67
- package/src/extensions/DraggableBlocks/components/DragHandle.tsx +0 -102
- package/src/extensions/DraggableBlocks/components/DragHandleMenu.tsx +0 -19
- package/src/extensions/Hyperlinks/HyperlinkMark.tsx +0 -16
- package/src/extensions/Hyperlinks/HyperlinkMenuPlugin.tsx +0 -165
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenu.tsx +0 -44
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItem.tsx +0 -34
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemIcon.tsx +0 -31
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemInput.tsx +0 -40
- package/src/extensions/Hyperlinks/menus/HoverHyperlinkMenu.tsx +0 -37
- package/src/extensions/Hyperlinks/menus/HyperlinkMenu.tsx +0 -63
- package/src/fonts-inter.css +0 -94
- package/src/globals.css +0 -28
- package/src/root.module.css +0 -19
- package/src/shared/components/toolbar/Toolbar.tsx +0 -10
- package/src/shared/components/toolbar/ToolbarButton.tsx +0 -57
- package/src/shared/components/toolbar/ToolbarDropdown.tsx +0 -35
- package/src/shared/components/toolbar/ToolbarDropdownItem.tsx +0 -35
- package/src/shared/components/toolbar/ToolbarDropdownTarget.tsx +0 -31
- package/src/shared/components/tooltip/TooltipContent.module.css +0 -15
- package/src/shared/components/tooltip/TooltipContent.tsx +0 -23
- package/src/shared/hooks/useEditorForceUpdate.tsx +0 -30
- package/src/shared/plugins/suggestion/SuggestionListReactRenderer.tsx +0 -236
- package/src/shared/plugins/suggestion/components/SuggestionGroup.tsx +0 -47
- package/src/shared/plugins/suggestion/components/SuggestionGroupItem.tsx +0 -82
- package/src/shared/plugins/suggestion/components/SuggestionList.tsx +0 -92
- package/src/useEditor.ts +0 -51
- package/types/src/BlockNoteTheme.d.ts +0 -2
- package/types/src/EditorContent.d.ts +0 -1
- package/types/src/commands/indentation.d.ts +0 -2
- package/types/src/extensions/Blocks/OrderedListPlugin.d.ts +0 -2
- package/types/src/extensions/Blocks/commands/joinBackward.d.ts +0 -14
- package/types/src/extensions/Blocks/helpers/setBlockHeading.d.ts +0 -5
- package/types/src/extensions/Blocks/nodes/Content.d.ts +0 -5
- package/types/src/extensions/Blocks/rule.d.ts +0 -16
- package/types/src/extensions/BubbleMenu/component/BubbleMenu.d.ts +0 -5
- package/types/src/extensions/BubbleMenu/component/DropdownBlockItem.d.ts +0 -10
- package/types/src/extensions/BubbleMenu/component/LinkToolbarButton.d.ts +0 -11
- package/types/src/extensions/DraggableBlocks/components/DragHandle.d.ts +0 -12
- package/types/src/extensions/DraggableBlocks/components/DragHandleMenu.d.ts +0 -6
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenu.d.ts +0 -11
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItem.d.ts +0 -13
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemIcon.d.ts +0 -8
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemInput.d.ts +0 -9
- package/types/src/extensions/Hyperlinks/menus/HoverHyperlinkMenu.d.ts +0 -12
- package/types/src/extensions/Hyperlinks/menus/HyperlinkBasicMenu.d.ts +0 -12
- package/types/src/extensions/Hyperlinks/menus/HyperlinkEditMenu.d.ts +0 -10
- package/types/src/extensions/Hyperlinks/menus/HyperlinkMenu.d.ts +0 -21
- package/types/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInput.d.ts +0 -39
- package/types/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInputStyles.d.ts +0 -1
- package/types/src/extensions/Hyperlinks/menus/atlaskit/ToolbarComponent.d.ts +0 -11
- package/types/src/extensions/Hyperlinks/menus/helpers/PanelTextInput.d.ts +0 -39
- package/types/src/extensions/Hyperlinks/menus/helpers/PanelTextInputStyles.d.ts +0 -3
- package/types/src/extensions/Hyperlinks/menus/helpers/ToolbarComponent.d.ts +0 -13
- package/types/src/extensions/helpers/formatKeyboardShortcut.d.ts +0 -1
- package/types/src/lib/atlaskit/browser.d.ts +0 -12
- package/types/src/nodes/ChildgroupNode.d.ts +0 -28
- package/types/src/nodes/patchNodes.d.ts +0 -1
- package/types/src/plugins/TreeViewPlugin/index.d.ts +0 -2
- package/types/src/plugins/animation.d.ts +0 -2
- package/types/src/react/BlockNoteComposer.d.ts +0 -17
- package/types/src/react/BlockNotePlugin.d.ts +0 -1
- package/types/src/react/index.d.ts +0 -3
- package/types/src/react/useBlockNoteSetup.d.ts +0 -2
- package/types/src/registerBlockNote.d.ts +0 -2
- package/types/src/shared/components/toolbar/SimpleToolbarButton.d.ts +0 -15
- package/types/src/shared/components/toolbar/SimpleToolbarDropdown.d.ts +0 -11
- package/types/src/shared/components/toolbar/SimpleToolbarDropdownItem.d.ts +0 -11
- package/types/src/shared/components/toolbar/Toolbar.d.ts +0 -4
- package/types/src/shared/components/toolbar/ToolbarButton.d.ts +0 -15
- package/types/src/shared/components/toolbar/ToolbarDropdown.d.ts +0 -17
- package/types/src/shared/components/toolbar/ToolbarDropdownItem.d.ts +0 -11
- package/types/src/shared/components/toolbar/ToolbarDropdownTarget.d.ts +0 -8
- package/types/src/shared/components/toolbar/ToolbarSeparator.d.ts +0 -2
- package/types/src/shared/components/tooltip/TooltipContent.d.ts +0 -15
- package/types/src/shared/hooks/useEditorForceUpdate.d.ts +0 -2
- package/types/src/shared/plugins/suggestion/SuggestionListReactRenderer.d.ts +0 -71
- package/types/src/shared/plugins/suggestion/components/SuggestionGroup.d.ts +0 -23
- package/types/src/shared/plugins/suggestion/components/SuggestionGroupItem.d.ts +0 -9
- package/types/src/shared/plugins/suggestion/components/SuggestionList.d.ts +0 -11
- package/types/src/themes/BlockNoteEditorTheme.d.ts +0 -11
- package/types/src/useEditor.d.ts +0 -11
|
@@ -1,41 +1,38 @@
|
|
|
1
1
|
import { mergeAttributes, Node } from "@tiptap/core";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import { OrderedListPlugin } from "../OrderedListPlugin";
|
|
2
|
+
import { Slice } from "prosemirror-model";
|
|
3
|
+
import { TextSelection } from "prosemirror-state";
|
|
4
|
+
import BlockAttributes from "../BlockAttributes";
|
|
5
|
+
import { getBlockInfoFromPos } from "../helpers/getBlockInfoFromPos";
|
|
7
6
|
import { PreviousBlockTypePlugin } from "../PreviousBlockTypePlugin";
|
|
8
|
-
import { textblockTypeInputRuleSameNodeType } from "../rule";
|
|
9
7
|
import styles from "./Block.module.css";
|
|
10
|
-
import
|
|
8
|
+
import { TextContentType } from "./BlockTypes/TextBlock/TextContent";
|
|
9
|
+
import { HeadingContentType } from "./BlockTypes/HeadingBlock/HeadingContent";
|
|
10
|
+
import { ListItemContentType } from "./BlockTypes/ListItemBlock/ListItemContent";
|
|
11
11
|
|
|
12
12
|
export interface IBlock {
|
|
13
13
|
HTMLAttributes: Record<string, any>;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export type
|
|
17
|
-
|
|
16
|
+
export type BlockContentType =
|
|
17
|
+
| TextContentType
|
|
18
|
+
| HeadingContentType
|
|
19
|
+
| ListItemContentType;
|
|
18
20
|
|
|
19
21
|
declare module "@tiptap/core" {
|
|
20
22
|
interface Commands<ReturnType> {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
headingType?: Level;
|
|
35
|
-
listType?: ListType;
|
|
36
|
-
}) => ReturnType;
|
|
37
|
-
|
|
38
|
-
setBlockList: (type: ListType) => ReturnType;
|
|
23
|
+
block: {
|
|
24
|
+
BNCreateBlock: (pos: number) => ReturnType;
|
|
25
|
+
BNDeleteBlock: (posInBlock: number) => ReturnType;
|
|
26
|
+
BNMergeBlocks: (posBetweenBlocks: number) => ReturnType;
|
|
27
|
+
BNSplitBlock: (posInBlock: number, keepType: boolean) => ReturnType;
|
|
28
|
+
BNSetContentType: (
|
|
29
|
+
posInBlock: number,
|
|
30
|
+
type: BlockContentType
|
|
31
|
+
) => ReturnType;
|
|
32
|
+
BNCreateBlockOrSetContentType: (
|
|
33
|
+
posInBlock: number,
|
|
34
|
+
type: BlockContentType
|
|
35
|
+
) => ReturnType;
|
|
39
36
|
};
|
|
40
37
|
}
|
|
41
38
|
}
|
|
@@ -46,38 +43,31 @@ declare module "@tiptap/core" {
|
|
|
46
43
|
export const Block = Node.create<IBlock>({
|
|
47
44
|
name: "block",
|
|
48
45
|
group: "block",
|
|
46
|
+
// A block always contains content, and optionally a blockGroup which contains nested blocks
|
|
47
|
+
content: "blockContent blockGroup?",
|
|
48
|
+
// Ensures content-specific keyboard handlers trigger first.
|
|
49
|
+
priority: 50,
|
|
50
|
+
defining: true,
|
|
51
|
+
|
|
49
52
|
addOptions() {
|
|
50
53
|
return {
|
|
51
54
|
HTMLAttributes: {},
|
|
52
55
|
};
|
|
53
56
|
},
|
|
54
57
|
|
|
55
|
-
// A block always contains content, and optionally a blockGroup which contains nested blocks
|
|
56
|
-
content: "content blockgroup?",
|
|
57
|
-
|
|
58
|
-
defining: true,
|
|
59
|
-
|
|
60
58
|
addAttributes() {
|
|
61
59
|
return {
|
|
62
|
-
listType: {
|
|
63
|
-
default: undefined,
|
|
64
|
-
},
|
|
65
60
|
blockColor: {
|
|
66
61
|
default: undefined,
|
|
67
62
|
},
|
|
68
63
|
blockStyle: {
|
|
69
64
|
default: undefined,
|
|
70
65
|
},
|
|
71
|
-
headingType: {
|
|
72
|
-
default: undefined,
|
|
73
|
-
keepOnSplit: false,
|
|
74
|
-
},
|
|
75
66
|
};
|
|
76
67
|
},
|
|
77
68
|
|
|
78
69
|
parseHTML() {
|
|
79
70
|
return [
|
|
80
|
-
// For parsing blocks within the editor.
|
|
81
71
|
{
|
|
82
72
|
tag: "div",
|
|
83
73
|
getAttrs: (element) => {
|
|
@@ -96,49 +86,6 @@ export const Block = Node.create<IBlock>({
|
|
|
96
86
|
return attrs;
|
|
97
87
|
}
|
|
98
88
|
|
|
99
|
-
return false;
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
// For parsing headings & paragraphs copied from outside the editor.
|
|
103
|
-
{
|
|
104
|
-
tag: "p",
|
|
105
|
-
priority: 100,
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
tag: "h1",
|
|
109
|
-
attrs: { headingType: "1" },
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
tag: "h2",
|
|
113
|
-
attrs: { headingType: "2" },
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
tag: "h3",
|
|
117
|
-
attrs: { headingType: "3" },
|
|
118
|
-
},
|
|
119
|
-
// For parsing list items copied from outside the editor.
|
|
120
|
-
{
|
|
121
|
-
tag: "li",
|
|
122
|
-
getAttrs: (element) => {
|
|
123
|
-
if (typeof element === "string") {
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const parent = element.parentElement;
|
|
128
|
-
|
|
129
|
-
if (parent === null) {
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Gets type of list item (ordered/unordered) based on parent element's tag ("ol"/"ul").
|
|
134
|
-
if (parent.tagName === "UL") {
|
|
135
|
-
return { listType: "li" };
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (parent.tagName === "OL") {
|
|
139
|
-
return { listType: "oli" };
|
|
140
|
-
}
|
|
141
|
-
|
|
142
89
|
return false;
|
|
143
90
|
},
|
|
144
91
|
},
|
|
@@ -148,7 +95,8 @@ export const Block = Node.create<IBlock>({
|
|
|
148
95
|
renderHTML({ HTMLAttributes }) {
|
|
149
96
|
const attrs: Record<string, string> = {};
|
|
150
97
|
for (let [nodeAttr, HTMLAttr] of Object.entries(BlockAttributes)) {
|
|
151
|
-
|
|
98
|
+
// Ensure falsy values are not misinterpreted.
|
|
99
|
+
if (HTMLAttributes[nodeAttr] !== undefined) {
|
|
152
100
|
attrs[HTMLAttr] = HTMLAttributes[nodeAttr];
|
|
153
101
|
}
|
|
154
102
|
}
|
|
@@ -162,275 +110,448 @@ export const Block = Node.create<IBlock>({
|
|
|
162
110
|
[
|
|
163
111
|
"div",
|
|
164
112
|
mergeAttributes(attrs, {
|
|
113
|
+
// TODO: maybe remove html attributes from inner block
|
|
165
114
|
class: styles.block,
|
|
166
|
-
"data-node-type":
|
|
115
|
+
"data-node-type": this.name,
|
|
167
116
|
}),
|
|
168
117
|
0,
|
|
169
118
|
],
|
|
170
119
|
];
|
|
171
120
|
},
|
|
172
121
|
|
|
173
|
-
addInputRules() {
|
|
174
|
-
return [
|
|
175
|
-
...["1", "2", "3"].map((level) => {
|
|
176
|
-
// Create a heading when starting with "#", "##", or "###""
|
|
177
|
-
return textblockTypeInputRuleSameNodeType({
|
|
178
|
-
find: new RegExp(`^(#{1,${level}})\\s$`),
|
|
179
|
-
type: this.type,
|
|
180
|
-
getAttributes: {
|
|
181
|
-
headingType: level,
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
}),
|
|
185
|
-
// Create a list when starting with "-"
|
|
186
|
-
textblockTypeInputRuleSameNodeType({
|
|
187
|
-
find: /^\s*([-+*])\s$/,
|
|
188
|
-
type: this.type,
|
|
189
|
-
getAttributes: {
|
|
190
|
-
listType: "li",
|
|
191
|
-
},
|
|
192
|
-
}),
|
|
193
|
-
textblockTypeInputRuleSameNodeType({
|
|
194
|
-
find: new RegExp(/^1.\s/),
|
|
195
|
-
type: this.type,
|
|
196
|
-
getAttributes: {
|
|
197
|
-
listType: "oli",
|
|
198
|
-
},
|
|
199
|
-
}),
|
|
200
|
-
];
|
|
201
|
-
},
|
|
202
|
-
|
|
203
122
|
addCommands() {
|
|
204
123
|
return {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
(
|
|
208
|
-
|
|
124
|
+
// Creates a new text block at a given position.
|
|
125
|
+
BNCreateBlock:
|
|
126
|
+
(pos) =>
|
|
127
|
+
({ state, dispatch }) => {
|
|
128
|
+
const newBlock = state.schema.nodes["block"].createAndFill()!;
|
|
129
|
+
|
|
130
|
+
if (dispatch) {
|
|
131
|
+
state.tr.insert(pos, newBlock);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return true;
|
|
209
135
|
},
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
(
|
|
213
|
-
|
|
136
|
+
// Deletes a block at a given position and sets the selection to where the block was.
|
|
137
|
+
BNDeleteBlock:
|
|
138
|
+
(posInBlock) =>
|
|
139
|
+
({ state, view, dispatch }) => {
|
|
140
|
+
const blockInfo = getBlockInfoFromPos(state.doc, posInBlock);
|
|
141
|
+
if (blockInfo === undefined) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const { startPos, endPos } = blockInfo;
|
|
146
|
+
|
|
147
|
+
if (dispatch) {
|
|
148
|
+
state.tr.deleteRange(startPos, endPos);
|
|
149
|
+
state.tr.setSelection(
|
|
150
|
+
new TextSelection(state.doc.resolve(startPos + 1))
|
|
151
|
+
);
|
|
152
|
+
view.focus();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return true;
|
|
214
156
|
},
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
157
|
+
// Appends the text contents of a block to the nearest previous block, given a position between them. Children of
|
|
158
|
+
// the merged block are moved out of it first, rather than also being merged.
|
|
159
|
+
//
|
|
160
|
+
// In the example below, the position passed into the function is between Block1 and Block2.
|
|
161
|
+
//
|
|
162
|
+
// Block1
|
|
163
|
+
// Block2
|
|
164
|
+
// Block3
|
|
165
|
+
// Block4
|
|
166
|
+
// Block5
|
|
167
|
+
//
|
|
168
|
+
// Becomes:
|
|
169
|
+
//
|
|
170
|
+
// Block1
|
|
171
|
+
// Block2Block3
|
|
172
|
+
// Block4
|
|
173
|
+
// Block5
|
|
174
|
+
BNMergeBlocks:
|
|
175
|
+
(posBetweenBlocks) =>
|
|
176
|
+
({ state, dispatch }) => {
|
|
177
|
+
const nextNodeIsBlock =
|
|
178
|
+
state.doc.resolve(posBetweenBlocks + 1).node().type.name ===
|
|
179
|
+
"block";
|
|
180
|
+
const prevNodeIsBlock =
|
|
181
|
+
state.doc.resolve(posBetweenBlocks - 1).node().type.name ===
|
|
182
|
+
"block";
|
|
183
|
+
|
|
184
|
+
if (!nextNodeIsBlock || !prevNodeIsBlock) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const nextBlockInfo = getBlockInfoFromPos(
|
|
189
|
+
state.doc,
|
|
190
|
+
posBetweenBlocks + 1
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const { node, contentNode, startPos, endPos, depth } = nextBlockInfo!;
|
|
194
|
+
|
|
195
|
+
// Removes a level of nesting all children of the next block by 1 level, if it contains both content and block
|
|
196
|
+
// group nodes.
|
|
197
|
+
if (node.childCount === 2) {
|
|
198
|
+
const childBlocksStart = state.doc.resolve(
|
|
199
|
+
startPos + contentNode.nodeSize + 1
|
|
200
|
+
);
|
|
201
|
+
const childBlocksEnd = state.doc.resolve(endPos - 1);
|
|
202
|
+
const childBlocksRange =
|
|
203
|
+
childBlocksStart.blockRange(childBlocksEnd);
|
|
220
204
|
|
|
221
|
-
|
|
222
|
-
if (node.type.name === "block" && node.attrs["listType"]) {
|
|
205
|
+
// Moves the block group node inside the block into the block group node that the current block is in.
|
|
223
206
|
if (dispatch) {
|
|
224
|
-
tr.
|
|
225
|
-
...node.attrs,
|
|
226
|
-
listType: undefined,
|
|
227
|
-
});
|
|
228
|
-
return true;
|
|
207
|
+
state.tr.lift(childBlocksRange!, depth - 1);
|
|
229
208
|
}
|
|
230
209
|
}
|
|
231
|
-
return false;
|
|
232
|
-
},
|
|
233
210
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
//
|
|
238
|
-
|
|
239
|
-
|
|
211
|
+
let prevBlockEndPos = posBetweenBlocks - 1;
|
|
212
|
+
let prevBlockInfo = getBlockInfoFromPos(state.doc, prevBlockEndPos);
|
|
213
|
+
|
|
214
|
+
// Finds the nearest previous block, regardless of nesting level.
|
|
215
|
+
while (prevBlockInfo!.numChildBlocks > 0) {
|
|
216
|
+
prevBlockEndPos--;
|
|
217
|
+
prevBlockInfo = getBlockInfoFromPos(state.doc, prevBlockEndPos);
|
|
218
|
+
if (prevBlockInfo === undefined) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Deletes next block and adds its text content to the nearest previous block.
|
|
224
|
+
// TODO: Is there any situation where we need the whole block content, not just text? Implementation for this
|
|
225
|
+
// is trickier.
|
|
226
|
+
if (dispatch) {
|
|
227
|
+
state.tr.deleteRange(startPos, startPos + contentNode.nodeSize);
|
|
228
|
+
state.tr.insertText(contentNode.textContent, prevBlockEndPos - 1);
|
|
229
|
+
state.tr.setSelection(
|
|
230
|
+
new TextSelection(state.doc.resolve(prevBlockEndPos - 1))
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return true;
|
|
235
|
+
},
|
|
236
|
+
// Splits a block at a given position. Content after the position is moved to a new block below, at the same
|
|
237
|
+
// nesting level.
|
|
238
|
+
BNSplitBlock:
|
|
239
|
+
(posInBlock, keepType) =>
|
|
240
|
+
({ state, dispatch }) => {
|
|
241
|
+
const blockInfo = getBlockInfoFromPos(state.doc, posInBlock);
|
|
242
|
+
if (blockInfo === undefined) {
|
|
240
243
|
return false;
|
|
241
244
|
}
|
|
242
245
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
246
|
+
const { contentNode, contentType, startPos, endPos, depth } =
|
|
247
|
+
blockInfo;
|
|
248
|
+
|
|
249
|
+
const newBlockInsertionPos = endPos + 1;
|
|
250
|
+
|
|
251
|
+
// Creates new block first, otherwise positions get changed due to the original block's content changing.
|
|
252
|
+
// Only text content is transferred to the new block.
|
|
253
|
+
const secondBlockContent = state.doc.textBetween(posInBlock, endPos);
|
|
254
|
+
|
|
255
|
+
const newBlock = state.schema.nodes["block"].createAndFill()!;
|
|
256
|
+
const newBlockContentPos = newBlockInsertionPos + 2;
|
|
257
|
+
|
|
258
|
+
if (dispatch) {
|
|
259
|
+
state.tr.insert(newBlockInsertionPos, newBlock);
|
|
260
|
+
state.tr.insertText(secondBlockContent, newBlockContentPos);
|
|
261
|
+
|
|
262
|
+
if (keepType) {
|
|
263
|
+
state.tr.setBlockType(
|
|
264
|
+
newBlockContentPos,
|
|
265
|
+
newBlockContentPos,
|
|
266
|
+
state.schema.node(contentType).type,
|
|
267
|
+
contentNode.attrs
|
|
268
|
+
);
|
|
247
269
|
}
|
|
248
|
-
return true;
|
|
249
270
|
}
|
|
250
271
|
|
|
251
|
-
//
|
|
252
|
-
const
|
|
253
|
-
|
|
272
|
+
// Updates content of original block.
|
|
273
|
+
const firstBlockContent = state.doc.content.cut(startPos, posInBlock);
|
|
274
|
+
|
|
254
275
|
if (dispatch) {
|
|
255
|
-
tr.
|
|
256
|
-
|
|
276
|
+
state.tr.replace(
|
|
277
|
+
startPos,
|
|
278
|
+
endPos,
|
|
279
|
+
new Slice(firstBlockContent, depth, depth)
|
|
280
|
+
);
|
|
257
281
|
}
|
|
282
|
+
|
|
258
283
|
return true;
|
|
259
284
|
},
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
(
|
|
263
|
-
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
285
|
+
// Changes the content of a block at a given position to a given type.
|
|
286
|
+
BNSetContentType:
|
|
287
|
+
(posInBlock, type) =>
|
|
288
|
+
({ state, dispatch }) => {
|
|
289
|
+
const blockInfo = getBlockInfoFromPos(state.doc, posInBlock);
|
|
290
|
+
if (blockInfo === undefined) {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const { startPos, contentNode } = blockInfo;
|
|
295
|
+
|
|
296
|
+
if (dispatch) {
|
|
297
|
+
state.tr.setBlockType(
|
|
298
|
+
startPos + 1,
|
|
299
|
+
startPos + contentNode.nodeSize + 1,
|
|
300
|
+
state.schema.node(type.name).type,
|
|
301
|
+
type.attrs
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return true;
|
|
306
|
+
},
|
|
307
|
+
// Changes the block at a given position to a given content type if it's empty, otherwise creates a new block of
|
|
308
|
+
// that type below it.
|
|
309
|
+
BNCreateBlockOrSetContentType:
|
|
310
|
+
(posInBlock, type) =>
|
|
311
|
+
({ state, chain }) => {
|
|
312
|
+
const blockInfo = getBlockInfoFromPos(state.doc, posInBlock);
|
|
313
|
+
if (blockInfo === undefined) {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const { node, startPos, endPos } = blockInfo;
|
|
318
|
+
|
|
319
|
+
if (node.textContent.length === 0) {
|
|
320
|
+
const oldBlockContentPos = startPos + 1;
|
|
321
|
+
|
|
322
|
+
return chain()
|
|
323
|
+
.BNSetContentType(posInBlock, type)
|
|
324
|
+
.setTextSelection(oldBlockContentPos)
|
|
325
|
+
.run();
|
|
326
|
+
} else {
|
|
327
|
+
const newBlockInsertionPos = endPos + 1;
|
|
328
|
+
const newBlockContentPos = newBlockInsertionPos + 1;
|
|
329
|
+
|
|
330
|
+
return chain()
|
|
331
|
+
.BNCreateBlock(newBlockInsertionPos)
|
|
332
|
+
.BNSetContentType(newBlockContentPos, type)
|
|
333
|
+
.setTextSelection(newBlockContentPos)
|
|
334
|
+
.run();
|
|
275
335
|
}
|
|
276
|
-
return false;
|
|
277
336
|
},
|
|
278
|
-
joinBackward:
|
|
279
|
-
() =>
|
|
280
|
-
({ view, dispatch, state }) =>
|
|
281
|
-
joinBackward(state, dispatch, view), // Override default joinBackward with edited command
|
|
282
337
|
};
|
|
283
338
|
},
|
|
339
|
+
|
|
284
340
|
addProseMirrorPlugins() {
|
|
285
|
-
return [PreviousBlockTypePlugin()
|
|
341
|
+
return [PreviousBlockTypePlugin()];
|
|
286
342
|
},
|
|
343
|
+
|
|
287
344
|
addKeyboardShortcuts() {
|
|
288
345
|
// handleBackspace is partially adapted from https://github.com/ueberdosis/tiptap/blob/ed56337470efb4fd277128ab7ef792b37cfae992/packages/core/src/extensions/keymap.ts
|
|
289
346
|
const handleBackspace = () =>
|
|
290
347
|
this.editor.commands.first(({ commands }) => [
|
|
291
|
-
//
|
|
348
|
+
// Deletes the selection if it's not empty.
|
|
349
|
+
() => commands.deleteSelection(),
|
|
350
|
+
// Undoes an input rule if one was triggered in the last editor state change.
|
|
292
351
|
() => commands.undoInputRule(),
|
|
293
|
-
//
|
|
352
|
+
// Changes block type to a text block if it's not already, while the selection is at the start of the block.
|
|
353
|
+
() =>
|
|
354
|
+
commands.command(({ state }) => {
|
|
355
|
+
const { contentType } = getBlockInfoFromPos(
|
|
356
|
+
state.doc,
|
|
357
|
+
state.selection.from
|
|
358
|
+
)!;
|
|
359
|
+
|
|
360
|
+
const selectionAtBlockStart =
|
|
361
|
+
state.selection.$anchor.parentOffset === 0;
|
|
362
|
+
const isTextBlock = contentType.name === "textContent";
|
|
363
|
+
|
|
364
|
+
if (selectionAtBlockStart && !isTextBlock) {
|
|
365
|
+
return commands.BNSetContentType(state.selection.from, {
|
|
366
|
+
name: "textContent",
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return false;
|
|
371
|
+
}),
|
|
372
|
+
// Removes a level of nesting if the block is indented if the selection is at the start of the block.
|
|
294
373
|
() =>
|
|
295
|
-
commands.command(({
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
const { pos, parent } = $anchor;
|
|
299
|
-
const isAtStart = Selection.atStart(doc).from === pos;
|
|
374
|
+
commands.command(({ state }) => {
|
|
375
|
+
const selectionAtBlockStart =
|
|
376
|
+
state.selection.$anchor.parentOffset === 0;
|
|
300
377
|
|
|
301
|
-
if (
|
|
302
|
-
|
|
303
|
-
!isAtStart ||
|
|
304
|
-
!parent.type.isTextblock ||
|
|
305
|
-
parent.textContent.length
|
|
306
|
-
) {
|
|
307
|
-
return false;
|
|
378
|
+
if (selectionAtBlockStart) {
|
|
379
|
+
return commands.liftListItem("block");
|
|
308
380
|
}
|
|
309
381
|
|
|
310
|
-
return
|
|
382
|
+
return false;
|
|
311
383
|
}),
|
|
312
|
-
|
|
384
|
+
// Merges block with the previous one if it isn't indented, isn't the first block in the doc, and the selection
|
|
385
|
+
// is at the start of the block.
|
|
313
386
|
() =>
|
|
314
|
-
commands.command(({
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
387
|
+
commands.command(({ state }) => {
|
|
388
|
+
const { depth, startPos } = getBlockInfoFromPos(
|
|
389
|
+
state.doc,
|
|
390
|
+
state.selection.from
|
|
391
|
+
)!;
|
|
392
|
+
|
|
393
|
+
const selectionAtBlockStart =
|
|
394
|
+
state.selection.$anchor.parentOffset === 0;
|
|
395
|
+
const selectionEmpty =
|
|
396
|
+
state.selection.anchor === state.selection.head;
|
|
397
|
+
const blockAtDocStart = startPos === 2;
|
|
398
|
+
|
|
399
|
+
const posBetweenBlocks = startPos - 1;
|
|
400
|
+
|
|
401
|
+
if (
|
|
402
|
+
!blockAtDocStart &&
|
|
403
|
+
selectionAtBlockStart &&
|
|
404
|
+
selectionEmpty &&
|
|
405
|
+
depth === 2
|
|
406
|
+
) {
|
|
407
|
+
return commands.BNMergeBlocks(posBetweenBlocks);
|
|
323
408
|
}
|
|
409
|
+
|
|
324
410
|
return false;
|
|
325
411
|
}),
|
|
326
|
-
({ chain }) =>
|
|
327
|
-
// we are at the start of a block at the root level. The user hits backspace to "merge it" to the end of the block above
|
|
328
|
-
//
|
|
329
|
-
// BlockA
|
|
330
|
-
// BlockB
|
|
331
|
-
|
|
332
|
-
// Becomes:
|
|
333
|
-
|
|
334
|
-
// BlockABlockB
|
|
335
|
-
|
|
336
|
-
chain()
|
|
337
|
-
.command(({ tr, state, dispatch }) => {
|
|
338
|
-
const isAtStartOfNode = tr.selection.$anchor.parentOffset === 0;
|
|
339
|
-
const anchor = tr.selection.$anchor;
|
|
340
|
-
const node = anchor.node(-1);
|
|
341
|
-
if (isAtStartOfNode && node.type.name === "block") {
|
|
342
|
-
if (node.childCount === 2) {
|
|
343
|
-
// BlockB has children. We want to go from this:
|
|
344
|
-
//
|
|
345
|
-
// BlockA
|
|
346
|
-
// BlockB
|
|
347
|
-
// BlockC
|
|
348
|
-
// BlockD
|
|
349
|
-
//
|
|
350
|
-
// to:
|
|
351
|
-
//
|
|
352
|
-
// BlockABlockB
|
|
353
|
-
// BlockC
|
|
354
|
-
// BlockD
|
|
355
|
-
|
|
356
|
-
// This parts moves the children of BlockB to the top level
|
|
357
|
-
const startSecondChild = anchor.posAtIndex(1, -1) + 1; // start of blockgroup
|
|
358
|
-
const endSecondChild = anchor.posAtIndex(2, -1) - 1;
|
|
359
|
-
const range = state.doc
|
|
360
|
-
.resolve(startSecondChild)
|
|
361
|
-
.blockRange(state.doc.resolve(endSecondChild));
|
|
362
|
-
|
|
363
|
-
if (dispatch) {
|
|
364
|
-
tr.lift(range!, anchor.depth - 2);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
return true;
|
|
368
|
-
}
|
|
369
|
-
return false;
|
|
370
|
-
})
|
|
371
|
-
// use joinBackward to merge BlockB to BlockA (i.e.: turn it into BlockABlockB)
|
|
372
|
-
// The standard JoinBackward would break here, and would turn it into:
|
|
373
|
-
// BlockA
|
|
374
|
-
// BlockB
|
|
375
|
-
//
|
|
376
|
-
// joinBackward has been patched with our custom version to fix this (see commands/joinBackward)
|
|
377
|
-
.joinBackward()
|
|
378
|
-
.run(),
|
|
379
|
-
|
|
380
|
-
() => commands.selectNodeBackward(), // (source: tiptap)
|
|
381
412
|
]);
|
|
382
413
|
|
|
383
414
|
const handleEnter = () =>
|
|
384
415
|
this.editor.commands.first(({ commands }) => [
|
|
385
|
-
//
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
416
|
+
// Removes a level of nesting if the block is empty & indented, while the selection is also empty & at the start
|
|
417
|
+
// of the block.
|
|
418
|
+
() =>
|
|
419
|
+
commands.command(({ state }) => {
|
|
420
|
+
const { node, depth } = getBlockInfoFromPos(
|
|
421
|
+
state.doc,
|
|
422
|
+
state.selection.from
|
|
423
|
+
)!;
|
|
424
|
+
|
|
425
|
+
const selectionAtBlockStart =
|
|
426
|
+
state.selection.$anchor.parentOffset === 0;
|
|
427
|
+
const selectionEmpty =
|
|
428
|
+
state.selection.anchor === state.selection.head;
|
|
429
|
+
const blockEmpty = node.textContent.length === 0;
|
|
430
|
+
const blockIndented = depth > 2;
|
|
431
|
+
|
|
432
|
+
if (
|
|
433
|
+
selectionAtBlockStart &&
|
|
434
|
+
selectionEmpty &&
|
|
435
|
+
blockEmpty &&
|
|
436
|
+
blockIndented
|
|
437
|
+
) {
|
|
438
|
+
return commands.liftListItem("block");
|
|
439
|
+
}
|
|
440
|
+
|
|
392
441
|
return false;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
|
|
442
|
+
}),
|
|
443
|
+
// Creates a new block and moves the selection to it if the current one is empty, while the selection is also
|
|
444
|
+
// empty & at the start of the block.
|
|
445
|
+
() =>
|
|
446
|
+
commands.command(({ state, chain }) => {
|
|
447
|
+
const { node, endPos } = getBlockInfoFromPos(
|
|
448
|
+
state.doc,
|
|
449
|
+
state.selection.from
|
|
450
|
+
)!;
|
|
451
|
+
|
|
452
|
+
const selectionAtBlockStart =
|
|
453
|
+
state.selection.$anchor.parentOffset === 0;
|
|
454
|
+
const selectionEmpty =
|
|
455
|
+
state.selection.anchor === state.selection.head;
|
|
456
|
+
const blockEmpty = node.textContent.length === 0;
|
|
457
|
+
|
|
458
|
+
if (selectionAtBlockStart && selectionEmpty && blockEmpty) {
|
|
459
|
+
const newBlockInsertionPos = endPos + 1;
|
|
460
|
+
const newBlockContentPos = newBlockInsertionPos + 2;
|
|
461
|
+
|
|
462
|
+
chain()
|
|
463
|
+
.BNCreateBlock(newBlockInsertionPos)
|
|
464
|
+
.setTextSelection(newBlockContentPos)
|
|
465
|
+
.run();
|
|
396
466
|
|
|
397
|
-
|
|
398
|
-
if (dispatch) {
|
|
399
|
-
tr.setNodeMarkup(nodePos, undefined, {
|
|
400
|
-
...node.attrs,
|
|
401
|
-
listType: undefined,
|
|
402
|
-
});
|
|
467
|
+
return true;
|
|
403
468
|
}
|
|
404
|
-
return true;
|
|
405
|
-
}
|
|
406
|
-
return false;
|
|
407
|
-
},
|
|
408
|
-
// Otherwise, we might be on an empty line and hit "Enter" to create a new line:
|
|
409
|
-
({ tr, dispatch }) => {
|
|
410
|
-
const $from = tr.selection.$from;
|
|
411
469
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
470
|
+
return false;
|
|
471
|
+
}),
|
|
472
|
+
// Splits the current block, moving content inside that's after the cursor to a new text block below. Also
|
|
473
|
+
// deletes the selection beforehand, if it's not empty.
|
|
474
|
+
() =>
|
|
475
|
+
commands.command(({ state, chain }) => {
|
|
476
|
+
const { node } = getBlockInfoFromPos(
|
|
477
|
+
state.doc,
|
|
478
|
+
state.selection.from
|
|
479
|
+
)!;
|
|
480
|
+
|
|
481
|
+
const blockEmpty = node.textContent.length === 0;
|
|
482
|
+
|
|
483
|
+
if (!blockEmpty) {
|
|
484
|
+
chain()
|
|
485
|
+
.deleteSelection()
|
|
486
|
+
.BNSplitBlock(state.selection.from, false)
|
|
487
|
+
.run();
|
|
488
|
+
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return false;
|
|
493
|
+
}),
|
|
417
494
|
]);
|
|
418
495
|
|
|
419
496
|
return {
|
|
420
497
|
Backspace: handleBackspace,
|
|
421
498
|
Enter: handleEnter,
|
|
422
499
|
Tab: () => this.editor.commands.sinkListItem("block"),
|
|
423
|
-
"Shift-Tab": () =>
|
|
424
|
-
return this.editor.commands.liftListItem("block");
|
|
425
|
-
},
|
|
500
|
+
"Shift-Tab": () => this.editor.commands.liftListItem("block"),
|
|
426
501
|
"Mod-Alt-0": () =>
|
|
427
|
-
this.editor.
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
"Mod-Alt-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
502
|
+
this.editor.commands.BNCreateBlock(
|
|
503
|
+
this.editor.state.selection.anchor + 2
|
|
504
|
+
),
|
|
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
|
+
),
|
|
515
|
+
"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
|
+
),
|
|
525
|
+
"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
|
+
),
|
|
535
|
+
"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
|
+
),
|
|
545
|
+
"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
|
+
),
|
|
434
555
|
};
|
|
435
556
|
},
|
|
436
557
|
});
|