Skip to content

Commit 44e68f9

Browse files
committed
feat: more extension stuff
1 parent add965f commit 44e68f9

File tree

4 files changed

+199
-421
lines changed

4 files changed

+199
-421
lines changed

packages/core/src/editor/BlockNoteEditor.ts

Lines changed: 101 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import {
22
AnyExtension,
33
createDocument,
44
EditorOptions,
5-
Extension,
5+
Extension as TiptapExtension,
66
FocusPosition,
77
getSchema,
88
InputRule,
9-
Mark,
9+
Mark as TiptapMark,
1010
Editor as TiptapEditor,
11-
Node as TipTapNode,
11+
Node as TiptapNode,
1212
} from "@tiptap/core";
1313
import { type Command, type Plugin, type Transaction } from "@tiptap/pm/state";
1414
import { dropCursor } from "prosemirror-dropcursor";
@@ -53,7 +53,6 @@ import type {
5353
import { mergeCSSClasses } from "../util/browser.js";
5454
import { EventEmitter } from "../util/EventEmitter.js";
5555
import type { NoInfer } from "../util/typescript.js";
56-
import { BlockNoteExtension } from "./BlockNoteExtension.js";
5756
import { getBlockNoteExtensions } from "./BlockNoteExtensions.js";
5857
import type { TextCursorPosition } from "./cursorPositionTypes.js";
5958
import {
@@ -74,33 +73,24 @@ import { updateBlockTr } from "../api/blockManipulation/commands/updateBlock/upd
7473
import { getBlockInfoFromTransaction } from "../api/getBlockInfoFromPos.js";
7574
import { blockToNode } from "../api/nodeConversions/blockToNode.js";
7675
import "../style.css";
77-
import { ExtensionFactory } from "./managers/extensions/types.js";
78-
79-
/**
80-
* A factory function that returns a BlockNoteExtension
81-
* This is useful so we can create extensions that require an editor instance
82-
* in the constructor
83-
*/
84-
export type BlockNoteExtensionFactory = (
85-
editor: BlockNoteEditor<any, any, any>,
86-
) => BlockNoteExtension;
76+
import { Extension, ExtensionFactory } from "./BlockNoteExtension.js";
8777

8878
/**
8979
* We support Tiptap extensions and BlockNoteExtension based extensions
9080
*/
91-
export type SupportedExtension = AnyExtension | BlockNoteExtension;
81+
export type SupportedExtension = AnyExtension | Extension;
9282

9383
export type BlockCache<
9484
BSchema extends BlockSchema = any,
9585
ISchema extends InlineContentSchema = any,
9686
SSchema extends StyleSchema = any,
9787
> = WeakMap<Node, Block<BSchema, ISchema, SSchema>>;
9888

99-
export type BlockNoteEditorOptions<
89+
export interface BlockNoteEditorOptions<
10090
BSchema extends BlockSchema,
10191
ISchema extends InlineContentSchema,
10292
SSchema extends StyleSchema,
103-
> = {
93+
> {
10494
/**
10595
* Whether changes to blocks (like indentation, creating lists, changing headings) should be animated or not. Defaults to `true`.
10696
*
@@ -394,10 +384,10 @@ export type BlockNoteEditorOptions<
394384
*
395385
* See [Extensions](/docs/features/extensions) for more info.
396386
*
397-
* @remarks `BlockNoteExtension[]`
387+
* @remarks `Extension[]`
398388
*/
399-
extensions?: Array<BlockNoteExtension | BlockNoteExtensionFactory>;
400-
};
389+
extensions?: Array<Extension | ExtensionFactory>;
390+
}
401391

402392
const blockNoteTipTapOptions = {
403393
enableInputRules: true,
@@ -698,18 +688,18 @@ export class BlockNoteEditor<
698688
return;
699689
}
700690

701-
this.extensions[key] = new (class extends BlockNoteExtension {
702-
public static key() {
703-
return key;
704-
}
705-
constructor() {
706-
super();
707-
this.addProsemirrorPlugin(instance.plugin);
708-
}
709-
public get priority() {
710-
return instance.priority;
711-
}
712-
})();
691+
// this.extensions[key] = new (class extends BlockNoteExtension {
692+
// public static key() {
693+
// return key;
694+
// }
695+
// constructor() {
696+
// super();
697+
// this.addProsemirrorPlugin(instance.plugin);
698+
// }
699+
// public get priority() {
700+
// return instance.priority;
701+
// }
702+
// })();
713703
});
714704

715705
if (newOptions.uploadFile) {
@@ -752,94 +742,94 @@ export class BlockNoteEditor<
752742
...Object.entries({ ...this.extensions, ...blockExtensions }).map(
753743
([key, ext]) => {
754744
if (
755-
ext instanceof Extension ||
756-
ext instanceof TipTapNode ||
757-
ext instanceof Mark
745+
ext instanceof TiptapExtension ||
746+
ext instanceof TiptapNode ||
747+
ext instanceof TiptapMark
758748
) {
759749
// tiptap extension
760750
return ext;
761751
}
762752

763-
if (ext instanceof BlockNoteExtension) {
764-
if (
765-
!ext.plugins.length &&
766-
!ext.keyboardShortcuts &&
767-
!ext.inputRules &&
768-
!ext.tiptapExtensions
769-
) {
770-
return undefined;
771-
}
772-
// "blocknote" extensions (prosemirror plugins)
773-
return Extension.create({
774-
name: key,
775-
priority: ext.priority,
776-
addProseMirrorPlugins: () => ext.plugins,
777-
addExtensions: () => ext.tiptapExtensions || [],
778-
// TODO maybe collect all input rules from all extensions into one plugin
779-
// TODO consider using the prosemirror-inputrules package instead
780-
addInputRules: ext.inputRules
781-
? () =>
782-
ext.inputRules!.map(
783-
(inputRule) =>
784-
new InputRule({
785-
find: inputRule.find,
786-
handler: ({ range, match, state }) => {
787-
const replaceWith = inputRule.replace({
788-
match,
789-
range,
790-
editor: this,
791-
});
792-
if (replaceWith) {
793-
const cursorPosition =
794-
this.getTextCursorPosition();
795-
796-
if (
797-
this.schema.blockSchema[
798-
cursorPosition.block.type
799-
].content !== "inline"
800-
) {
801-
return undefined;
802-
}
803-
804-
const blockInfo = getBlockInfoFromTransaction(
805-
state.tr,
806-
);
807-
const tr = state.tr.deleteRange(
808-
range.from,
809-
range.to,
810-
);
811-
812-
updateBlockTr(
813-
tr,
814-
blockInfo.bnBlock.beforePos,
815-
replaceWith,
816-
);
817-
return undefined;
818-
}
819-
return null;
820-
},
821-
}),
822-
)
823-
: undefined,
824-
addKeyboardShortcuts: ext.keyboardShortcuts
825-
? () => {
826-
return Object.fromEntries(
827-
Object.entries(ext.keyboardShortcuts!).map(
828-
([key, value]) => [
829-
key,
830-
() => value({ editor: this as any }),
831-
],
832-
),
833-
);
834-
}
835-
: undefined,
836-
});
837-
}
753+
// if (ext instanceof BlockNoteExtension) {
754+
// if (
755+
// !ext.plugins.length &&
756+
// !ext.keyboardShortcuts &&
757+
// !ext.inputRules &&
758+
// !ext.tiptapExtensions
759+
// ) {
760+
// return undefined;
761+
// }
762+
// // "blocknote" extensions (prosemirror plugins)
763+
// return Extension.create({
764+
// name: key,
765+
// priority: ext.priority,
766+
// addProseMirrorPlugins: () => ext.plugins,
767+
// addExtensions: () => ext.tiptapExtensions || [],
768+
// // TODO maybe collect all input rules from all extensions into one plugin
769+
// // TODO consider using the prosemirror-inputrules package instead
770+
// addInputRules: ext.inputRules
771+
// ? () =>
772+
// ext.inputRules!.map(
773+
// (inputRule) =>
774+
// new InputRule({
775+
// find: inputRule.find,
776+
// handler: ({ range, match, state }) => {
777+
// const replaceWith = inputRule.replace({
778+
// match,
779+
// range,
780+
// editor: this,
781+
// });
782+
// if (replaceWith) {
783+
// const cursorPosition =
784+
// this.getTextCursorPosition();
785+
786+
// if (
787+
// this.schema.blockSchema[
788+
// cursorPosition.block.type
789+
// ].content !== "inline"
790+
// ) {
791+
// return undefined;
792+
// }
793+
794+
// const blockInfo = getBlockInfoFromTransaction(
795+
// state.tr,
796+
// );
797+
// const tr = state.tr.deleteRange(
798+
// range.from,
799+
// range.to,
800+
// );
801+
802+
// updateBlockTr(
803+
// tr,
804+
// blockInfo.bnBlock.beforePos,
805+
// replaceWith,
806+
// );
807+
// return undefined;
808+
// }
809+
// return null;
810+
// },
811+
// }),
812+
// )
813+
// : undefined,
814+
// addKeyboardShortcuts: ext.keyboardShortcuts
815+
// ? () => {
816+
// return Object.fromEntries(
817+
// Object.entries(ext.keyboardShortcuts!).map(
818+
// ([key, value]) => [
819+
// key,
820+
// () => value({ editor: this as any }),
821+
// ],
822+
// ),
823+
// );
824+
// }
825+
// : undefined,
826+
// });
827+
// }
838828

839829
return undefined;
840830
},
841831
),
842-
].filter((ext): ext is Extension => ext !== undefined);
832+
].filter((ext): ext is TiptapExtension => ext !== undefined);
843833
const tiptapOptions: EditorOptions = {
844834
...blockNoteTipTapOptions,
845835
...newOptions._tiptapOptions,
@@ -1014,22 +1004,6 @@ export class BlockNoteEditor<
10141004
return this._stateManager.transact(callback);
10151005
}
10161006

1017-
// TO DISCUSS
1018-
/**
1019-
* Shorthand to get a typed extension from the editor, by
1020-
* just passing in the extension class.
1021-
*
1022-
* @param ext - The extension class to get
1023-
* @param key - optional, the key of the extension in the extensions object (defaults to the extension name)
1024-
* @returns The extension instance
1025-
*/
1026-
public extension<T extends BlockNoteExtension>(
1027-
ext: { new (...args: any[]): T } & typeof BlockNoteExtension,
1028-
key = ext.key(),
1029-
): T {
1030-
return this._extensionManager.getExtension(key) as any;
1031-
}
1032-
10331007
/**
10341008
* Add an extension to the editor
10351009
* @param extension The extension to add

0 commit comments

Comments
 (0)