@blocknote/core 0.1.0-alpha.3 → 0.1.1
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/LICENSE +373 -0
- package/README.md +4 -2
- package/dist/blocknote.js +3461 -2429
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +35 -71
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +9 -7
- package/src/BlockNoteExtensions.ts +10 -17
- package/src/BlockNoteTheme.ts +150 -0
- package/src/EditorContent.tsx +2 -1
- package/src/extensions/Blocks/BlockAttributes.ts +12 -0
- package/src/extensions/Blocks/MultipleNodeSelection.ts +87 -0
- package/src/extensions/Blocks/OrderedListPlugin.ts +2 -2
- package/src/extensions/Blocks/PreviousBlockTypePlugin.ts +8 -2
- package/src/extensions/Blocks/helpers/findBlock.ts +1 -1
- package/src/extensions/Blocks/nodes/Block.module.css +37 -37
- package/src/extensions/Blocks/nodes/Block.ts +89 -45
- package/src/extensions/Blocks/nodes/BlockGroup.ts +19 -2
- package/src/extensions/Blocks/nodes/Content.ts +15 -2
- package/src/extensions/BubbleMenu/BubbleMenuExtension.tsx +10 -2
- package/src/extensions/BubbleMenu/component/BubbleMenu.tsx +122 -98
- package/src/extensions/BubbleMenu/component/LinkToolbarButton.tsx +8 -8
- package/src/extensions/DraggableBlocks/DraggableBlocksPlugin.tsx +143 -33
- package/src/extensions/DraggableBlocks/components/DragHandle.tsx +15 -21
- package/src/extensions/DraggableBlocks/components/DragHandleMenu.tsx +8 -7
- package/src/extensions/Hyperlinks/HyperlinkMenuPlugin.tsx +31 -66
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenu.tsx +44 -0
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItem.tsx +34 -0
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemIcon.tsx +31 -0
- package/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemInput.tsx +40 -0
- package/src/extensions/Hyperlinks/menus/HoverHyperlinkMenu.tsx +37 -0
- package/src/extensions/Hyperlinks/menus/HyperlinkMenu.tsx +63 -0
- package/src/extensions/SlashMenu/SlashMenuItem.ts +3 -1
- package/src/extensions/SlashMenu/defaultCommands.tsx +4 -4
- package/src/extensions/TrailingNode/TrailingNodeExtension.ts +8 -5
- package/src/shared/components/toolbar/Toolbar.tsx +8 -3
- package/src/shared/components/toolbar/ToolbarButton.tsx +57 -0
- package/src/shared/components/toolbar/ToolbarDropdown.tsx +35 -0
- package/src/shared/components/toolbar/ToolbarDropdownItem.tsx +35 -0
- package/src/shared/components/toolbar/ToolbarDropdownTarget.tsx +31 -0
- package/src/shared/plugins/suggestion/SuggestionItem.ts +3 -1
- package/src/shared/plugins/suggestion/{SuggestionListReactRenderer.ts → SuggestionListReactRenderer.tsx} +13 -4
- package/src/shared/plugins/suggestion/components/SuggestionGroup.tsx +6 -93
- package/src/shared/plugins/suggestion/components/SuggestionGroupItem.tsx +82 -0
- package/src/shared/plugins/suggestion/components/SuggestionList.tsx +24 -23
- package/src/useEditor.ts +4 -0
- package/src/utils.ts +12 -0
- package/types/src/BlockNoteExtensions.d.ts +3 -0
- package/types/src/BlockNoteTheme.d.ts +2 -0
- package/types/src/commands/indentation.d.ts +2 -0
- package/types/src/extensions/Blocks/BlockAttributes.d.ts +2 -0
- package/types/src/extensions/Blocks/MultipleNodeSelection.d.ts +24 -0
- package/types/src/extensions/Blocks/nodes/Block.d.ts +1 -1
- package/types/src/extensions/BubbleMenu/component/LinkToolbarButton.d.ts +2 -2
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenu.d.ts +11 -0
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItem.d.ts +13 -0
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemIcon.d.ts +8 -0
- package/types/src/extensions/Hyperlinks/menus/EditHyperlinkMenuItemInput.d.ts +9 -0
- package/types/src/extensions/Hyperlinks/menus/HoverHyperlinkMenu.d.ts +12 -0
- package/types/src/extensions/Hyperlinks/menus/HyperlinkMenu.d.ts +21 -0
- package/types/src/extensions/Hyperlinks/menus/helpers/PanelTextInput.d.ts +39 -0
- package/types/src/extensions/Hyperlinks/menus/helpers/PanelTextInputStyles.d.ts +3 -0
- package/types/src/extensions/Hyperlinks/menus/helpers/ToolbarComponent.d.ts +13 -0
- package/types/src/extensions/SlashMenu/SlashMenuItem.d.ts +4 -7
- package/types/src/extensions/TrailingNode/TrailingNodeExtension.d.ts +3 -0
- package/types/src/nodes/ChildgroupNode.d.ts +28 -0
- package/types/src/nodes/patchNodes.d.ts +1 -0
- package/types/src/plugins/TreeViewPlugin/index.d.ts +2 -0
- package/types/src/plugins/animation.d.ts +2 -0
- package/types/src/react/BlockNoteComposer.d.ts +17 -0
- package/types/src/react/BlockNotePlugin.d.ts +1 -0
- package/types/src/react/index.d.ts +3 -0
- package/types/src/react/useBlockNoteSetup.d.ts +2 -0
- package/types/src/registerBlockNote.d.ts +2 -0
- package/types/src/shared/components/toolbar/SimpleToolbarButton.d.ts +2 -3
- package/types/src/shared/components/toolbar/SimpleToolbarDropdown.d.ts +11 -0
- package/types/src/shared/components/toolbar/SimpleToolbarDropdownItem.d.ts +11 -0
- package/types/src/shared/components/toolbar/Toolbar.d.ts +2 -2
- package/types/src/shared/components/toolbar/ToolbarButton.d.ts +15 -0
- package/types/src/shared/components/toolbar/ToolbarDropdown.d.ts +17 -0
- package/types/src/shared/components/toolbar/ToolbarDropdownItem.d.ts +11 -0
- package/types/src/shared/components/toolbar/ToolbarDropdownTarget.d.ts +8 -0
- package/types/src/shared/plugins/suggestion/SuggestionItem.d.ts +2 -4
- package/types/src/shared/plugins/suggestion/components/SuggestionGroupItem.d.ts +9 -0
- package/types/src/shared/plugins/suggestion/components/SuggestionList.d.ts +0 -15
- package/types/src/themes/BlockNoteEditorTheme.d.ts +11 -0
- package/types/src/useEditor.d.ts +3 -0
- package/types/src/utils.d.ts +2 -0
- package/src/extensions/Blocks/nodes/README.md +0 -26
- package/src/extensions/BubbleMenu/component/DropdownBlockItem.module.css +0 -13
- package/src/extensions/BubbleMenu/component/DropdownBlockItem.tsx +0 -25
- package/src/extensions/DraggableBlocks/components/DragHandle.module.css +0 -33
- package/src/extensions/DraggableBlocks/components/DragHandleMenu.module.css +0 -10
- package/src/extensions/Hyperlinks/menus/HyperlinkBasicMenu.tsx +0 -59
- package/src/extensions/Hyperlinks/menus/HyperlinkEditMenu.tsx +0 -72
- package/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInput.tsx +0 -173
- package/src/extensions/Hyperlinks/menus/atlaskit/PanelTextInputStyles.ts +0 -36
- package/src/extensions/Hyperlinks/menus/atlaskit/README.md +0 -1
- package/src/extensions/Hyperlinks/menus/atlaskit/ToolbarComponent.tsx +0 -61
- package/src/extensions/helpers/formatKeyboardShortcut.ts +0 -9
- package/src/lib/atlaskit/browser.ts +0 -47
- package/src/shared/components/toolbar/SimpleToolbarButton.module.css +0 -13
- package/src/shared/components/toolbar/SimpleToolbarButton.tsx +0 -56
- package/src/shared/components/toolbar/Toolbar.module.css +0 -10
- package/src/shared/components/toolbar/ToolbarSeparator.module.css +0 -13
- package/src/shared/components/toolbar/ToolbarSeparator.tsx +0 -7
- package/src/shared/plugins/suggestion/components/SuggestionGroup.module.css +0 -45
- package/src/shared/plugins/suggestion/components/SuggestionList.module.css +0 -10
|
@@ -7,12 +7,13 @@ import { OrderedListPlugin } from "../OrderedListPlugin";
|
|
|
7
7
|
import { PreviousBlockTypePlugin } from "../PreviousBlockTypePlugin";
|
|
8
8
|
import { textblockTypeInputRuleSameNodeType } from "../rule";
|
|
9
9
|
import styles from "./Block.module.css";
|
|
10
|
+
import BlockAttributes from "../BlockAttributes";
|
|
10
11
|
|
|
11
12
|
export interface IBlock {
|
|
12
13
|
HTMLAttributes: Record<string, any>;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
export type Level = 1 | 2 | 3;
|
|
16
|
+
export type Level = "1" | "2" | "3";
|
|
16
17
|
export type ListType = "li" | "oli";
|
|
17
18
|
|
|
18
19
|
declare module "@tiptap/core" {
|
|
@@ -43,7 +44,7 @@ declare module "@tiptap/core" {
|
|
|
43
44
|
* The main "Block node" documents consist of
|
|
44
45
|
*/
|
|
45
46
|
export const Block = Node.create<IBlock>({
|
|
46
|
-
name: "
|
|
47
|
+
name: "block",
|
|
47
48
|
group: "block",
|
|
48
49
|
addOptions() {
|
|
49
50
|
return {
|
|
@@ -52,7 +53,7 @@ export const Block = Node.create<IBlock>({
|
|
|
52
53
|
},
|
|
53
54
|
|
|
54
55
|
// A block always contains content, and optionally a blockGroup which contains nested blocks
|
|
55
|
-
content: "
|
|
56
|
+
content: "content blockgroup?",
|
|
56
57
|
|
|
57
58
|
defining: true,
|
|
58
59
|
|
|
@@ -60,63 +61,107 @@ export const Block = Node.create<IBlock>({
|
|
|
60
61
|
return {
|
|
61
62
|
listType: {
|
|
62
63
|
default: undefined,
|
|
63
|
-
renderHTML: (attributes) => {
|
|
64
|
-
return {
|
|
65
|
-
"data-listType": attributes.listType,
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
parseHTML: (element) => element.getAttribute("data-listType"),
|
|
69
64
|
},
|
|
70
65
|
blockColor: {
|
|
71
66
|
default: undefined,
|
|
72
|
-
renderHTML: (attributes) => {
|
|
73
|
-
return {
|
|
74
|
-
"data-blockColor": attributes.blockColor,
|
|
75
|
-
};
|
|
76
|
-
},
|
|
77
|
-
parseHTML: (element) => element.getAttribute("data-blockColor"),
|
|
78
67
|
},
|
|
79
68
|
blockStyle: {
|
|
80
69
|
default: undefined,
|
|
81
|
-
renderHTML: (attributes) => {
|
|
82
|
-
return {
|
|
83
|
-
"data-blockStyle": attributes.blockStyle,
|
|
84
|
-
};
|
|
85
|
-
},
|
|
86
|
-
parseHTML: (element) => element.getAttribute("data-blockStyle"),
|
|
87
70
|
},
|
|
88
71
|
headingType: {
|
|
89
72
|
default: undefined,
|
|
90
73
|
keepOnSplit: false,
|
|
91
|
-
renderHTML: (attributes) => {
|
|
92
|
-
return {
|
|
93
|
-
"data-headingType": attributes.headingType,
|
|
94
|
-
};
|
|
95
|
-
},
|
|
96
|
-
parseHTML: (element) => element.getAttribute("data-headingType"),
|
|
97
74
|
},
|
|
98
75
|
};
|
|
99
76
|
},
|
|
100
77
|
|
|
101
|
-
// TODO: should we parse <li>, <ol>, <h1>, etc?
|
|
102
78
|
parseHTML() {
|
|
103
79
|
return [
|
|
80
|
+
// For parsing blocks within the editor.
|
|
104
81
|
{
|
|
105
82
|
tag: "div",
|
|
83
|
+
getAttrs: (element) => {
|
|
84
|
+
if (typeof element === "string") {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const attrs: Record<string, string> = {};
|
|
89
|
+
for (let [nodeAttr, HTMLAttr] of Object.entries(BlockAttributes)) {
|
|
90
|
+
if (element.getAttribute(HTMLAttr)) {
|
|
91
|
+
attrs[nodeAttr] = element.getAttribute(HTMLAttr)!;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (element.getAttribute("data-node-type") === "block") {
|
|
96
|
+
return attrs;
|
|
97
|
+
}
|
|
98
|
+
|
|
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
|
+
return false;
|
|
143
|
+
},
|
|
106
144
|
},
|
|
107
145
|
];
|
|
108
146
|
},
|
|
109
147
|
|
|
110
148
|
renderHTML({ HTMLAttributes }) {
|
|
149
|
+
const attrs: Record<string, string> = {};
|
|
150
|
+
for (let [nodeAttr, HTMLAttr] of Object.entries(BlockAttributes)) {
|
|
151
|
+
attrs[HTMLAttr] = HTMLAttributes[nodeAttr];
|
|
152
|
+
}
|
|
153
|
+
|
|
111
154
|
return [
|
|
112
155
|
"div",
|
|
113
|
-
mergeAttributes(
|
|
156
|
+
mergeAttributes(attrs, {
|
|
114
157
|
class: styles.blockOuter,
|
|
158
|
+
"data-node-type": "block-outer",
|
|
115
159
|
}),
|
|
116
160
|
[
|
|
117
161
|
"div",
|
|
118
|
-
mergeAttributes(
|
|
162
|
+
mergeAttributes(attrs, {
|
|
119
163
|
class: styles.block,
|
|
164
|
+
"data-node-type": "block",
|
|
120
165
|
}),
|
|
121
166
|
0,
|
|
122
167
|
],
|
|
@@ -125,7 +170,7 @@ export const Block = Node.create<IBlock>({
|
|
|
125
170
|
|
|
126
171
|
addInputRules() {
|
|
127
172
|
return [
|
|
128
|
-
...[1, 2, 3].map((level) => {
|
|
173
|
+
...["1", "2", "3"].map((level) => {
|
|
129
174
|
// Create a heading when starting with "#", "##", or "###""
|
|
130
175
|
return textblockTypeInputRuleSameNodeType({
|
|
131
176
|
find: new RegExp(`^(#{1,${level}})\\s$`),
|
|
@@ -172,7 +217,7 @@ export const Block = Node.create<IBlock>({
|
|
|
172
217
|
const nodePos = tr.selection.$anchor.posAtIndex(0, -1) - 1;
|
|
173
218
|
|
|
174
219
|
// const node2 = tr.doc.nodeAt(nodePos);
|
|
175
|
-
if (node.type.name === "
|
|
220
|
+
if (node.type.name === "block" && node.attrs["listType"]) {
|
|
176
221
|
if (dispatch) {
|
|
177
222
|
tr.setNodeMarkup(nodePos, undefined, {
|
|
178
223
|
...node.attrs,
|
|
@@ -203,8 +248,7 @@ export const Block = Node.create<IBlock>({
|
|
|
203
248
|
|
|
204
249
|
// Create new block after current block
|
|
205
250
|
const endOfBlock = currentBlock.pos + currentBlock.node.nodeSize;
|
|
206
|
-
let newBlock =
|
|
207
|
-
state.schema.nodes["tcblock"].createAndFill(attributes)!;
|
|
251
|
+
let newBlock = state.schema.nodes["block"].createAndFill(attributes)!;
|
|
208
252
|
if (dispatch) {
|
|
209
253
|
tr.insert(endOfBlock, newBlock);
|
|
210
254
|
tr.setSelection(new TextSelection(tr.doc.resolve(endOfBlock + 1)));
|
|
@@ -218,7 +262,7 @@ export const Block = Node.create<IBlock>({
|
|
|
218
262
|
const nodePos = tr.selection.$anchor.posAtIndex(0, -1) - 1;
|
|
219
263
|
|
|
220
264
|
// const node2 = tr.doc.nodeAt(nodePos);
|
|
221
|
-
if (node.type.name === "
|
|
265
|
+
if (node.type.name === "block") {
|
|
222
266
|
if (dispatch) {
|
|
223
267
|
tr.setNodeMarkup(nodePos, undefined, {
|
|
224
268
|
...node.attrs,
|
|
@@ -268,11 +312,11 @@ export const Block = Node.create<IBlock>({
|
|
|
268
312
|
commands.command(({ tr }) => {
|
|
269
313
|
const isAtStartOfNode = tr.selection.$anchor.parentOffset === 0;
|
|
270
314
|
const node = tr.selection.$anchor.node(-1);
|
|
271
|
-
if (isAtStartOfNode && node.type.name === "
|
|
315
|
+
if (isAtStartOfNode && node.type.name === "block") {
|
|
272
316
|
// we're at the start of the block, so we're trying to "backspace" the bullet or indentation
|
|
273
317
|
return commands.first([
|
|
274
318
|
() => commands.unsetList(), // first try to remove the "list" property
|
|
275
|
-
() => commands.liftListItem("
|
|
319
|
+
() => commands.liftListItem("block"), // then try to remove a level of indentation
|
|
276
320
|
]);
|
|
277
321
|
}
|
|
278
322
|
return false;
|
|
@@ -292,7 +336,7 @@ export const Block = Node.create<IBlock>({
|
|
|
292
336
|
const isAtStartOfNode = tr.selection.$anchor.parentOffset === 0;
|
|
293
337
|
const anchor = tr.selection.$anchor;
|
|
294
338
|
const node = anchor.node(-1);
|
|
295
|
-
if (isAtStartOfNode && node.type.name === "
|
|
339
|
+
if (isAtStartOfNode && node.type.name === "block") {
|
|
296
340
|
if (node.childCount === 2) {
|
|
297
341
|
// BlockB has children. We want to go from this:
|
|
298
342
|
//
|
|
@@ -337,7 +381,7 @@ export const Block = Node.create<IBlock>({
|
|
|
337
381
|
const handleEnter = () =>
|
|
338
382
|
this.editor.commands.first(({ commands }) => [
|
|
339
383
|
// Try to split the current block into 2 items:
|
|
340
|
-
() => commands.splitListItem("
|
|
384
|
+
() => commands.splitListItem("block"),
|
|
341
385
|
// Otherwise, maybe we are in an empty list item. "Enter" should remove the list bullet
|
|
342
386
|
({ tr, dispatch }) => {
|
|
343
387
|
const $from = tr.selection.$from;
|
|
@@ -348,7 +392,7 @@ export const Block = Node.create<IBlock>({
|
|
|
348
392
|
const node = tr.selection.$anchor.node(-1);
|
|
349
393
|
const nodePos = tr.selection.$anchor.posAtIndex(0, -1) - 1;
|
|
350
394
|
|
|
351
|
-
if (node.type.name === "
|
|
395
|
+
if (node.type.name === "block" && node.attrs["listType"]) {
|
|
352
396
|
if (dispatch) {
|
|
353
397
|
tr.setNodeMarkup(nodePos, undefined, {
|
|
354
398
|
...node.attrs,
|
|
@@ -373,15 +417,15 @@ export const Block = Node.create<IBlock>({
|
|
|
373
417
|
return {
|
|
374
418
|
Backspace: handleBackspace,
|
|
375
419
|
Enter: handleEnter,
|
|
376
|
-
Tab: () => this.editor.commands.sinkListItem("
|
|
420
|
+
Tab: () => this.editor.commands.sinkListItem("block"),
|
|
377
421
|
"Shift-Tab": () => {
|
|
378
|
-
return this.editor.commands.liftListItem("
|
|
422
|
+
return this.editor.commands.liftListItem("block");
|
|
379
423
|
},
|
|
380
424
|
"Mod-Alt-0": () =>
|
|
381
425
|
this.editor.chain().unsetList().unsetBlockHeading().run(),
|
|
382
|
-
"Mod-Alt-1": () => this.editor.commands.setBlockHeading({ level: 1 }),
|
|
383
|
-
"Mod-Alt-2": () => this.editor.commands.setBlockHeading({ level: 2 }),
|
|
384
|
-
"Mod-Alt-3": () => this.editor.commands.setBlockHeading({ level: 3 }),
|
|
426
|
+
"Mod-Alt-1": () => this.editor.commands.setBlockHeading({ level: "1" }),
|
|
427
|
+
"Mod-Alt-2": () => this.editor.commands.setBlockHeading({ level: "2" }),
|
|
428
|
+
"Mod-Alt-3": () => this.editor.commands.setBlockHeading({ level: "3" }),
|
|
385
429
|
"Mod-Shift-7": () => this.editor.commands.setBlockList("li"),
|
|
386
430
|
"Mod-Shift-8": () => this.editor.commands.setBlockList("oli"),
|
|
387
431
|
// TODO: Add shortcuts for numbered and bullet list
|
|
@@ -10,10 +10,26 @@ export const BlockGroup = Node.create({
|
|
|
10
10
|
};
|
|
11
11
|
},
|
|
12
12
|
|
|
13
|
-
content: "
|
|
13
|
+
content: "block+",
|
|
14
14
|
|
|
15
15
|
parseHTML() {
|
|
16
|
-
return [
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
tag: "div",
|
|
19
|
+
getAttrs: (element) => {
|
|
20
|
+
if(typeof element === "string") {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if(element.getAttribute("data-node-type") === "block-group") {
|
|
25
|
+
// Null means the element matches, but we don't want to add any attributes to the node.
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
];
|
|
17
33
|
},
|
|
18
34
|
|
|
19
35
|
renderHTML({ HTMLAttributes }) {
|
|
@@ -21,6 +37,7 @@ export const BlockGroup = Node.create({
|
|
|
21
37
|
"div",
|
|
22
38
|
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
|
|
23
39
|
class: styles.blockGroup,
|
|
40
|
+
"data-node-type": "block-group"
|
|
24
41
|
}),
|
|
25
42
|
0,
|
|
26
43
|
];
|
|
@@ -5,7 +5,7 @@ export interface IBlock {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export const ContentBlock = Node.create<IBlock>({
|
|
8
|
-
name: "
|
|
8
|
+
name: "content",
|
|
9
9
|
|
|
10
10
|
addOptions() {
|
|
11
11
|
return {
|
|
@@ -32,7 +32,19 @@ export const ContentBlock = Node.create<IBlock>({
|
|
|
32
32
|
return [
|
|
33
33
|
{
|
|
34
34
|
tag: "div",
|
|
35
|
-
|
|
35
|
+
getAttrs: (element) => {
|
|
36
|
+
if(typeof element === "string") {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if(element.getAttribute("data-node-type") === "block-content") {
|
|
41
|
+
// Null means the element matches, but we don't want to add any attributes to the node.
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
36
48
|
];
|
|
37
49
|
},
|
|
38
50
|
|
|
@@ -41,6 +53,7 @@ export const ContentBlock = Node.create<IBlock>({
|
|
|
41
53
|
"div",
|
|
42
54
|
mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, {
|
|
43
55
|
class: styles.blockContent,
|
|
56
|
+
"data-node-type": "block-content"
|
|
44
57
|
}),
|
|
45
58
|
// TODO: The extra nested div is only needed for placeholders, different solution (without extra div) would be preferable
|
|
46
59
|
// We can't use the other div because the ::before attribute on that one is already reserved for list-bullets
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { MantineProvider } from "@mantine/core";
|
|
1
2
|
import { Extension } from "@tiptap/core";
|
|
2
3
|
import { PluginKey } from "prosemirror-state";
|
|
3
4
|
import ReactDOM from "react-dom";
|
|
5
|
+
import { BlockNoteTheme } from "../../BlockNoteTheme";
|
|
6
|
+
import rootStyles from "../../root.module.css";
|
|
4
7
|
import { createBubbleMenuPlugin } from "./BubbleMenuPlugin";
|
|
5
8
|
import { BubbleMenu } from "./component/BubbleMenu";
|
|
6
|
-
|
|
9
|
+
|
|
7
10
|
/**
|
|
8
11
|
* The menu that is displayed when selecting a piece of text.
|
|
9
12
|
*/
|
|
@@ -13,7 +16,12 @@ export const BubbleMenuExtension = Extension.create<{}>({
|
|
|
13
16
|
addProseMirrorPlugins() {
|
|
14
17
|
const element = document.createElement("div");
|
|
15
18
|
element.className = rootStyles.bnRoot;
|
|
16
|
-
ReactDOM.render(
|
|
19
|
+
ReactDOM.render(
|
|
20
|
+
<MantineProvider theme={BlockNoteTheme}>
|
|
21
|
+
<BubbleMenu editor={this.editor} />
|
|
22
|
+
</MantineProvider>,
|
|
23
|
+
element
|
|
24
|
+
);
|
|
17
25
|
return [
|
|
18
26
|
createBubbleMenuPlugin({
|
|
19
27
|
editor: this.editor,
|
|
@@ -4,24 +4,24 @@ import {
|
|
|
4
4
|
RiH1,
|
|
5
5
|
RiH2,
|
|
6
6
|
RiH3,
|
|
7
|
+
RiIndentDecrease,
|
|
8
|
+
RiIndentIncrease,
|
|
7
9
|
RiItalic,
|
|
8
10
|
RiLink,
|
|
9
|
-
RiStrikethrough,
|
|
10
|
-
RiUnderline,
|
|
11
|
-
RiIndentIncrease,
|
|
12
|
-
RiIndentDecrease,
|
|
13
|
-
RiText,
|
|
14
11
|
RiListOrdered,
|
|
15
12
|
RiListUnordered,
|
|
13
|
+
RiStrikethrough,
|
|
14
|
+
RiText,
|
|
15
|
+
RiUnderline,
|
|
16
16
|
} from "react-icons/ri";
|
|
17
|
-
import {
|
|
17
|
+
import { ToolbarButton } from "../../../shared/components/toolbar/ToolbarButton";
|
|
18
|
+
import { ToolbarDropdown } from "../../../shared/components/toolbar/ToolbarDropdown";
|
|
18
19
|
import { Toolbar } from "../../../shared/components/toolbar/Toolbar";
|
|
19
20
|
import { useEditorForceUpdate } from "../../../shared/hooks/useEditorForceUpdate";
|
|
20
21
|
import { findBlock } from "../../Blocks/helpers/findBlock";
|
|
21
|
-
import formatKeyboardShortcut from "
|
|
22
|
+
import { formatKeyboardShortcut } from "../../../utils";
|
|
22
23
|
import LinkToolbarButton from "./LinkToolbarButton";
|
|
23
|
-
import
|
|
24
|
-
import DropdownBlockItem from "./DropdownBlockItem";
|
|
24
|
+
import { IconType } from "react-icons";
|
|
25
25
|
|
|
26
26
|
type ListType = "li" | "oli";
|
|
27
27
|
|
|
@@ -59,127 +59,151 @@ export const BubbleMenu = (props: { editor: Editor }) => {
|
|
|
59
59
|
currentBlockListType
|
|
60
60
|
);
|
|
61
61
|
|
|
62
|
+
const blockIconMap: Record<string, IconType> = {
|
|
63
|
+
Text: RiText,
|
|
64
|
+
"Heading 1": RiH1,
|
|
65
|
+
"Heading 2": RiH2,
|
|
66
|
+
"Heading 3": RiH3,
|
|
67
|
+
"Bullet List": RiListUnordered,
|
|
68
|
+
"Numbered List": RiListOrdered,
|
|
69
|
+
};
|
|
70
|
+
|
|
62
71
|
return (
|
|
63
72
|
<Toolbar>
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
props.editor.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
<ToolbarDropdown
|
|
74
|
+
text={currentBlockName}
|
|
75
|
+
icon={blockIconMap[currentBlockName]}
|
|
76
|
+
items={[
|
|
77
|
+
{
|
|
78
|
+
onClick: () => {
|
|
79
|
+
// Setting editor focus using a chained command instead causes bubble menu to flicker on click.
|
|
80
|
+
props.editor.view.focus();
|
|
81
|
+
props.editor.chain().unsetBlockHeading().unsetList().run();
|
|
82
|
+
},
|
|
83
|
+
text: "Text",
|
|
84
|
+
icon: RiText,
|
|
85
|
+
isSelected: currentBlockName === "Text",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
onClick: () => {
|
|
89
|
+
props.editor.view.focus();
|
|
79
90
|
props.editor
|
|
80
91
|
.chain()
|
|
81
|
-
.focus()
|
|
82
92
|
.unsetList()
|
|
83
|
-
.setBlockHeading({ level: 1 })
|
|
84
|
-
.run()
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
onClick
|
|
93
|
+
.setBlockHeading({ level: "1" })
|
|
94
|
+
.run();
|
|
95
|
+
},
|
|
96
|
+
text: "Heading 1",
|
|
97
|
+
icon: RiH1,
|
|
98
|
+
isSelected: currentBlockName === "Heading 1",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
onClick: () => {
|
|
102
|
+
props.editor.view.focus();
|
|
92
103
|
props.editor
|
|
93
104
|
.chain()
|
|
94
|
-
.focus()
|
|
95
105
|
.unsetList()
|
|
96
|
-
.setBlockHeading({ level: 2 })
|
|
97
|
-
.run()
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
onClick
|
|
106
|
+
.setBlockHeading({ level: "2" })
|
|
107
|
+
.run();
|
|
108
|
+
},
|
|
109
|
+
text: "Heading 2",
|
|
110
|
+
icon: RiH2,
|
|
111
|
+
isSelected: currentBlockName === "Heading 2",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
onClick: () => {
|
|
115
|
+
props.editor.view.focus();
|
|
105
116
|
props.editor
|
|
106
117
|
.chain()
|
|
107
|
-
.focus()
|
|
108
118
|
.unsetList()
|
|
109
|
-
.setBlockHeading({ level: 3 })
|
|
110
|
-
.run()
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
onClick
|
|
118
|
-
props.editor
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
icon={RiListOrdered}
|
|
129
|
-
isSelected={currentBlockName === "Numbered List"}
|
|
130
|
-
onClick={() =>
|
|
119
|
+
.setBlockHeading({ level: "3" })
|
|
120
|
+
.run();
|
|
121
|
+
},
|
|
122
|
+
text: "Heading 3",
|
|
123
|
+
icon: RiH3,
|
|
124
|
+
isSelected: currentBlockName === "Heading 3",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
onClick: () => {
|
|
128
|
+
props.editor.view.focus();
|
|
129
|
+
props.editor.chain().unsetBlockHeading().setBlockList("li").run();
|
|
130
|
+
},
|
|
131
|
+
text: "Bullet List",
|
|
132
|
+
icon: RiListUnordered,
|
|
133
|
+
isSelected: currentBlockName === "Bullet List",
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
onClick: () => {
|
|
137
|
+
props.editor.view.focus();
|
|
131
138
|
props.editor
|
|
132
139
|
.chain()
|
|
133
|
-
.focus()
|
|
134
140
|
.unsetBlockHeading()
|
|
135
141
|
.setBlockList("oli")
|
|
136
|
-
.run()
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
142
|
+
.run();
|
|
143
|
+
},
|
|
144
|
+
text: "Numbered List",
|
|
145
|
+
icon: RiListOrdered,
|
|
146
|
+
isSelected: currentBlockName === "Numbered List",
|
|
147
|
+
},
|
|
148
|
+
]}
|
|
149
|
+
/>
|
|
150
|
+
<ToolbarButton
|
|
151
|
+
onClick={() => {
|
|
152
|
+
// Setting editor focus using a chained command instead causes bubble menu to flicker on click.
|
|
153
|
+
props.editor.view.focus();
|
|
154
|
+
props.editor.commands.toggleBold();
|
|
155
|
+
}}
|
|
143
156
|
isSelected={props.editor.isActive("bold")}
|
|
144
157
|
mainTooltip="Bold"
|
|
145
158
|
secondaryTooltip={formatKeyboardShortcut("Mod+B")}
|
|
146
159
|
icon={RiBold}
|
|
147
160
|
/>
|
|
148
|
-
<
|
|
149
|
-
onClick={() =>
|
|
161
|
+
<ToolbarButton
|
|
162
|
+
onClick={() => {
|
|
163
|
+
props.editor.view.focus();
|
|
164
|
+
props.editor.commands.toggleItalic();
|
|
165
|
+
}}
|
|
150
166
|
isSelected={props.editor.isActive("italic")}
|
|
151
167
|
mainTooltip="Italic"
|
|
152
168
|
secondaryTooltip={formatKeyboardShortcut("Mod+I")}
|
|
153
169
|
icon={RiItalic}
|
|
154
170
|
/>
|
|
155
|
-
<
|
|
156
|
-
onClick={() =>
|
|
171
|
+
<ToolbarButton
|
|
172
|
+
onClick={() => {
|
|
173
|
+
props.editor.view.focus();
|
|
174
|
+
props.editor.commands.toggleUnderline();
|
|
175
|
+
}}
|
|
157
176
|
isSelected={props.editor.isActive("underline")}
|
|
158
177
|
mainTooltip="Underline"
|
|
159
178
|
secondaryTooltip={formatKeyboardShortcut("Mod+U")}
|
|
160
179
|
icon={RiUnderline}
|
|
161
180
|
/>
|
|
162
|
-
<
|
|
163
|
-
onClick={() =>
|
|
164
|
-
|
|
181
|
+
<ToolbarButton
|
|
182
|
+
onClick={() => {
|
|
183
|
+
props.editor.view.focus();
|
|
184
|
+
props.editor.commands.toggleStrike();
|
|
185
|
+
}}
|
|
186
|
+
isSelected={props.editor.isActive("strike")}
|
|
165
187
|
mainTooltip="Strike-through"
|
|
166
188
|
secondaryTooltip={formatKeyboardShortcut("Mod+Shift+X")}
|
|
167
189
|
icon={RiStrikethrough}
|
|
168
190
|
/>
|
|
169
|
-
<
|
|
170
|
-
onClick={() =>
|
|
171
|
-
props.editor.
|
|
172
|
-
|
|
173
|
-
|
|
191
|
+
<ToolbarButton
|
|
192
|
+
onClick={() => {
|
|
193
|
+
props.editor.view.focus();
|
|
194
|
+
props.editor.commands.sinkListItem("block");
|
|
195
|
+
}}
|
|
196
|
+
isDisabled={!props.editor.can().sinkListItem("block")}
|
|
174
197
|
mainTooltip="Indent"
|
|
175
198
|
secondaryTooltip={formatKeyboardShortcut("Tab")}
|
|
176
199
|
icon={RiIndentIncrease}
|
|
177
200
|
/>
|
|
178
201
|
|
|
179
|
-
<
|
|
180
|
-
onClick={() =>
|
|
181
|
-
props.editor.
|
|
182
|
-
|
|
202
|
+
<ToolbarButton
|
|
203
|
+
onClick={() => {
|
|
204
|
+
props.editor.view.focus();
|
|
205
|
+
props.editor.commands.liftListItem("block");
|
|
206
|
+
}}
|
|
183
207
|
isDisabled={
|
|
184
208
|
!props.editor.can().command(({ state }) => {
|
|
185
209
|
const block = findBlock(state.selection);
|
|
@@ -204,13 +228,13 @@ export const BubbleMenu = (props: { editor: Editor }) => {
|
|
|
204
228
|
editor={props.editor}
|
|
205
229
|
/>
|
|
206
230
|
{/* <SimpleBubbleMenuButton
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
231
|
+
editor={props.editor}
|
|
232
|
+
onClick={() => {
|
|
233
|
+
const comment = this.props.commentStore.createComment();
|
|
234
|
+
props.editor.chain().focus().setComment(comment.id).run();
|
|
235
|
+
}}
|
|
236
|
+
styleDetails={comment}
|
|
237
|
+
/> */}
|
|
214
238
|
</Toolbar>
|
|
215
239
|
);
|
|
216
240
|
};
|