Skip to content

Commit 8200117

Browse files
fix: getNearestBlockContainerPos unclear (#1241)
* Made `getNearestBlockContainerPos` more clear * Added `blockContainer` check to check list item change event
1 parent 082950b commit 8200117

File tree

13 files changed

+133
-76
lines changed

13 files changed

+133
-76
lines changed

packages/core/src/api/blockManipulation/commands/moveBlock/moveBlock.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@ import {
1313
const getEditor = setupTestEnv();
1414

1515
function makeSelectionSpanContent(selectionType: "text" | "node" | "cell") {
16-
const { blockContent } = getBlockInfoFromSelection(
17-
getEditor()._tiptapEditor.state
18-
);
16+
const blockInfo = getBlockInfoFromSelection(getEditor()._tiptapEditor.state);
17+
if (!blockInfo.isBlockContainer) {
18+
throw new Error(
19+
`Selection points to a ${blockInfo.blockNoteType} node, not a blockContainer node`
20+
);
21+
}
22+
const { blockContent } = blockInfo;
1923

2024
if (selectionType === "cell") {
2125
getEditor()._tiptapEditor.view.dispatch(

packages/core/src/api/blockManipulation/commands/splitBlock/splitBlock.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { EditorState } from "prosemirror-state";
22

33
import {
44
getBlockInfo,
5-
getNearestBlockContainerPos,
5+
getNearestBlockPos,
66
} from "../../../getBlockInfoFromPos.js";
77

88
export const splitBlockCommand = (
@@ -17,10 +17,7 @@ export const splitBlockCommand = (
1717
state: EditorState;
1818
dispatch: ((args?: any) => any) | undefined;
1919
}) => {
20-
const nearestBlockContainerPos = getNearestBlockContainerPos(
21-
state.doc,
22-
posInBlock
23-
);
20+
const nearestBlockContainerPos = getNearestBlockPos(state.doc, posInBlock);
2421

2522
const info = getBlockInfo(nearestBlockContainerPos);
2623

packages/core/src/api/clipboard/fromClipboard/handleFileInsertion.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@ import {
66
InlineContentSchema,
77
StyleSchema,
88
} from "../../../schema/index.js";
9-
import {
10-
getBlockInfo,
11-
getNearestBlockContainerPos,
12-
} from "../../getBlockInfoFromPos.js";
9+
import { getBlockInfo, getNearestBlockPos } from "../../getBlockInfoFromPos.js";
1310
import { acceptedMIMETypes } from "./acceptedMIMETypes.js";
1411

1512
function checkFileExtensionsMatch(
@@ -139,7 +136,7 @@ export async function handleFileInsertion<
139136
return;
140137
}
141138

142-
const posInfo = getNearestBlockContainerPos(
139+
const posInfo = getNearestBlockPos(
143140
editor._tiptapEditor.state.doc,
144141
pos.pos
145142
);

packages/core/src/api/getBlockInfoFromPos.ts

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,21 @@ export type BlockInfo = {
4545
);
4646

4747
/**
48-
* Retrieves the position just before the nearest blockContainer node in a
49-
* ProseMirror doc, relative to a position. If the position is within a
50-
* blockContainer node or its descendants, the position just before it is
51-
* returned. If the position is not within a blockContainer node or its
52-
* descendants, the position just before the next closest blockContainer node
53-
* is returned. If the position is beyond the last blockContainer, the position
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.
5554
* @param doc The ProseMirror doc.
5655
* @param pos An integer position in the document.
5756
* @returns The position just before the nearest blockContainer node.
5857
*/
59-
export function getNearestBlockContainerPos(doc: Node, pos: number) {
58+
export function getNearestBlockPos(doc: Node, pos: number) {
6059
const $pos = doc.resolve(pos);
6160

62-
// Checks if the position provided is already just before a blockContainer
63-
// node, in which case we return the position.
61+
// Checks if the position provided is already just before a block node, in
62+
// which case we return the position.
6463
if ($pos.nodeAfter && $pos.nodeAfter.type.isInGroup("bnBlock")) {
6564
return {
6665
posBeforeNode: $pos.pos,
@@ -69,7 +68,7 @@ export function getNearestBlockContainerPos(doc: Node, pos: number) {
6968
}
7069

7170
// Checks the node containing the position and its ancestors until a
72-
// blockContainer node is found and returned.
71+
// block node is found and returned.
7372
let depth = $pos.depth;
7473
let node = $pos.node(depth);
7574
while (depth > 0) {
@@ -84,13 +83,12 @@ export function getNearestBlockContainerPos(doc: Node, pos: number) {
8483
node = $pos.node(depth);
8584
}
8685

87-
// If the position doesn't lie within a blockContainer node, we instead find
88-
// the position of the next closest one. If the position is beyond the last
89-
// blockContainer, we return the position of the last blockContainer. While
90-
// running `doc.descendants` is expensive, this case should be very rarely
91-
// triggered. However, it's possible for the position to sometimes be beyond
92-
// the last blockContainer node. This is a problem specifically when using the
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.
9492
const allBlockContainerPositions: number[] = [];
9593
doc.descendants((node, pos) => {
9694
if (node.type.isInGroup("bnBlock")) {
@@ -119,7 +117,7 @@ export function getNearestBlockContainerPos(doc: Node, pos: number) {
119117
* the ProseMirror positions just before & after each node.
120118
* @param node The main `blockContainer` node that the block information should
121119
* be retrieved from,
122-
* @param blockContainerBeforePosOffset the position just before the
120+
* @param bnBlockBeforePosOffset the position just before the
123121
* `blockContainer` node in the document.
124122
*/
125123
export function getBlockInfoWithManualOffset(
@@ -237,15 +235,7 @@ export function getBlockInfoFromResolvedPos(resolvedPos: ResolvedPos) {
237235
* @param state The ProseMirror editor state.
238236
*/
239237
export function getBlockInfoFromSelection(state: EditorState) {
240-
const posInfo = getNearestBlockContainerPos(
241-
state.doc,
242-
state.selection.anchor
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);
251241
}

packages/core/src/blocks/HeadingBlockContent/HeadingBlockContent.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
4848
find: new RegExp(`^(#{${level}})\\s$`),
4949
handler: ({ state, chain, range }) => {
5050
const blockInfo = getBlockInfoFromSelection(state);
51-
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
51+
if (
52+
!blockInfo.isBlockContainer ||
53+
blockInfo.blockContent.node.type.spec.content !== "inline*"
54+
) {
5255
return;
5356
}
5457

@@ -78,7 +81,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
7881
return {
7982
"Mod-Alt-1": () => {
8083
const blockInfo = getBlockInfoFromSelection(this.editor.state);
81-
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
84+
if (
85+
!blockInfo.isBlockContainer ||
86+
blockInfo.blockContent.node.type.spec.content !== "inline*"
87+
) {
8288
return true;
8389
}
8490

@@ -94,7 +100,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
94100
},
95101
"Mod-Alt-2": () => {
96102
const blockInfo = getBlockInfoFromSelection(this.editor.state);
97-
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
103+
if (
104+
!blockInfo.isBlockContainer ||
105+
blockInfo.blockContent.node.type.spec.content !== "inline*"
106+
) {
98107
return true;
99108
}
100109

@@ -109,7 +118,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
109118
},
110119
"Mod-Alt-3": () => {
111120
const blockInfo = getBlockInfoFromSelection(this.editor.state);
112-
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
121+
if (
122+
!blockInfo.isBlockContainer ||
123+
blockInfo.blockContent.node.type.spec.content !== "inline*"
124+
) {
113125
return true;
114126
}
115127

packages/core/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ const BulletListItemBlockContent = createStronglyTypedTiptapNode({
2828
find: new RegExp(`^[-+*]\\s$`),
2929
handler: ({ state, chain, range }) => {
3030
const blockInfo = getBlockInfoFromSelection(state);
31-
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
31+
if (
32+
!blockInfo.isBlockContainer ||
33+
blockInfo.blockContent.node.type.spec.content !== "inline*"
34+
) {
3235
return;
3336
}
3437

@@ -55,7 +58,10 @@ const BulletListItemBlockContent = createStronglyTypedTiptapNode({
5558
Enter: () => handleEnter(this.options.editor),
5659
"Mod-Shift-8": () => {
5760
const blockInfo = getBlockInfoFromSelection(this.editor.state);
58-
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
61+
if (
62+
!blockInfo.isBlockContainer ||
63+
blockInfo.blockContent.node.type.spec.content !== "inline*"
64+
) {
5965
return true;
6066
}
6167

packages/core/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { InputRule } from "@tiptap/core";
22
import { updateBlockCommand } from "../../../api/blockManipulation/commands/updateBlock/updateBlock.js";
33
import {
44
getBlockInfoFromSelection,
5-
getNearestBlockContainerPos,
5+
getNearestBlockPos,
66
} from "../../../api/getBlockInfoFromPos.js";
77
import {
88
PropSchema,
@@ -49,7 +49,10 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
4949
find: new RegExp(`\\[\\s*\\]\\s$`),
5050
handler: ({ state, chain, range }) => {
5151
const blockInfo = getBlockInfoFromSelection(state);
52-
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
52+
if (
53+
!blockInfo.isBlockContainer ||
54+
blockInfo.blockContent.node.type.spec.content !== "inline*"
55+
) {
5356
return;
5457
}
5558

@@ -75,7 +78,10 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
7578
handler: ({ state, chain, range }) => {
7679
const blockInfo = getBlockInfoFromSelection(state);
7780

78-
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
81+
if (
82+
!blockInfo.isBlockContainer ||
83+
blockInfo.blockContent.node.type.spec.content !== "inline*"
84+
) {
7985
return;
8086
}
8187

@@ -104,7 +110,10 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
104110
Enter: () => handleEnter(this.options.editor),
105111
"Mod-Shift-9": () => {
106112
const blockInfo = getBlockInfoFromSelection(this.options.editor.state);
107-
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
113+
if (
114+
!blockInfo.isBlockContainer ||
115+
blockInfo.blockContent.node.type.spec.content !== "inline*"
116+
) {
108117
return true;
109118
}
110119

@@ -232,10 +241,17 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
232241

233242
// TODO: test
234243
if (typeof getPos !== "boolean") {
235-
const beforeBlockContainerPos = getNearestBlockContainerPos(
244+
const beforeBlockContainerPos = getNearestBlockPos(
236245
editor.state.doc,
237246
getPos()
238247
);
248+
249+
if (beforeBlockContainerPos.node.type.name !== "blockContainer") {
250+
throw new Error(
251+
`Expected blockContainer node, got ${beforeBlockContainerPos.node.type.name}`
252+
);
253+
}
254+
239255
this.editor.commands.command(
240256
updateBlockCommand(
241257
this.options.editor,

packages/core/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
55

66
export const handleEnter = (editor: BlockNoteEditor<any, any, any>) => {
77
const ttEditor = editor._tiptapEditor;
8-
const { blockContent, bnBlock: blockContainer } = getBlockInfoFromSelection(
9-
ttEditor.state
10-
);
8+
const blockInfo = getBlockInfoFromSelection(ttEditor.state);
9+
if (!blockInfo.isBlockContainer) {
10+
return false;
11+
}
12+
const { bnBlock: blockContainer, blockContent } = blockInfo;
1113

1214
const selectionEmpty =
1315
ttEditor.state.selection.anchor === ttEditor.state.selection.head;

packages/core/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ const NumberedListItemBlockContent = createStronglyTypedTiptapNode({
4141
find: new RegExp(`^1\\.\\s$`),
4242
handler: ({ state, chain, range }) => {
4343
const blockInfo = getBlockInfoFromSelection(state);
44-
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
44+
if (
45+
!blockInfo.isBlockContainer ||
46+
blockInfo.blockContent.node.type.spec.content !== "inline*"
47+
) {
4548
return;
4649
}
4750

@@ -68,7 +71,10 @@ const NumberedListItemBlockContent = createStronglyTypedTiptapNode({
6871
Enter: () => handleEnter(this.options.editor),
6972
"Mod-Shift-7": () => {
7073
const blockInfo = getBlockInfoFromSelection(this.editor.state);
71-
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
74+
if (
75+
!blockInfo.isBlockContainer ||
76+
blockInfo.blockContent.node.type.spec.content !== "inline*"
77+
) {
7278
return true;
7379
}
7480

packages/core/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ export const ParagraphBlockContent = createStronglyTypedTiptapNode({
2020
return {
2121
"Mod-Alt-0": () => {
2222
const blockInfo = getBlockInfoFromSelection(this.editor.state);
23-
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
23+
if (
24+
!blockInfo.isBlockContainer ||
25+
blockInfo.blockContent.node.type.spec.content !== "inline*"
26+
) {
2427
return true;
2528
}
2629

0 commit comments

Comments
 (0)