UNPKG

@blocknote/react

Version:

A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.

58 lines (49 loc) 1.94 kB
import { BlockNoteEditor } from "@blocknote/core"; import { flushSync } from "react-dom"; import { Root, createRoot } from "react-dom/client"; export function renderToDOMSpec( fc: (refCB: (ref: HTMLElement | null) => void) => React.ReactNode, editor: BlockNoteEditor<any, any, any> | undefined ) { let contentDOM: HTMLElement | undefined; const div = document.createElement("div"); let root: Root | undefined; if (editor?.elementRenderer) { // Render temporarily using `ElementRenderer` // This way React Context will still work, as `fc` will be rendered inside the existing React tree editor.elementRenderer( fc((el) => (contentDOM = el || undefined)), div ); } else { // If no editor is provided, use a temporary root. This is currently only used for Styles (see ReactStyleSpec). // In this case, react context etc. won't be available inside `fc` // We also use this if _tiptapEditor or _tiptapEditor.contentComponent is undefined, use a temporary root. // This is actually a fallback / temporary fix, as normally this shouldn't happen (see #755). TODO: find cause root = createRoot(div); flushSync(() => { root!.render(fc((el) => (contentDOM = el || undefined))); }); } if (!div.childElementCount) { // TODO // eslint-disable-next-line no-console console.warn("ReactInlineContentSpec: renderHTML() failed"); return { dom: document.createElement("span"), }; } // clone so we can unmount the react root contentDOM?.setAttribute("data-tmp-find", "true"); const cloneRoot = div.cloneNode(true) as HTMLElement; const dom = cloneRoot.firstElementChild! as HTMLElement; const contentDOMClone = cloneRoot.querySelector( "[data-tmp-find]" ) as HTMLElement | null; contentDOMClone?.removeAttribute("data-tmp-find"); root?.unmount(); return { dom, contentDOM: contentDOMClone || undefined, }; }