Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ import {
const getEditor = setupTestEnv();

function makeSelectionSpanContent(selectionType: "text" | "node" | "cell") {
const { blockContent } = getBlockInfoFromSelection(
getEditor()._tiptapEditor.state
);
const blockInfo = getBlockInfoFromSelection(getEditor()._tiptapEditor.state);
if (!blockInfo.isBlockContainer) {
throw new Error(
`Selection points to a ${blockInfo.blockNoteType} node, not a blockContainer node`
);
}
const { blockContent } = blockInfo;

if (selectionType === "cell") {
getEditor()._tiptapEditor.view.dispatch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { EditorState } from "prosemirror-state";

import {
getBlockInfo,
getNearestBlockContainerPos,
getNearestBlockPos,
} from "../../../getBlockInfoFromPos.js";

export const splitBlockCommand = (
Expand All @@ -17,10 +17,7 @@ export const splitBlockCommand = (
state: EditorState;
dispatch: ((args?: any) => any) | undefined;
}) => {
const nearestBlockContainerPos = getNearestBlockContainerPos(
state.doc,
posInBlock
);
const nearestBlockContainerPos = getNearestBlockPos(state.doc, posInBlock);

const info = getBlockInfo(nearestBlockContainerPos);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import {
InlineContentSchema,
StyleSchema,
} from "../../../schema/index.js";
import {
getBlockInfo,
getNearestBlockContainerPos,
} from "../../getBlockInfoFromPos.js";
import { getBlockInfo, getNearestBlockPos } from "../../getBlockInfoFromPos.js";
import { acceptedMIMETypes } from "./acceptedMIMETypes.js";

function checkFileExtensionsMatch(
Expand Down Expand Up @@ -135,7 +132,7 @@ export async function handleFileInsertion<
return;
}

const posInfo = getNearestBlockContainerPos(
const posInfo = getNearestBlockPos(
editor._tiptapEditor.state.doc,
pos.pos
);
Expand Down
50 changes: 20 additions & 30 deletions packages/core/src/api/getBlockInfoFromPos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,21 @@ export type BlockInfo = {
);

/**
* Retrieves the position just before the nearest blockContainer node in a
* ProseMirror doc, relative to a position. If the position is within a
* blockContainer node or its descendants, the position just before it is
* returned. If the position is not within a blockContainer node or its
* descendants, the position just before the next closest blockContainer node
* is returned. If the position is beyond the last blockContainer, the position
* just before the last blockContainer is returned.
* Retrieves the position just before the nearest block node in a ProseMirror
* doc, relative to a position. If the position is within a block node or its
* descendants, the position just before it is returned. If the position is not
* within a block node or its descendants, the position just before the next
* closest block node is returned. If the position is beyond the last block, the
* position just before the last block is returned.
* @param doc The ProseMirror doc.
* @param pos An integer position in the document.
* @returns The position just before the nearest blockContainer node.
*/
export function getNearestBlockContainerPos(doc: Node, pos: number) {
export function getNearestBlockPos(doc: Node, pos: number) {
const $pos = doc.resolve(pos);

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

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

// If the position doesn't lie within a blockContainer node, we instead find
// the position of the next closest one. If the position is beyond the last
// blockContainer, we return the position of the last blockContainer. While
// running `doc.descendants` is expensive, this case should be very rarely
// triggered. However, it's possible for the position to sometimes be beyond
// the last blockContainer node. This is a problem specifically when using the
// collaboration plugin.
// If the position doesn't lie within a block node, we instead find the
// position of the next closest one. If the position is beyond the last block,
// we return the position of the last block. While running `doc.descendants`
// is expensive, this case should be very rarely triggered. However, it's
// possible for the position to sometimes be beyond the last block node. This
// is a problem specifically when using the collaboration plugin.
const allBlockContainerPositions: number[] = [];
doc.descendants((node, pos) => {
if (node.type.isInGroup("bnBlock")) {
Expand Down Expand Up @@ -119,7 +117,7 @@ export function getNearestBlockContainerPos(doc: Node, pos: number) {
* the ProseMirror positions just before & after each node.
* @param node The main `blockContainer` node that the block information should
* be retrieved from,
* @param blockContainerBeforePosOffset the position just before the
* @param bnBlockBeforePosOffset the position just before the
* `blockContainer` node in the document.
*/
export function getBlockInfoWithManualOffset(
Expand Down Expand Up @@ -237,15 +235,7 @@ export function getBlockInfoFromResolvedPos(resolvedPos: ResolvedPos) {
* @param state The ProseMirror editor state.
*/
export function getBlockInfoFromSelection(state: EditorState) {
const posInfo = getNearestBlockContainerPos(
state.doc,
state.selection.anchor
);
const ret = getBlockInfo(posInfo);
if (!ret.isBlockContainer) {
throw new Error(
`selection always expected to return blockContainer ${state.selection.anchor}`
);
}
return ret;
const posInfo = getNearestBlockPos(state.doc, state.selection.anchor);

return getBlockInfo(posInfo);
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
find: new RegExp(`^(#{${level}})\\s$`),
handler: ({ state, chain, range }) => {
const blockInfo = getBlockInfoFromSelection(state);
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
if (
!blockInfo.isBlockContainer ||
blockInfo.blockContent.node.type.spec.content !== "inline*"
) {
return;
}

Expand Down Expand Up @@ -78,7 +81,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
return {
"Mod-Alt-1": () => {
const blockInfo = getBlockInfoFromSelection(this.editor.state);
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
if (
!blockInfo.isBlockContainer ||
blockInfo.blockContent.node.type.spec.content !== "inline*"
) {
return true;
}

Expand All @@ -94,7 +100,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
},
"Mod-Alt-2": () => {
const blockInfo = getBlockInfoFromSelection(this.editor.state);
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
if (
!blockInfo.isBlockContainer ||
blockInfo.blockContent.node.type.spec.content !== "inline*"
) {
return true;
}

Expand All @@ -109,7 +118,10 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
},
"Mod-Alt-3": () => {
const blockInfo = getBlockInfoFromSelection(this.editor.state);
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
if (
!blockInfo.isBlockContainer ||
blockInfo.blockContent.node.type.spec.content !== "inline*"
) {
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ const BulletListItemBlockContent = createStronglyTypedTiptapNode({
find: new RegExp(`^[-+*]\\s$`),
handler: ({ state, chain, range }) => {
const blockInfo = getBlockInfoFromSelection(state);
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
if (
!blockInfo.isBlockContainer ||
blockInfo.blockContent.node.type.spec.content !== "inline*"
) {
return;
}

Expand All @@ -55,7 +58,10 @@ const BulletListItemBlockContent = createStronglyTypedTiptapNode({
Enter: () => handleEnter(this.options.editor),
"Mod-Shift-8": () => {
const blockInfo = getBlockInfoFromSelection(this.editor.state);
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
if (
!blockInfo.isBlockContainer ||
blockInfo.blockContent.node.type.spec.content !== "inline*"
) {
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { InputRule } from "@tiptap/core";
import { updateBlockCommand } from "../../../api/blockManipulation/commands/updateBlock/updateBlock.js";
import {
getBlockInfoFromSelection,
getNearestBlockContainerPos,
getNearestBlockPos,
} from "../../../api/getBlockInfoFromPos.js";
import {
PropSchema,
Expand Down Expand Up @@ -49,7 +49,10 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
find: new RegExp(`\\[\\s*\\]\\s$`),
handler: ({ state, chain, range }) => {
const blockInfo = getBlockInfoFromSelection(state);
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
if (
!blockInfo.isBlockContainer ||
blockInfo.blockContent.node.type.spec.content !== "inline*"
) {
return;
}

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

if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
if (
!blockInfo.isBlockContainer ||
blockInfo.blockContent.node.type.spec.content !== "inline*"
) {
return;
}

Expand Down Expand Up @@ -104,7 +110,10 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({
Enter: () => handleEnter(this.options.editor),
"Mod-Shift-9": () => {
const blockInfo = getBlockInfoFromSelection(this.options.editor.state);
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
if (
!blockInfo.isBlockContainer ||
blockInfo.blockContent.node.type.spec.content !== "inline*"
) {
return true;
}

Expand Down Expand Up @@ -232,10 +241,17 @@ const checkListItemBlockContent = createStronglyTypedTiptapNode({

// TODO: test
if (typeof getPos !== "boolean") {
const beforeBlockContainerPos = getNearestBlockContainerPos(
const beforeBlockContainerPos = getNearestBlockPos(
editor.state.doc,
getPos()
);

if (beforeBlockContainerPos.node.type.name !== "blockContainer") {
throw new Error(
`Expected blockContainer node, got ${beforeBlockContainerPos.node.type.name}`
);
}

this.editor.commands.command(
updateBlockCommand(
this.options.editor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";

export const handleEnter = (editor: BlockNoteEditor<any, any, any>) => {
const ttEditor = editor._tiptapEditor;
const { blockContent, bnBlock: blockContainer } = getBlockInfoFromSelection(
ttEditor.state
);
const blockInfo = getBlockInfoFromSelection(ttEditor.state);
if (!blockInfo.isBlockContainer) {
return false;
}
const { bnBlock: blockContainer, blockContent } = blockInfo;

const selectionEmpty =
ttEditor.state.selection.anchor === ttEditor.state.selection.head;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ const NumberedListItemBlockContent = createStronglyTypedTiptapNode({
find: new RegExp(`^1\\.\\s$`),
handler: ({ state, chain, range }) => {
const blockInfo = getBlockInfoFromSelection(state);
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
if (
!blockInfo.isBlockContainer ||
blockInfo.blockContent.node.type.spec.content !== "inline*"
) {
return;
}

Expand All @@ -68,7 +71,10 @@ const NumberedListItemBlockContent = createStronglyTypedTiptapNode({
Enter: () => handleEnter(this.options.editor),
"Mod-Shift-7": () => {
const blockInfo = getBlockInfoFromSelection(this.editor.state);
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
if (
!blockInfo.isBlockContainer ||
blockInfo.blockContent.node.type.spec.content !== "inline*"
) {
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ export const ParagraphBlockContent = createStronglyTypedTiptapNode({
return {
"Mod-Alt-0": () => {
const blockInfo = getBlockInfoFromSelection(this.editor.state);
if (blockInfo.blockContent.node.type.spec.content !== "inline*") {
if (
!blockInfo.isBlockContainer ||
blockInfo.blockContent.node.type.spec.content !== "inline*"
) {
return true;
}

Expand Down
7 changes: 2 additions & 5 deletions packages/core/src/editor/BlockNoteEditor.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { expect, it } from "vitest";
import {
getBlockInfo,
getNearestBlockContainerPos,
getNearestBlockPos,
} from "../api/getBlockInfoFromPos.js";
import { BlockNoteEditor } from "./BlockNoteEditor.js";

Expand All @@ -10,10 +10,7 @@ import { BlockNoteEditor } from "./BlockNoteEditor.js";
*/
it("creates an editor", () => {
const editor = BlockNoteEditor.create();
const posInfo = getNearestBlockContainerPos(
editor._tiptapEditor.state.doc,
2
);
const posInfo = getNearestBlockPos(editor._tiptapEditor.state.doc, 2);
const info = getBlockInfo(posInfo);
expect(info.blockNoteType).toEqual("paragraph");
});
Expand Down
Loading
Loading