import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react'; import { RenderRoot, RenderBlock, type BlockEditor, } from '@toeverything/components/editor-core'; import { useCurrentEditors } from '@toeverything/datasource/state'; import { createEditor } from './create-editor'; interface AffineEditorProps { workspace: string; rootBlockId: string; /** * Whether to show the visual blank at the bottom of the article */ scrollBlank?: boolean; isWhiteboard?: boolean; scrollContainer?: HTMLElement; scrollController?: { lockScroll: () => void; unLockScroll: () => void; }; } function _useConstantWithDispose( workspace: string, rootBlockId: string, isWhiteboard: boolean ) { const ref = useRef<{ data: BlockEditor; onInit: boolean }>(null); const { setCurrentEditors } = useCurrentEditors(); ref.current ??= { data: createEditor(workspace, rootBlockId, isWhiteboard), onInit: true, }; useEffect(() => { if (ref.current.onInit) { ref.current.onInit = false; } else { ref.current.data = createEditor( workspace, rootBlockId, isWhiteboard ); } setCurrentEditors(prev => ({ ...prev, [rootBlockId]: ref.current.data, })); return () => ref.current.data.dispose(); }, [workspace, rootBlockId, isWhiteboard, setCurrentEditors]); return ref.current.data; } export const AffineEditor = forwardRef( ( { workspace, rootBlockId, scrollBlank = true, isWhiteboard, scrollController, scrollContainer, }, ref ) => { const editor = _useConstantWithDispose( workspace, rootBlockId, isWhiteboard ); useEffect(() => { if (scrollContainer) { editor.scrollManager.scrollContainer = scrollContainer; } if (scrollController) { editor.scrollManager.scrollController = scrollController; } }, [editor, scrollContainer, scrollController]); useImperativeHandle(ref, () => editor); return ( ); } );