import React, { useCallback, useRef, useEffect, FC } from 'react'; import { Editor, DraftHandleValue } from 'draft-js'; import Title from './components/title'; // `ImageToolbar`, `InlineToolbar` and `Sidebar` only has one instance. import ImageToolbar from './components/image-toolbar'; import InlineToolbar from './components/inline-toolbar'; import Sidebar from './components/sidebar'; import compareArray from './utils/compareArray'; import './style.css'; // // https://draftjs.org/docs/advanced-topics-issues-and-pitfalls.html#missing-draftcss import 'draft-js/dist/Draft.css'; import withEditor from './withEditor'; import { EditorProps, EditorPropsBefore } from './types'; const NewEditor: FC = props => { const { getEditor, forwardRef, placeholder, imageRef, inlineRef, sidebarRef, blockRenderMap, customStyleMap, } = props; const { hooks, editorState } = getEditor(); const didUpdate = useRef(false); const pasteText = useRef(); // ts-hint: useRef([]) as RefObject is not right. please refer to // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31065 const lastBlockMapKeys = useRef([]); const isInCompositionModeRef = useRef(false); useEffect(() => { const currentContent = editorState.getCurrentContent(); const currentBlockMap = currentContent.getBlockMap(); const currentBlockMapKeys = currentBlockMap.keySeq().toArray(); const diff = compareArray(lastBlockMapKeys.current!, currentBlockMapKeys); const isInCompositionMode = editorState.isInCompositionMode(); const force = diff.length || (isInCompositionModeRef.current && !isInCompositionMode); hooks.syncBlockKeys.call(currentBlockMapKeys, force); isInCompositionModeRef.current = editorState.isInCompositionMode(); }); useEffect(() => { if (didUpdate.current) { hooks.didUpdate.call(editorState); hooks.updatePlaceholder.call(editorState, placeholder); hooks.syncSelectionChange.call(editorState); } }); useEffect(() => { hooks.updatePlaceholder.call(editorState, placeholder); didUpdate.current = true; }, [editorState, hooks.updatePlaceholder, placeholder]); useEffect(() => { const currentContent = editorState.getCurrentContent(); const currentBlockMap = currentContent.getBlockMap(); const currentBlockMapKeys = currentBlockMap.keySeq().toArray(); const diff = compareArray(lastBlockMapKeys.current!, currentBlockMapKeys); if (diff.length) { hooks.updateDragSubscription.call(diff); } lastBlockMapKeys.current = currentBlockMapKeys; }); const onChange = useCallback( newEditorState => { const { editorState } = getEditor(); let nextState = hooks.stateFilter.call( editorState, newEditorState, pasteText.current ); hooks.onChange.call(nextState); }, [getEditor, hooks.onChange, hooks.stateFilter] ); const handleKeyCommand = useCallback( (command, es) => hooks.handleKeyCommand.call(command, es), [hooks.handleKeyCommand] ); const getBlockStyle = useCallback(block => hooks.blockStyleFn.call(block), [ hooks.blockStyleFn, ]); const handleBlockRender = useCallback( contentBlock => { const { editorState } = getEditor(); return hooks.blockRendererFn.call(contentBlock, editorState); }, [getEditor, hooks.blockRendererFn] ); /** * To trigger `addImage` */ const handleDroppedFiles = useCallback( (dropSelection, files): DraftHandleValue => { hooks.handleDroppedFiles.call(editorState, dropSelection, files); return 'handled'; }, [editorState, hooks.handleDroppedFiles] ); return (
<Editor editorState={editorState} blockStyleFn={getBlockStyle} customStyleMap={customStyleMap} blockRenderMap={blockRenderMap} blockRendererFn={handleBlockRender} onChange={onChange} handleKeyCommand={handleKeyCommand} handleDroppedFiles={handleDroppedFiles} ref={forwardRef} /> </div> <ImageToolbar forwardRef={imageRef} /> <InlineToolbar forwardRef={inlineRef} /> <Sidebar forwardRef={sidebarRef} /> </div> ); }; const WrappedEditor = withEditor(NewEditor); // ts-hint: https://medium.com/@jrwebdev/react-higher-order-component-patterns-in-typescript-42278f7590fb // ts-hint: https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315 export default React.forwardRef<Editor, EditorPropsBefore>((props, ref) => ( <WrappedEditor {...props} forwardRef={ref} /> ));