mirror of
https://github.com/docmost/docmost.git
synced 2026-09-11 07:56:54 +08:00
fix(editor): allow gap cursors between top-level blocks (#2477)
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import { Editor, Node } from "@tiptap/core";
|
||||
import { GapCursor } from "@tiptap/pm/gapcursor";
|
||||
import { NodeSelection, TextSelection } from "@tiptap/pm/state";
|
||||
import { StarterKit } from "@tiptap/starter-kit";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { TiptapDocument } from "./document";
|
||||
|
||||
const Footnotes = Node.create({
|
||||
name: "footnotes",
|
||||
group: "",
|
||||
content: "paragraph*",
|
||||
isolating: true,
|
||||
renderHTML() {
|
||||
return ["ol", { class: "footnotes" }, 0];
|
||||
},
|
||||
});
|
||||
|
||||
const AtomBlock = Node.create({
|
||||
name: "atomBlock",
|
||||
group: "block",
|
||||
atom: true,
|
||||
renderHTML() {
|
||||
return ["div", { "data-atom-block": "" }];
|
||||
},
|
||||
});
|
||||
|
||||
const IsolatingBlock = Node.create({
|
||||
name: "isolatingBlock",
|
||||
group: "block",
|
||||
content: "paragraph+",
|
||||
isolating: true,
|
||||
renderHTML() {
|
||||
return ["div", { "data-isolating-block": "" }, 0];
|
||||
},
|
||||
});
|
||||
|
||||
function createEditor(content: object[]) {
|
||||
const element = document.createElement("div");
|
||||
document.body.appendChild(element);
|
||||
|
||||
return new Editor({
|
||||
element,
|
||||
extensions: [
|
||||
TiptapDocument,
|
||||
StarterKit.configure({ document: false }),
|
||||
Footnotes,
|
||||
AtomBlock,
|
||||
IsolatingBlock,
|
||||
],
|
||||
content: { type: "doc", content },
|
||||
});
|
||||
}
|
||||
|
||||
function pressKey(editor: Editor, key: string, keyCode: number) {
|
||||
editor.view.dom.dispatchEvent(
|
||||
new KeyboardEvent("keydown", {
|
||||
key,
|
||||
keyCode,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
describe("TiptapDocument", () => {
|
||||
it("stops on the gap when arrowing down from a selected block node", () => {
|
||||
const editor = createEditor([
|
||||
{ type: "atomBlock" },
|
||||
{ type: "atomBlock" },
|
||||
{ type: "paragraph" },
|
||||
]);
|
||||
const gapPos = editor.state.doc.child(0).nodeSize;
|
||||
editor.view.dispatch(
|
||||
editor.state.tr.setSelection(
|
||||
NodeSelection.create(editor.state.doc, 0),
|
||||
),
|
||||
);
|
||||
|
||||
pressKey(editor, "ArrowDown", 40);
|
||||
|
||||
expect(editor.state.selection).toBeInstanceOf(GapCursor);
|
||||
expect(editor.state.selection.head).toBe(gapPos);
|
||||
|
||||
editor.destroy();
|
||||
});
|
||||
|
||||
it("stops on the gap when arrowing right out of an isolating block", () => {
|
||||
const paragraph = (text: string) => ({
|
||||
type: "paragraph",
|
||||
content: [{ type: "text", text }],
|
||||
});
|
||||
const editor = createEditor([
|
||||
{ type: "isolatingBlock", content: [paragraph("a")] },
|
||||
{ type: "isolatingBlock", content: [paragraph("b")] },
|
||||
{ type: "paragraph" },
|
||||
]);
|
||||
const gapPos = editor.state.doc.child(0).nodeSize;
|
||||
const endOfFirstText = gapPos - 2;
|
||||
editor.view.dispatch(
|
||||
editor.state.tr.setSelection(
|
||||
TextSelection.create(editor.state.doc, endOfFirstText),
|
||||
),
|
||||
);
|
||||
|
||||
pressKey(editor, "ArrowRight", 39);
|
||||
|
||||
expect(editor.state.selection).toBeInstanceOf(GapCursor);
|
||||
expect(editor.state.selection.head).toBe(gapPos);
|
||||
|
||||
editor.destroy();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user