import { forwardRef, useCallback, useContext, useEffect, useImperativeHandle, useRef } from "react"; import TinyUndo from "tiny-undo"; import appContext from "../../stores/appContext"; import { storage } from "../../helpers/storage"; import useRefresh from "../../hooks/useRefresh"; import Only from "../common/OnlyWhen"; import "../../less/editor.less"; export interface EditorRefActions { focus: FunctionType; insertText: (text: string) => void; setContent: (text: string) => void; getContent: () => string; } interface Props { className: string; initialContent: string; placeholder: string; showConfirmBtn: boolean; showCancelBtn: boolean; showTools: boolean; onConfirmBtnClick: (content: string) => void; onCancelBtnClick: () => void; onContentChange: (content: string) => void; } const Editor = forwardRef((props: Props, ref: React.ForwardedRef) => { const { globalState: { useTinyUndoHistoryCache }, } = useContext(appContext); const { className, initialContent, placeholder, showConfirmBtn, showCancelBtn, showTools, onConfirmBtnClick: handleConfirmBtnClickCallback, onCancelBtnClick: handleCancelBtnClickCallback, onContentChange: handleContentChangeCallback, } = props; const editorRef = useRef(null); const tinyUndoRef = useRef(null); const refresh = useRefresh(); useEffect(() => { if (initialContent) { editorRef.current!.value = initialContent; refresh(); } }, []); useEffect(() => { if (useTinyUndoHistoryCache) { const { tinyUndoActionsCache, tinyUndoIndexCache } = storage.get(["tinyUndoActionsCache", "tinyUndoIndexCache"]); tinyUndoRef.current = new TinyUndo(editorRef.current!, { interval: 5000, initialActions: tinyUndoActionsCache, initialIndex: tinyUndoIndexCache, }); tinyUndoRef.current.subscribe((actions, index) => { storage.set({ tinyUndoActionsCache: actions, tinyUndoIndexCache: index, }); }); return () => { tinyUndoRef.current?.destroy(); }; } else { tinyUndoRef.current?.destroy(); tinyUndoRef.current = null; storage.remove(["tinyUndoActionsCache", "tinyUndoIndexCache"]); } }, [useTinyUndoHistoryCache]); useEffect(() => { if (editorRef.current) { editorRef.current.style.height = "auto"; editorRef.current.style.height = (editorRef.current.scrollHeight ?? 0) + "px"; } }, [editorRef.current?.value]); useImperativeHandle( ref, () => ({ focus: () => { editorRef.current!.focus(); }, insertText: (rawText: string) => { const prevValue = editorRef.current!.value; editorRef.current!.value = prevValue + rawText; handleContentChangeCallback(editorRef.current!.value); refresh(); }, setContent: (text: string) => { editorRef.current!.value = text; refresh(); }, getContent: (): string => { return editorRef.current?.value ?? ""; }, }), [] ); const handleEditorInput = useCallback(() => { handleContentChangeCallback(editorRef.current!.value); refresh(); }, []); const handleEditorKeyDown = useCallback((event: React.KeyboardEvent) => { event.stopPropagation(); if (event.code === "Enter") { if (event.metaKey || event.ctrlKey) { handleCommonConfirmBtnClick(); } } refresh(); }, []); const handleCommonConfirmBtnClick = useCallback(() => { handleConfirmBtnClickCallback(editorRef.current!.value); editorRef.current!.value = ""; refresh(); // After confirm btn clicked, tiny-undo should reset state(clear actions and index) tinyUndoRef.current?.resetState(); }, []); const handleCommonCancelBtnClick = useCallback(() => { handleCancelBtnClickCallback(); }, []); return (
{/* nth */}
); }); export default Editor;