@blocknote/core 0.19.0 → 0.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/blocknote.js +2990 -2898
- package/dist/blocknote.js.map +1 -1
- package/dist/blocknote.umd.cjs +7 -7
- package/dist/blocknote.umd.cjs.map +1 -1
- package/dist/src/api/blockManipulation/commands/moveBlock/moveBlock.test.js +5 -1
- package/dist/src/api/blockManipulation/commands/moveBlock/moveBlock.test.js.map +1 -1
- package/dist/src/api/blockManipulation/commands/removeBlocks/removeBlocks.js +2 -40
- package/dist/src/api/blockManipulation/commands/removeBlocks/removeBlocks.js.map +1 -1
- package/dist/src/api/blockManipulation/commands/removeBlocks/removeBlocks.test.js +4 -0
- package/dist/src/api/blockManipulation/commands/removeBlocks/removeBlocks.test.js.map +1 -1
- package/dist/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.js +51 -9
- package/dist/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.js.map +1 -1
- package/dist/src/api/blockManipulation/commands/splitBlock/splitBlock.js +2 -2
- package/dist/src/api/blockManipulation/commands/splitBlock/splitBlock.js.map +1 -1
- package/dist/src/api/clipboard/fromClipboard/acceptedMIMETypes.js +1 -1
- package/dist/src/api/clipboard/fromClipboard/acceptedMIMETypes.js.map +1 -1
- package/dist/src/api/clipboard/fromClipboard/handleFileInsertion.js +4 -2
- package/dist/src/api/clipboard/fromClipboard/handleFileInsertion.js.map +1 -1
- package/dist/src/api/clipboard/fromClipboard/handleVSCodePaste.js +3 -3
- package/dist/src/api/clipboard/fromClipboard/handleVSCodePaste.js.map +1 -1
- package/dist/src/api/getBlockInfoFromPos.js +19 -25
- package/dist/src/api/getBlockInfoFromPos.js.map +1 -1
- package/dist/src/blocks/CodeBlockContent/CodeBlockContent.js +15 -7
- package/dist/src/blocks/CodeBlockContent/CodeBlockContent.js.map +1 -1
- package/dist/src/blocks/CodeBlockContent/defaultSupportedLanguages.js +38 -18
- package/dist/src/blocks/CodeBlockContent/defaultSupportedLanguages.js.map +1 -1
- package/dist/src/blocks/HeadingBlockContent/HeadingBlockContent.js +8 -4
- package/dist/src/blocks/HeadingBlockContent/HeadingBlockContent.js.map +1 -1
- package/dist/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.js +5 -3
- package/dist/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.js.map +1 -1
- package/dist/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.js +12 -6
- package/dist/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.js.map +1 -1
- package/dist/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.js +5 -1
- package/dist/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.js.map +1 -1
- package/dist/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.js +4 -2
- package/dist/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.js.map +1 -1
- package/dist/src/blocks/ParagraphBlockContent/ParagraphBlockContent.js +2 -1
- package/dist/src/blocks/ParagraphBlockContent/ParagraphBlockContent.js.map +1 -1
- package/dist/src/blocks/TableBlockContent/TableBlockContent.js +0 -1
- package/dist/src/blocks/TableBlockContent/TableBlockContent.js.map +1 -1
- package/dist/src/editor/BlockNoteEditor.js +42 -43
- package/dist/src/editor/BlockNoteEditor.js.map +1 -1
- package/dist/src/editor/BlockNoteEditor.test.js +2 -2
- package/dist/src/editor/BlockNoteEditor.test.js.map +1 -1
- package/dist/src/editor/BlockNoteExtensions.js +52 -6
- package/dist/src/editor/BlockNoteExtensions.js.map +1 -1
- package/dist/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.js +36 -6
- package/dist/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.js.map +1 -1
- package/dist/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.js +35 -32
- package/dist/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.js.map +1 -1
- package/dist/src/extensions/Placeholder/PlaceholderPlugin.js +74 -71
- package/dist/src/extensions/Placeholder/PlaceholderPlugin.js.map +1 -1
- package/dist/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.js +153 -149
- package/dist/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.js.map +1 -1
- package/dist/src/extensions/SideMenu/SideMenuPlugin.js +5 -1
- package/dist/src/extensions/SideMenu/SideMenuPlugin.js.map +1 -1
- package/dist/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.js +0 -3
- package/dist/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.js.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +2 -2
- package/src/api/blockManipulation/commands/insertBlocks/__snapshots__/insertBlocks.test.ts.snap +0 -6
- package/src/api/blockManipulation/commands/mergeBlocks/__snapshots__/mergeBlocks.test.ts.snap +0 -5
- package/src/api/blockManipulation/commands/moveBlock/__snapshots__/moveBlock.test.ts.snap +0 -8
- package/src/api/blockManipulation/commands/moveBlock/moveBlock.test.ts +7 -3
- package/src/api/blockManipulation/commands/removeBlocks/__snapshots__/removeBlocks.test.ts.snap +439 -2
- package/src/api/blockManipulation/commands/removeBlocks/removeBlocks.test.ts +6 -0
- package/src/api/blockManipulation/commands/removeBlocks/removeBlocks.ts +2 -82
- package/src/api/blockManipulation/commands/replaceBlocks/__snapshots__/replaceBlocks.test.ts.snap +0 -8
- package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +96 -20
- package/src/api/blockManipulation/commands/splitBlock/__snapshots__/splitBlock.test.ts.snap +0 -6
- package/src/api/blockManipulation/commands/splitBlock/splitBlock.ts +2 -5
- package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +0 -490
- package/src/api/clipboard/fromClipboard/acceptedMIMETypes.ts +1 -1
- package/src/api/clipboard/fromClipboard/handleFileInsertion.ts +6 -5
- package/src/api/clipboard/fromClipboard/handleVSCodePaste.ts +4 -4
- package/src/api/getBlockInfoFromPos.ts +20 -30
- package/src/api/parsers/html/__snapshots__/parse-notion-html.json +1 -2
- package/src/blocks/CodeBlockContent/CodeBlockContent.ts +18 -8
- package/src/blocks/CodeBlockContent/defaultSupportedLanguages.ts +38 -18
- package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +16 -4
- package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +9 -3
- package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +22 -6
- package/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts +5 -3
- package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +8 -2
- package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +4 -1
- package/src/blocks/TableBlockContent/TableBlockContent.ts +0 -1
- package/src/editor/Block.css +3 -0
- package/src/editor/BlockNoteEditor.test.ts +2 -5
- package/src/editor/BlockNoteEditor.ts +77 -42
- package/src/editor/BlockNoteExtensions.ts +90 -14
- package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +36 -9
- package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts +45 -42
- package/src/extensions/Placeholder/PlaceholderPlugin.ts +94 -90
- package/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts +173 -169
- package/src/extensions/SideMenu/SideMenuPlugin.ts +5 -1
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +0 -5
- package/src/index.ts +1 -0
- package/types/src/api/blockManipulation/commands/removeBlocks/removeBlocks.d.ts +0 -3
- package/types/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.d.ts +4 -0
- package/types/src/api/blockManipulation/setupTestEnv.d.ts +0 -6
- package/types/src/api/clipboard/fromClipboard/acceptedMIMETypes.d.ts +1 -1
- package/types/src/api/getBlockInfoFromPos.d.ts +9 -34
- package/types/src/api/testUtil/cases/customBlocks.d.ts +0 -6
- package/types/src/api/testUtil/cases/customInlineContent.d.ts +0 -6
- package/types/src/api/testUtil/cases/customStyles.d.ts +0 -6
- package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +0 -9
- package/types/src/blocks/defaultBlocks.d.ts +0 -12
- package/types/src/editor/BlockNoteEditor.d.ts +19 -2
- package/types/src/editor/BlockNoteExtensions.d.ts +14 -7
- package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.d.ts +4 -1
- package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +4 -1
- package/types/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.d.ts +4 -5
- package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +1 -1
- package/types/src/index.d.ts +1 -0
|
@@ -45,22 +45,21 @@ export type BlockInfo = {
|
|
|
45
45
|
);
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
* Retrieves the position just before the nearest
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* just before the last blockContainer is returned.
|
|
48
|
+
* Retrieves the position just before the nearest block node in a ProseMirror
|
|
49
|
+
* doc, relative to a position. If the position is within a block node or its
|
|
50
|
+
* descendants, the position just before it is returned. If the position is not
|
|
51
|
+
* within a block node or its descendants, the position just before the next
|
|
52
|
+
* closest block node is returned. If the position is beyond the last block, the
|
|
53
|
+
* position just before the last block is returned.
|
|
55
54
|
* @param doc The ProseMirror doc.
|
|
56
55
|
* @param pos An integer position in the document.
|
|
57
56
|
* @returns The position just before the nearest blockContainer node.
|
|
58
57
|
*/
|
|
59
|
-
export function
|
|
58
|
+
export function getNearestBlockPos(doc: Node, pos: number) {
|
|
60
59
|
const $pos = doc.resolve(pos);
|
|
61
60
|
|
|
62
|
-
// Checks if the position provided is already just before a
|
|
63
|
-
//
|
|
61
|
+
// Checks if the position provided is already just before a block node, in
|
|
62
|
+
// which case we return the position.
|
|
64
63
|
if ($pos.nodeAfter && $pos.nodeAfter.type.isInGroup("bnBlock")) {
|
|
65
64
|
return {
|
|
66
65
|
posBeforeNode: $pos.pos,
|
|
@@ -69,7 +68,7 @@ export function getNearestBlockContainerPos(doc: Node, pos: number) {
|
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
// Checks the node containing the position and its ancestors until a
|
|
72
|
-
//
|
|
71
|
+
// block node is found and returned.
|
|
73
72
|
let depth = $pos.depth;
|
|
74
73
|
let node = $pos.node(depth);
|
|
75
74
|
while (depth > 0) {
|
|
@@ -84,13 +83,12 @@ export function getNearestBlockContainerPos(doc: Node, pos: number) {
|
|
|
84
83
|
node = $pos.node(depth);
|
|
85
84
|
}
|
|
86
85
|
|
|
87
|
-
// If the position doesn't lie within a
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
// collaboration plugin.
|
|
86
|
+
// If the position doesn't lie within a block node, we instead find the
|
|
87
|
+
// position of the next closest one. If the position is beyond the last block,
|
|
88
|
+
// we return the position of the last block. While running `doc.descendants`
|
|
89
|
+
// is expensive, this case should be very rarely triggered. However, it's
|
|
90
|
+
// possible for the position to sometimes be beyond the last block node. This
|
|
91
|
+
// is a problem specifically when using the collaboration plugin.
|
|
94
92
|
const allBlockContainerPositions: number[] = [];
|
|
95
93
|
doc.descendants((node, pos) => {
|
|
96
94
|
if (node.type.isInGroup("bnBlock")) {
|
|
@@ -119,7 +117,7 @@ export function getNearestBlockContainerPos(doc: Node, pos: number) {
|
|
|
119
117
|
* the ProseMirror positions just before & after each node.
|
|
120
118
|
* @param node The main `blockContainer` node that the block information should
|
|
121
119
|
* be retrieved from,
|
|
122
|
-
* @param
|
|
120
|
+
* @param bnBlockBeforePosOffset the position just before the
|
|
123
121
|
* `blockContainer` node in the document.
|
|
124
122
|
*/
|
|
125
123
|
export function getBlockInfoWithManualOffset(
|
|
@@ -237,15 +235,7 @@ export function getBlockInfoFromResolvedPos(resolvedPos: ResolvedPos) {
|
|
|
237
235
|
* @param state The ProseMirror editor state.
|
|
238
236
|
*/
|
|
239
237
|
export function getBlockInfoFromSelection(state: EditorState) {
|
|
240
|
-
const posInfo =
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
);
|
|
244
|
-
const ret = getBlockInfo(posInfo);
|
|
245
|
-
if (!ret.isBlockContainer) {
|
|
246
|
-
throw new Error(
|
|
247
|
-
`selection always expected to return blockContainer ${state.selection.anchor}`
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
return ret;
|
|
238
|
+
const posInfo = getNearestBlockPos(state.doc, state.selection.anchor);
|
|
239
|
+
|
|
240
|
+
return getBlockInfo(posInfo);
|
|
251
241
|
}
|
|
@@ -47,11 +47,15 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
|
|
|
47
47
|
};
|
|
48
48
|
},
|
|
49
49
|
addAttributes() {
|
|
50
|
+
const supportedLanguages = this.options
|
|
51
|
+
.supportedLanguages as SupportedLanguageConfig[];
|
|
52
|
+
|
|
50
53
|
return {
|
|
51
54
|
language: {
|
|
52
55
|
default: this.options.defaultLanguage,
|
|
53
56
|
parseHTML: (inputElement) => {
|
|
54
57
|
let element = inputElement as HTMLElement | null;
|
|
58
|
+
let language: string | null = null;
|
|
55
59
|
|
|
56
60
|
if (
|
|
57
61
|
element?.tagName === "DIV" &&
|
|
@@ -67,20 +71,26 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
|
|
|
67
71
|
const dataLanguage = element?.getAttribute("data-language");
|
|
68
72
|
|
|
69
73
|
if (dataLanguage) {
|
|
70
|
-
|
|
74
|
+
language = dataLanguage.toLowerCase();
|
|
75
|
+
} else {
|
|
76
|
+
const classNames = [...(element?.className.split(" ") || [])];
|
|
77
|
+
const languages = classNames
|
|
78
|
+
.filter((className) => className.startsWith("language-"))
|
|
79
|
+
.map((className) => className.replace("language-", ""));
|
|
80
|
+
const [classLanguage] = languages;
|
|
81
|
+
|
|
82
|
+
language = classLanguage.toLowerCase();
|
|
71
83
|
}
|
|
72
84
|
|
|
73
|
-
const classNames = [...(element?.className.split(" ") || [])];
|
|
74
|
-
const languages = classNames
|
|
75
|
-
.filter((className) => className.startsWith("language-"))
|
|
76
|
-
.map((className) => className.replace("language-", ""));
|
|
77
|
-
const [language] = languages;
|
|
78
|
-
|
|
79
85
|
if (!language) {
|
|
80
86
|
return null;
|
|
81
87
|
}
|
|
82
88
|
|
|
83
|
-
return
|
|
89
|
+
return (
|
|
90
|
+
supportedLanguages.find(({ match }) => {
|
|
91
|
+
return match.includes(language);
|
|
92
|
+
})?.id || this.options.defaultLanguage
|
|
93
|
+
);
|
|
84
94
|
},
|
|
85
95
|
renderHTML: (attributes) => {
|
|
86
96
|
return attributes.language && attributes.language !== "text"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { bundledLanguagesInfo } from "shiki
|
|
1
|
+
import { bundledLanguagesInfo } from "shiki";
|
|
2
2
|
|
|
3
3
|
export type SupportedLanguageConfig = {
|
|
4
4
|
id: string;
|
|
@@ -14,23 +14,42 @@ export const defaultSupportedLanguages: SupportedLanguageConfig[] = [
|
|
|
14
14
|
},
|
|
15
15
|
...bundledLanguagesInfo
|
|
16
16
|
.filter((lang) => {
|
|
17
|
-
return
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"html
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
17
|
+
return [
|
|
18
|
+
"c",
|
|
19
|
+
"cpp",
|
|
20
|
+
"css",
|
|
21
|
+
"glsl",
|
|
22
|
+
"graphql",
|
|
23
|
+
"haml",
|
|
24
|
+
"html",
|
|
25
|
+
"java",
|
|
26
|
+
"javascript",
|
|
27
|
+
"json",
|
|
28
|
+
"jsonc",
|
|
29
|
+
"jsonl",
|
|
30
|
+
"jsx",
|
|
31
|
+
"julia",
|
|
32
|
+
"less",
|
|
33
|
+
"markdown",
|
|
34
|
+
"mdx",
|
|
35
|
+
"php",
|
|
36
|
+
"postcss",
|
|
37
|
+
"pug",
|
|
38
|
+
"python",
|
|
39
|
+
"r",
|
|
40
|
+
"regexp",
|
|
41
|
+
"sass",
|
|
42
|
+
"scss",
|
|
43
|
+
"shellscript",
|
|
44
|
+
"sql",
|
|
45
|
+
"svelte",
|
|
46
|
+
"typescript",
|
|
47
|
+
"vue",
|
|
48
|
+
"vue-html",
|
|
49
|
+
"wasm",
|
|
50
|
+
"wgsl",
|
|
51
|
+
"xml",
|
|
52
|
+
"yaml",
|
|
34
53
|
].includes(lang.id);
|
|
35
54
|
})
|
|
36
55
|
.map((lang) => ({
|
|
@@ -38,6 +57,7 @@ export const defaultSupportedLanguages: SupportedLanguageConfig[] = [
|
|
|
38
57
|
id: lang.id,
|
|
39
58
|
name: lang.name,
|
|
40
59
|
})),
|
|
60
|
+
{ id: "tsx", name: "TSX", match: ["tsx", "typescriptreact"] },
|
|
41
61
|
{
|
|
42
62
|
id: "haskell",
|
|
43
63
|
name: "Haskell",
|
|
@@ -48,7 +48,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
|
|
|
48
48
|
find: new RegExp(`^(#{${level}})\\s$`),
|
|
49
49
|
handler: ({ state, chain, range }) => {
|
|
50
50
|
const blockInfo = getBlockInfoFromSelection(state);
|
|
51
|
-
if (
|
|
51
|
+
if (
|
|
52
|
+
!blockInfo.isBlockContainer ||
|
|
53
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
54
|
+
) {
|
|
52
55
|
return;
|
|
53
56
|
}
|
|
54
57
|
|
|
@@ -78,7 +81,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
|
|
|
78
81
|
return {
|
|
79
82
|
"Mod-Alt-1": () => {
|
|
80
83
|
const blockInfo = getBlockInfoFromSelection(this.editor.state);
|
|
81
|
-
if (
|
|
84
|
+
if (
|
|
85
|
+
!blockInfo.isBlockContainer ||
|
|
86
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
87
|
+
) {
|
|
82
88
|
return true;
|
|
83
89
|
}
|
|
84
90
|
|
|
@@ -94,7 +100,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
|
|
|
94
100
|
},
|
|
95
101
|
"Mod-Alt-2": () => {
|
|
96
102
|
const blockInfo = getBlockInfoFromSelection(this.editor.state);
|
|
97
|
-
if (
|
|
103
|
+
if (
|
|
104
|
+
!blockInfo.isBlockContainer ||
|
|
105
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
106
|
+
) {
|
|
98
107
|
return true;
|
|
99
108
|
}
|
|
100
109
|
|
|
@@ -109,7 +118,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
|
|
|
109
118
|
},
|
|
110
119
|
"Mod-Alt-3": () => {
|
|
111
120
|
const blockInfo = getBlockInfoFromSelection(this.editor.state);
|
|
112
|
-
if (
|
|
121
|
+
if (
|
|
122
|
+
!blockInfo.isBlockContainer ||
|
|
123
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
124
|
+
) {
|
|
113
125
|
return true;
|
|
114
126
|
}
|
|
115
127
|
|
package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts
CHANGED
|
@@ -28,7 +28,10 @@ const BulletListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
28
28
|
find: new RegExp(`^[-+*]\\s$`),
|
|
29
29
|
handler: ({ state, chain, range }) => {
|
|
30
30
|
const blockInfo = getBlockInfoFromSelection(state);
|
|
31
|
-
if (
|
|
31
|
+
if (
|
|
32
|
+
!blockInfo.isBlockContainer ||
|
|
33
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
34
|
+
) {
|
|
32
35
|
return;
|
|
33
36
|
}
|
|
34
37
|
|
|
@@ -55,11 +58,14 @@ const BulletListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
55
58
|
Enter: () => handleEnter(this.options.editor),
|
|
56
59
|
"Mod-Shift-8": () => {
|
|
57
60
|
const blockInfo = getBlockInfoFromSelection(this.editor.state);
|
|
58
|
-
if (
|
|
61
|
+
if (
|
|
62
|
+
!blockInfo.isBlockContainer ||
|
|
63
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
64
|
+
) {
|
|
59
65
|
return true;
|
|
60
66
|
}
|
|
61
67
|
|
|
62
|
-
return this.
|
|
68
|
+
return this.editor.commands.command(
|
|
63
69
|
updateBlockCommand(this.options.editor, blockInfo.bnBlock.beforePos, {
|
|
64
70
|
type: "bulletListItem",
|
|
65
71
|
props: {},
|
package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { InputRule } from "@tiptap/core";
|
|
|
2
2
|
import { updateBlockCommand } from "../../../api/blockManipulation/commands/updateBlock/updateBlock.js";
|
|
3
3
|
import {
|
|
4
4
|
getBlockInfoFromSelection,
|
|
5
|
-
|
|
5
|
+
getNearestBlockPos,
|
|
6
6
|
} from "../../../api/getBlockInfoFromPos.js";
|
|
7
7
|
import {
|
|
8
8
|
PropSchema,
|
|
@@ -49,7 +49,10 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
49
49
|
find: new RegExp(`\\[\\s*\\]\\s$`),
|
|
50
50
|
handler: ({ state, chain, range }) => {
|
|
51
51
|
const blockInfo = getBlockInfoFromSelection(state);
|
|
52
|
-
if (
|
|
52
|
+
if (
|
|
53
|
+
!blockInfo.isBlockContainer ||
|
|
54
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
55
|
+
) {
|
|
53
56
|
return;
|
|
54
57
|
}
|
|
55
58
|
|
|
@@ -75,7 +78,10 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
75
78
|
handler: ({ state, chain, range }) => {
|
|
76
79
|
const blockInfo = getBlockInfoFromSelection(state);
|
|
77
80
|
|
|
78
|
-
if (
|
|
81
|
+
if (
|
|
82
|
+
!blockInfo.isBlockContainer ||
|
|
83
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
84
|
+
) {
|
|
79
85
|
return;
|
|
80
86
|
}
|
|
81
87
|
|
|
@@ -103,8 +109,11 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
103
109
|
return {
|
|
104
110
|
Enter: () => handleEnter(this.options.editor),
|
|
105
111
|
"Mod-Shift-9": () => {
|
|
106
|
-
const blockInfo = getBlockInfoFromSelection(this.
|
|
107
|
-
if (
|
|
112
|
+
const blockInfo = getBlockInfoFromSelection(this.editor.state);
|
|
113
|
+
if (
|
|
114
|
+
!blockInfo.isBlockContainer ||
|
|
115
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
116
|
+
) {
|
|
108
117
|
return true;
|
|
109
118
|
}
|
|
110
119
|
|
|
@@ -232,10 +241,17 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
232
241
|
|
|
233
242
|
// TODO: test
|
|
234
243
|
if (typeof getPos !== "boolean") {
|
|
235
|
-
const beforeBlockContainerPos =
|
|
244
|
+
const beforeBlockContainerPos = getNearestBlockPos(
|
|
236
245
|
editor.state.doc,
|
|
237
246
|
getPos()
|
|
238
247
|
);
|
|
248
|
+
|
|
249
|
+
if (beforeBlockContainerPos.node.type.name !== "blockContainer") {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`Expected blockContainer node, got ${beforeBlockContainerPos.node.type.name}`
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
239
255
|
this.editor.commands.command(
|
|
240
256
|
updateBlockCommand(
|
|
241
257
|
this.options.editor,
|
|
@@ -5,9 +5,11 @@ import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
|
|
|
5
5
|
|
|
6
6
|
export const handleEnter = (editor: BlockNoteEditor<any, any, any>) => {
|
|
7
7
|
const ttEditor = editor._tiptapEditor;
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
const blockInfo = getBlockInfoFromSelection(ttEditor.state);
|
|
9
|
+
if (!blockInfo.isBlockContainer) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const { bnBlock: blockContainer, blockContent } = blockInfo;
|
|
11
13
|
|
|
12
14
|
const selectionEmpty =
|
|
13
15
|
ttEditor.state.selection.anchor === ttEditor.state.selection.head;
|
package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts
CHANGED
|
@@ -41,7 +41,10 @@ const NumberedListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
41
41
|
find: new RegExp(`^1\\.\\s$`),
|
|
42
42
|
handler: ({ state, chain, range }) => {
|
|
43
43
|
const blockInfo = getBlockInfoFromSelection(state);
|
|
44
|
-
if (
|
|
44
|
+
if (
|
|
45
|
+
!blockInfo.isBlockContainer ||
|
|
46
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
47
|
+
) {
|
|
45
48
|
return;
|
|
46
49
|
}
|
|
47
50
|
|
|
@@ -68,7 +71,10 @@ const NumberedListItemBlockContent = createStronglyTypedTiptapNode({
|
|
|
68
71
|
Enter: () => handleEnter(this.options.editor),
|
|
69
72
|
"Mod-Shift-7": () => {
|
|
70
73
|
const blockInfo = getBlockInfoFromSelection(this.editor.state);
|
|
71
|
-
if (
|
|
74
|
+
if (
|
|
75
|
+
!blockInfo.isBlockContainer ||
|
|
76
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
77
|
+
) {
|
|
72
78
|
return true;
|
|
73
79
|
}
|
|
74
80
|
|
|
@@ -20,7 +20,10 @@ export const ParagraphBlockContent = createStronglyTypedTiptapNode({
|
|
|
20
20
|
return {
|
|
21
21
|
"Mod-Alt-0": () => {
|
|
22
22
|
const blockInfo = getBlockInfoFromSelection(this.editor.state);
|
|
23
|
-
if (
|
|
23
|
+
if (
|
|
24
|
+
!blockInfo.isBlockContainer ||
|
|
25
|
+
blockInfo.blockContent.node.type.spec.content !== "inline*"
|
|
26
|
+
) {
|
|
24
27
|
return true;
|
|
25
28
|
}
|
|
26
29
|
|
package/src/editor/Block.css
CHANGED
|
@@ -299,6 +299,9 @@ NESTED BLOCKS
|
|
|
299
299
|
transition: opacity 0.3s;
|
|
300
300
|
transition-delay: 1s;
|
|
301
301
|
}
|
|
302
|
+
.bn-block-content[data-content-type="codeBlock"] > div > select > option {
|
|
303
|
+
color: black;
|
|
304
|
+
}
|
|
302
305
|
.bn-block-content[data-content-type="codeBlock"]:hover > div > select,
|
|
303
306
|
.bn-block-content[data-content-type="codeBlock"] > div > select:focus {
|
|
304
307
|
opacity: 0.5;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { expect, it } from "vitest";
|
|
2
2
|
import {
|
|
3
3
|
getBlockInfo,
|
|
4
|
-
|
|
4
|
+
getNearestBlockPos,
|
|
5
5
|
} from "../api/getBlockInfoFromPos.js";
|
|
6
6
|
import { BlockNoteEditor } from "./BlockNoteEditor.js";
|
|
7
7
|
|
|
@@ -10,10 +10,7 @@ import { BlockNoteEditor } from "./BlockNoteEditor.js";
|
|
|
10
10
|
*/
|
|
11
11
|
it("creates an editor", () => {
|
|
12
12
|
const editor = BlockNoteEditor.create();
|
|
13
|
-
const posInfo =
|
|
14
|
-
editor._tiptapEditor.state.doc,
|
|
15
|
-
2
|
|
16
|
-
);
|
|
13
|
+
const posInfo = getNearestBlockPos(editor._tiptapEditor.state.doc, 2);
|
|
17
14
|
const info = getBlockInfo(posInfo);
|
|
18
15
|
expect(info.blockNoteType).toEqual("paragraph");
|
|
19
16
|
});
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AnyExtension,
|
|
3
|
+
EditorOptions,
|
|
4
|
+
Extension,
|
|
5
|
+
getSchema,
|
|
6
|
+
Mark,
|
|
7
|
+
Node as TipTapNode,
|
|
8
|
+
} from "@tiptap/core";
|
|
2
9
|
import { Node, Schema } from "prosemirror-model";
|
|
3
10
|
// import "./blocknote.css";
|
|
4
11
|
import * as Y from "yjs";
|
|
@@ -47,9 +54,9 @@ import {
|
|
|
47
54
|
InlineContentSchema,
|
|
48
55
|
InlineContentSpecs,
|
|
49
56
|
PartialInlineContent,
|
|
57
|
+
Styles,
|
|
50
58
|
StyleSchema,
|
|
51
59
|
StyleSpecs,
|
|
52
|
-
Styles,
|
|
53
60
|
} from "../schema/index.js";
|
|
54
61
|
import { mergeCSSClasses } from "../util/browser.js";
|
|
55
62
|
import { NoInfer, UnreachableCaseError } from "../util/typescript.js";
|
|
@@ -67,7 +74,6 @@ import {
|
|
|
67
74
|
BlockNoteTipTapEditorOptions,
|
|
68
75
|
} from "./BlockNoteTipTapEditor.js";
|
|
69
76
|
|
|
70
|
-
import { PlaceholderPlugin } from "../extensions/Placeholder/PlaceholderPlugin.js";
|
|
71
77
|
import { Dictionary } from "../i18n/dictionary.js";
|
|
72
78
|
import { en } from "../i18n/locales/index.js";
|
|
73
79
|
|
|
@@ -76,10 +82,14 @@ import { dropCursor } from "prosemirror-dropcursor";
|
|
|
76
82
|
import { createInternalHTMLSerializer } from "../api/exporters/html/internalHTMLSerializer.js";
|
|
77
83
|
import { inlineContentToNodes } from "../api/nodeConversions/blockToNode.js";
|
|
78
84
|
import { nodeToBlock } from "../api/nodeConversions/nodeToBlock.js";
|
|
79
|
-
import { NodeSelectionKeyboardPlugin } from "../extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.js";
|
|
80
|
-
import { PreviousBlockTypePlugin } from "../extensions/PreviousBlockType/PreviousBlockTypePlugin.js";
|
|
81
85
|
import "../style.css";
|
|
82
86
|
|
|
87
|
+
export type BlockNoteExtension =
|
|
88
|
+
| AnyExtension
|
|
89
|
+
| {
|
|
90
|
+
plugin: Plugin;
|
|
91
|
+
};
|
|
92
|
+
|
|
83
93
|
export type BlockNoteEditorOptions<
|
|
84
94
|
BSchema extends BlockSchema,
|
|
85
95
|
ISchema extends InlineContentSchema,
|
|
@@ -92,7 +102,11 @@ export type BlockNoteEditorOptions<
|
|
|
92
102
|
*/
|
|
93
103
|
animations?: boolean;
|
|
94
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Disable internal extensions (based on keys / extension name)
|
|
107
|
+
*/
|
|
95
108
|
disableExtensions: string[];
|
|
109
|
+
|
|
96
110
|
/**
|
|
97
111
|
* A dictionary object containing translations for the editor.
|
|
98
112
|
*/
|
|
@@ -173,9 +187,16 @@ export type BlockNoteEditorOptions<
|
|
|
173
187
|
renderCursor?: (user: any) => HTMLElement;
|
|
174
188
|
};
|
|
175
189
|
|
|
176
|
-
|
|
190
|
+
/**
|
|
191
|
+
* additional tiptap options, undocumented
|
|
192
|
+
*/
|
|
177
193
|
_tiptapOptions: Partial<EditorOptions>;
|
|
178
194
|
|
|
195
|
+
/**
|
|
196
|
+
* (experimental) add extra prosemirror plugins or tiptap extensions to the editor
|
|
197
|
+
*/
|
|
198
|
+
_extensions: Record<string, BlockNoteExtension>;
|
|
199
|
+
|
|
179
200
|
trailingBlock?: boolean;
|
|
180
201
|
|
|
181
202
|
/**
|
|
@@ -213,6 +234,11 @@ export class BlockNoteEditor<
|
|
|
213
234
|
> {
|
|
214
235
|
private readonly _pmSchema: Schema;
|
|
215
236
|
|
|
237
|
+
/**
|
|
238
|
+
* extensions that are added to the editor, can be tiptap extensions or prosemirror plugins
|
|
239
|
+
*/
|
|
240
|
+
public readonly extensions: Record<string, BlockNoteExtension> = {};
|
|
241
|
+
|
|
216
242
|
/**
|
|
217
243
|
* Boolean indicating whether the editor is in headless mode.
|
|
218
244
|
* Headless mode means we can use features like importing / exporting blocks,
|
|
@@ -355,17 +381,7 @@ export class BlockNoteEditor<
|
|
|
355
381
|
this.inlineContentImplementations = newOptions.schema.inlineContentSpecs;
|
|
356
382
|
this.styleImplementations = newOptions.schema.styleSpecs;
|
|
357
383
|
|
|
358
|
-
this.
|
|
359
|
-
this.linkToolbar = new LinkToolbarProsemirrorPlugin(this);
|
|
360
|
-
this.sideMenu = new SideMenuProsemirrorPlugin(this);
|
|
361
|
-
this.suggestionMenus = new SuggestionMenuProseMirrorPlugin(this);
|
|
362
|
-
this.filePanel = new FilePanelProsemirrorPlugin(this as any);
|
|
363
|
-
|
|
364
|
-
if (checkDefaultBlockTypeInSchema("table", this)) {
|
|
365
|
-
this.tableHandles = new TableHandlesProsemirrorPlugin(this as any);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
const extensions = getBlockNoteExtensions({
|
|
384
|
+
this.extensions = getBlockNoteExtensions({
|
|
369
385
|
editor: this,
|
|
370
386
|
domAttributes: newOptions.domAttributes || {},
|
|
371
387
|
blockSpecs: this.schema.blockSpecs,
|
|
@@ -375,30 +391,28 @@ export class BlockNoteEditor<
|
|
|
375
391
|
trailingBlock: newOptions.trailingBlock,
|
|
376
392
|
disableExtensions: newOptions.disableExtensions,
|
|
377
393
|
setIdAttribute: newOptions.setIdAttribute,
|
|
394
|
+
animations: newOptions.animations ?? true,
|
|
395
|
+
tableHandles: checkDefaultBlockTypeInSchema("table", this),
|
|
396
|
+
dropCursor: this.options.dropCursor ?? dropCursor,
|
|
397
|
+
placeholders: newOptions.placeholders,
|
|
378
398
|
});
|
|
379
399
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
name
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
this.sideMenu.plugin,
|
|
389
|
-
this.suggestionMenus.plugin,
|
|
390
|
-
...(this.filePanel ? [this.filePanel.plugin] : []),
|
|
391
|
-
...(this.tableHandles ? [this.tableHandles.plugin] : []),
|
|
392
|
-
dropCursorPlugin({ width: 5, color: "#ddeeff", editor: this }),
|
|
393
|
-
PlaceholderPlugin(this, newOptions.placeholders),
|
|
394
|
-
NodeSelectionKeyboardPlugin(),
|
|
395
|
-
...(this.options.animations ?? true
|
|
396
|
-
? [PreviousBlockTypePlugin()]
|
|
397
|
-
: []),
|
|
398
|
-
];
|
|
399
|
-
},
|
|
400
|
+
// add extensions from _tiptapOptions
|
|
401
|
+
(newOptions._tiptapOptions?.extensions || []).forEach((ext) => {
|
|
402
|
+
this.extensions[ext.name] = ext;
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// add extensions from options
|
|
406
|
+
Object.entries(newOptions._extensions || {}).forEach(([key, ext]) => {
|
|
407
|
+
this.extensions[key] = ext;
|
|
400
408
|
});
|
|
401
|
-
|
|
409
|
+
|
|
410
|
+
this.formattingToolbar = this.extensions["formattingToolbar"] as any;
|
|
411
|
+
this.linkToolbar = this.extensions["linkToolbar"] as any;
|
|
412
|
+
this.sideMenu = this.extensions["sideMenu"] as any;
|
|
413
|
+
this.suggestionMenus = this.extensions["suggestionMenus"] as any;
|
|
414
|
+
this.filePanel = this.extensions["filePanel"] as any;
|
|
415
|
+
this.tableHandles = this.extensions["tableHandles"] as any;
|
|
402
416
|
|
|
403
417
|
if (newOptions.uploadFile) {
|
|
404
418
|
const uploadFile = newOptions.uploadFile;
|
|
@@ -449,14 +463,35 @@ export class BlockNoteEditor<
|
|
|
449
463
|
);
|
|
450
464
|
}
|
|
451
465
|
|
|
466
|
+
const tiptapExtensions = [
|
|
467
|
+
...Object.entries(this.extensions).map(([key, ext]) => {
|
|
468
|
+
if (
|
|
469
|
+
ext instanceof Extension ||
|
|
470
|
+
ext instanceof TipTapNode ||
|
|
471
|
+
ext instanceof Mark
|
|
472
|
+
) {
|
|
473
|
+
// tiptap extension
|
|
474
|
+
return ext;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (!ext.plugin) {
|
|
478
|
+
throw new Error(
|
|
479
|
+
"Extension should either be a TipTap extension or a ProseMirror plugin in a plugin property"
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// "blocknote" extensions (prosemirror plugins)
|
|
484
|
+
return Extension.create({
|
|
485
|
+
name: key,
|
|
486
|
+
addProseMirrorPlugins: () => [ext.plugin],
|
|
487
|
+
});
|
|
488
|
+
}),
|
|
489
|
+
];
|
|
452
490
|
const tiptapOptions: BlockNoteTipTapEditorOptions = {
|
|
453
491
|
...blockNoteTipTapOptions,
|
|
454
492
|
...newOptions._tiptapOptions,
|
|
455
493
|
content: initialContent,
|
|
456
|
-
extensions:
|
|
457
|
-
...(newOptions._tiptapOptions?.extensions || []),
|
|
458
|
-
...extensions,
|
|
459
|
-
],
|
|
494
|
+
extensions: tiptapExtensions,
|
|
460
495
|
editorProps: {
|
|
461
496
|
...newOptions._tiptapOptions?.editorProps,
|
|
462
497
|
attributes: {
|