Skip to content

Commit be406b7

Browse files
fix initialcontent and uniqueid (#289)
* fix initialcontent and uniqueid * fix * Removed ready check in `BlockNoteView` --------- Co-authored-by: Matthew Lipski <[email protected]>
1 parent 0cad478 commit be406b7

File tree

4 files changed

+69
-72
lines changed

4 files changed

+69
-72
lines changed

packages/core/src/BlockNoteEditor.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ import {
1616
blocksToMarkdown,
1717
markdownToBlocks,
1818
} from "./api/formatConversions/formatConversions";
19-
import { nodeToBlock } from "./api/nodeConversions/nodeConversions";
19+
import {
20+
blockToNode,
21+
nodeToBlock,
22+
} from "./api/nodeConversions/nodeConversions";
2023
import { getNodeById } from "./api/util/nodeUtil";
2124
import styles from "./editor.module.css";
2225
import {
@@ -44,6 +47,7 @@ import { SideMenuProsemirrorPlugin } from "./extensions/SideMenu/SideMenuPlugin"
4447
import { BaseSlashMenuItem } from "./extensions/SlashMenu/BaseSlashMenuItem";
4548
import { SlashMenuProsemirrorPlugin } from "./extensions/SlashMenu/SlashMenuPlugin";
4649
import { getDefaultSlashMenuItems } from "./extensions/SlashMenu/defaultSlashMenuItems";
50+
import { UniqueID } from "./extensions/UniqueID/UniqueID";
4751

4852
export type BlockNoteEditorOptions<BSchema extends BlockSchema> = {
4953
// TODO: Figure out if enableBlockNoteExtensions/disableHistoryExtension are needed and document them.
@@ -201,22 +205,34 @@ export class BlockNoteEditor<BSchema extends BlockSchema = DefaultBlockSchema> {
201205

202206
this.schema = newOptions.blockSchema;
203207

208+
const initialContent = newOptions.initialContent || [
209+
{
210+
type: "paragraph",
211+
id: UniqueID.options.generateID(),
212+
},
213+
];
214+
204215
const tiptapOptions: EditorOptions = {
205-
// TODO: This approach to setting initial content is "cleaner" but requires the PM editor schema, which is only
206-
// created after initializing the TipTap editor. Not sure it's feasible.
207-
// content:
208-
// options.initialContent &&
209-
// options.initialContent.map((block) =>
210-
// blockToNode(block, this._tiptapEditor.schema).toJSON()
211-
// ),
212216
...blockNoteTipTapOptions,
213217
...newOptions._tiptapOptions,
214218
onCreate: () => {
215219
newOptions.onEditorReady?.(this);
216-
newOptions.initialContent &&
217-
this.replaceBlocks(this.topLevelBlocks, newOptions.initialContent);
218220
this.ready = true;
219221
},
222+
onBeforeCreate(editor) {
223+
// we have to set the initial content here, because now we can use the editor schema
224+
// which has been created at this point
225+
const schema = editor.editor.schema;
226+
const ic = initialContent.map((block) => blockToNode(block, schema));
227+
228+
const root = schema.node(
229+
"doc",
230+
undefined,
231+
schema.node("blockGroup", undefined, ic)
232+
);
233+
// override the initialcontent
234+
editor.editor.options.content = root.toJSON();
235+
},
220236
onUpdate: () => {
221237
// This seems to be necessary due to a bug in TipTap:
222238
// https://github.com/ueberdosis/tiptap/issues/2583

packages/core/src/extensions/UniqueID/UniqueID.ts

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
combineTransactionSteps,
33
Extension,
4-
findChildren,
54
findChildrenInRange,
65
getChangedRanges,
76
} from "@tiptap/core";
@@ -87,35 +86,35 @@ const UniqueID = Extension.create({
8786
];
8887
},
8988
// check initial content for missing ids
90-
onCreate() {
91-
// Don’t do this when the collaboration extension is active
92-
// because this may update the content, so Y.js tries to merge these changes.
93-
// This leads to empty block nodes.
94-
// See: https://github.com/ueberdosis/tiptap/issues/2400
95-
if (
96-
this.editor.extensionManager.extensions.find(
97-
(extension) => extension.name === "collaboration"
98-
)
99-
) {
100-
return;
101-
}
102-
const { view, state } = this.editor;
103-
const { tr, doc } = state;
104-
const { types, attributeName, generateID } = this.options;
105-
const nodesWithoutId = findChildren(doc, (node) => {
106-
return (
107-
types.includes(node.type.name) && node.attrs[attributeName] === null
108-
);
109-
});
110-
nodesWithoutId.forEach(({ node, pos }) => {
111-
tr.setNodeMarkup(pos, undefined, {
112-
...node.attrs,
113-
[attributeName]: generateID(),
114-
});
115-
});
116-
tr.setMeta("addToHistory", false);
117-
view.dispatch(tr);
118-
},
89+
// onCreate() {
90+
// // Don’t do this when the collaboration extension is active
91+
// // because this may update the content, so Y.js tries to merge these changes.
92+
// // This leads to empty block nodes.
93+
// // See: https://github.com/ueberdosis/tiptap/issues/2400
94+
// if (
95+
// this.editor.extensionManager.extensions.find(
96+
// (extension) => extension.name === "collaboration"
97+
// )
98+
// ) {
99+
// return;
100+
// }
101+
// const { view, state } = this.editor;
102+
// const { tr, doc } = state;
103+
// const { types, attributeName, generateID } = this.options;
104+
// const nodesWithoutId = findChildren(doc, (node) => {
105+
// return (
106+
// types.includes(node.type.name) && node.attrs[attributeName] === null
107+
// );
108+
// });
109+
// nodesWithoutId.forEach(({ node, pos }) => {
110+
// tr.setNodeMarkup(pos, undefined, {
111+
// ...node.attrs,
112+
// [attributeName]: generateID(),
113+
// });
114+
// });
115+
// tr.setMeta("addToHistory", false);
116+
// view.dispatch(tr);
117+
// },
119118
addProseMirrorPlugins() {
120119
let dragSourceElement: any = null;
121120
let transformPasted = false;

packages/react/src/BlockNoteView.tsx

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { BlockNoteEditor, BlockSchema } from "@blocknote/core";
22
import { MantineProvider } from "@mantine/core";
33
import { EditorContent } from "@tiptap/react";
4-
import { ReactNode, useEffect, useState } from "react";
4+
import { HTMLAttributes, ReactNode } from "react";
55
import { getBlockNoteTheme } from "./BlockNoteTheme";
66
import { FormattingToolbarPositioner } from "./FormattingToolbar/components/FormattingToolbarPositioner";
77
import { HyperlinkToolbarPositioner } from "./HyperlinkToolbar/components/HyperlinkToolbarPositioner";
@@ -12,40 +12,21 @@ export function BlockNoteView<BSchema extends BlockSchema>(
1212
props: {
1313
editor: BlockNoteEditor<BSchema>;
1414
children?: ReactNode;
15-
} & React.HTMLAttributes<HTMLDivElement>
15+
} & HTMLAttributes<HTMLDivElement>
1616
) {
1717
const { editor, children, ...rest } = props;
18-
const [ready, setReady] = useState(false);
19-
20-
useEffect(() => {
21-
function checkReady() {
22-
if (!props.editor.ready) {
23-
window.setTimeout(checkReady, 100);
24-
} else {
25-
setReady(true);
26-
}
27-
}
28-
checkReady();
29-
30-
// TODO: Shouldn't this work with props.editor.ready in deps? It fails in
31-
// tests so it doesn't seem to.
32-
// if (props.editor.ready) {
33-
// setReady(true);
34-
// }
35-
}, [props.editor.ready]);
3618

3719
return (
3820
<MantineProvider theme={getBlockNoteTheme()}>
3921
<EditorContent editor={props.editor?._tiptapEditor} {...rest}>
40-
{ready &&
41-
(props.children || (
42-
<>
43-
<FormattingToolbarPositioner editor={props.editor} />
44-
<HyperlinkToolbarPositioner editor={props.editor} />
45-
<SlashMenuPositioner editor={props.editor} />
46-
<SideMenuPositioner editor={props.editor} />
47-
</>
48-
))}
22+
{props.children || (
23+
<>
24+
<FormattingToolbarPositioner editor={props.editor} />
25+
<HyperlinkToolbarPositioner editor={props.editor} />
26+
<SlashMenuPositioner editor={props.editor} />
27+
<SideMenuPositioner editor={props.editor} />
28+
</>
29+
)}
4930
</EditorContent>
5031
</MantineProvider>
5132
);

packages/react/src/hooks/useBlockNote.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,16 @@ export const useBlockNote = <BSchema extends BlockSchema = DefaultBlockSchema>(
2525
options: Partial<BlockNoteEditorOptions<BSchema>> = {},
2626
deps: DependencyList = []
2727
): BlockNoteEditor<BSchema> => {
28-
const editorRef = useRef<BlockNoteEditor<BSchema>>(initEditor(options));
28+
const editorRef = useRef<BlockNoteEditor<BSchema>>();
2929

30-
useMemo(() => {
30+
const ret = useMemo(() => {
3131
if (editorRef.current) {
3232
editorRef.current._tiptapEditor.destroy();
3333
}
3434

3535
editorRef.current = initEditor(options);
36+
return editorRef.current;
3637
}, [deps, options]); //eslint-disable-line react-hooks/exhaustive-deps
3738

38-
return editorRef.current;
39+
return ret;
3940
};

0 commit comments

Comments
 (0)