Skip to content

Commit c96d5ec

Browse files
committed
Merge branch 'refs/heads/main' into releases
2 parents 9354902 + f595ba5 commit c96d5ec

File tree

5 files changed

+73
-49
lines changed

5 files changed

+73
-49
lines changed

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

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,6 @@ const schema = BlockNoteSchema.create({
3939
let editor: BlockNoteEditor<typeof schema.blockSchema>;
4040
const div = document.createElement("div");
4141

42-
function waitForEditor() {
43-
// wait for create event on editor,
44-
// this is necessary because otherwise UniqueId.create hasn't been called yet, and
45-
// blocks would have "null" as their id
46-
return new Promise<void>((resolve) => {
47-
editor._tiptapEditor.on("create", () => {
48-
resolve();
49-
});
50-
});
51-
}
52-
5342
let singleBlock: PartialBlock<
5443
typeof schema.blockSchema,
5544
DefaultInlineContentSchema,
@@ -195,35 +184,27 @@ describe("Test strong typing", () => {
195184
});
196185

197186
describe("Inserting Blocks with Different Placements", () => {
198-
it("Insert before existing block", async () => {
199-
await waitForEditor();
200-
187+
it("Insert before existing block", () => {
201188
const output = insert("before");
202189

203190
expect(output).toMatchSnapshot();
204191
});
205192

206-
it("Insert nested inside existing block", async () => {
207-
await waitForEditor();
208-
193+
it("Insert nested inside existing block", () => {
209194
const output = insert("nested");
210195

211196
expect(output).toMatchSnapshot();
212197
});
213198

214-
it("Insert after existing block", async () => {
215-
await waitForEditor();
216-
199+
it("Insert after existing block", () => {
217200
const output = insert("after");
218201

219202
expect(output).toMatchSnapshot();
220203
});
221204
});
222205

223206
describe("Insert, Update, & Delete Blocks", () => {
224-
it("Insert, update, & delete single block", async () => {
225-
await waitForEditor();
226-
207+
it("Insert, update, & delete single block", () => {
227208
const existingBlock = editor.document[0];
228209
editor.insertBlocks([singleBlock], existingBlock);
229210

@@ -263,9 +244,7 @@ describe("Insert, Update, & Delete Blocks", () => {
263244
expect(editor.document).toMatchSnapshot();
264245
});
265246

266-
it("Insert, update, & delete multiple blocks", async () => {
267-
await waitForEditor();
268-
247+
it("Insert, update, & delete multiple blocks", () => {
269248
const existingBlock = editor.document[0];
270249
editor.insertBlocks(multipleBlocks, existingBlock);
271250

@@ -286,9 +265,7 @@ describe("Insert, Update, & Delete Blocks", () => {
286265
});
287266

288267
describe("Update Line Breaks", () => {
289-
it("Update paragraph with line break", async () => {
290-
await waitForEditor();
291-
268+
it("Update paragraph with line break", () => {
292269
const existingBlock = editor.document[0];
293270
editor.insertBlocks(blocksWithLineBreaks, existingBlock);
294271

@@ -300,9 +277,7 @@ describe("Update Line Breaks", () => {
300277

301278
expect(editor.document).toMatchSnapshot();
302279
});
303-
it("Update custom block with line break", async () => {
304-
await waitForEditor();
305-
280+
it("Update custom block with line break", () => {
306281
const existingBlock = editor.document[0];
307282
editor.insertBlocks(blocksWithLineBreaks, existingBlock);
308283

packages/core/src/editor/BlockNoteEditor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ import {
4141
InlineContentSchema,
4242
InlineContentSpecs,
4343
PartialInlineContent,
44-
Styles,
4544
StyleSchema,
4645
StyleSpecs,
46+
Styles,
4747
} from "../schema";
4848
import { mergeCSSClasses } from "../util/browser";
4949
import { NoInfer, UnreachableCaseError } from "../util/typescript";
@@ -463,7 +463,7 @@ export class BlockNoteEditor<
463463
};
464464

465465
if (!this.headless) {
466-
this._tiptapEditor = new BlockNoteTipTapEditor(
466+
this._tiptapEditor = BlockNoteTipTapEditor.create(
467467
tiptapOptions,
468468
this.schema.styleSchema
469469
) as BlockNoteTipTapEditor & {

packages/core/src/editor/BlockNoteTipTapEditor.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,32 @@ export type BlockNoteTipTapEditorOptions = Partial<
2323
export class BlockNoteTipTapEditor extends TiptapEditor {
2424
private _state: EditorState;
2525

26-
constructor(options: BlockNoteTipTapEditorOptions, styleSchema: StyleSchema) {
26+
public static create = (
27+
options: BlockNoteTipTapEditorOptions,
28+
styleSchema: StyleSchema
29+
) => {
30+
// because we separate the constructor from the creation of the view,
31+
// we need to patch setTimeout to prevent this code from having any effect:
32+
// https://github.com/ueberdosis/tiptap/blob/45bac803283446795ad1b03f43d3746fa54a68ff/packages/core/src/Editor.ts#L117
33+
const oldSetTimeout = globalThis?.window?.setTimeout;
34+
if (typeof globalThis?.window?.setTimeout !== "undefined") {
35+
globalThis.window.setTimeout = (() => {
36+
return 0;
37+
}) as any;
38+
}
39+
try {
40+
return new BlockNoteTipTapEditor(options, styleSchema);
41+
} finally {
42+
if (oldSetTimeout) {
43+
globalThis.window.setTimeout = oldSetTimeout;
44+
}
45+
}
46+
};
47+
48+
protected constructor(
49+
options: BlockNoteTipTapEditorOptions,
50+
styleSchema: StyleSchema
51+
) {
2752
// possible fix for next.js server side rendering
2853
// const d = globalThis.document;
2954
// const w = globalThis.window;
@@ -32,14 +57,10 @@ export class BlockNoteTipTapEditor extends TiptapEditor {
3257
// createElement: () => {},
3358
// };
3459
// }
35-
// if (!globalThis.window) {
36-
// globalThis.window = {
37-
// setTimeout: () => {},
38-
// };
39-
// }
60+
4061
// options.injectCSS = false
41-
super({ ...options, content: undefined });
4262

63+
super({ ...options, content: undefined });
4364
// try {
4465
// globalThis.window = w;
4566
// } catch(e) {}
@@ -149,6 +170,12 @@ export class BlockNoteTipTapEditor extends TiptapEditor {
149170
this.view.updateState(newState);
150171

151172
this.createNodeViews();
173+
174+
// emit the created event, call here manually because we blocked the default call in the constructor
175+
// (https://github.com/ueberdosis/tiptap/blob/45bac803283446795ad1b03f43d3746fa54a68ff/packages/core/src/Editor.ts#L117)
176+
this.commands.focus(this.options.autofocus);
177+
this.emit("create", { editor: this });
178+
this.isInitialized = true;
152179
});
153180
}
154181

packages/core/src/extensions/TableHandles/TableHandlesPlugin.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ export class TableHandlesView<
107107

108108
public menuFrozen = false;
109109

110+
public mouseState: "up" | "down" | "selecting" = "up";
111+
110112
public prevWasEditable: boolean | null = null;
111113

112114
constructor(
@@ -127,6 +129,8 @@ export class TableHandlesView<
127129
};
128130

129131
pmView.dom.addEventListener("mousemove", this.mouseMoveHandler);
132+
pmView.dom.addEventListener("mousedown", this.viewMousedownHandler);
133+
pmView.dom.addEventListener("mouseup", this.viewMouseupHandler);
130134

131135
pmView.root.addEventListener(
132136
"dragover",
@@ -140,11 +144,33 @@ export class TableHandlesView<
140144
pmView.root.addEventListener("scroll", this.scrollHandler, true);
141145
}
142146

147+
viewMousedownHandler = () => {
148+
this.mouseState = "down";
149+
};
150+
151+
viewMouseupHandler = (event: MouseEvent) => {
152+
this.mouseState = "up";
153+
this.mouseMoveHandler(event);
154+
};
155+
143156
mouseMoveHandler = (event: MouseEvent) => {
144157
if (this.menuFrozen) {
145158
return;
146159
}
147160

161+
if (this.mouseState === "down") {
162+
this.mouseState = "selecting";
163+
164+
if (this.state?.show) {
165+
this.state.show = false;
166+
this.emitUpdate();
167+
}
168+
}
169+
170+
if (this.mouseState === "selecting") {
171+
return;
172+
}
173+
148174
const target = domCellAround(event.target as HTMLElement);
149175

150176
if (!target || !this.editor.isEditable) {

packages/core/src/schema/blocks/createSpec.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,24 +63,20 @@ export type CustomBlockImplementation<
6363
) => PartialBlockFromConfig<T, I, S>["props"] | undefined;
6464
};
6565

66-
// Function that enables copying of selected content within non-selectable
67-
// blocks.
66+
// Function that causes events within non-selectable blocks to be handled by the
67+
// browser instead of the editor.
6868
export function applyNonSelectableBlockFix(nodeView: NodeView, editor: Editor) {
6969
nodeView.stopEvent = (event) => {
70-
// Ensures copy events are handled by the browser and not by ProseMirror.
71-
if (event.type === "copy" || event.type === "cut") {
72-
return true;
73-
}
7470
// Blurs the editor on mouse down as the block is non-selectable. This is
7571
// mainly done to prevent UI elements like the formatting toolbar from being
7672
// visible while content within a non-selectable block is selected.
7773
if (event.type === "mousedown") {
7874
setTimeout(() => {
7975
editor.view.dom.blur();
8076
}, 10);
81-
return true;
8277
}
83-
return false;
78+
79+
return true;
8480
};
8581
}
8682

0 commit comments

Comments
 (0)