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 , ref ) => { const { globalState : { useTinyUndoHistoryCache } , } = useContext ( appContext ) ; const { className , initialContent , placeholder , showConfirmBtn , showCancelBtn , showTools , onConfirmBtnClick : handleConfirmBtnClickCallback , onCancelBtnClick : handleCancelBtnClickCallback , onContentChange : handleContentChangeCallback , } = props ; const editorRef = useRef < HTMLTextAreaElement > ( null ) ; const tinyUndoRef = useRef < TinyUndo | null > ( 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 ) => { const prevValue = editorRef . current ! . value ; editorRef . current ! . value = prevValue + rawText ; handleContentChangeCallback ( editorRef . current ! . value ) ; refresh ( ) ; } , setContent : ( text ) => { editorRef . current ! . value = text ; refresh ( ) ; } , getContent : ( ) : string => { return editorRef . current ?. value ?? "" ; } , } ) , [ ] ) ; const handleEditorInput = useCallback ( ( ) => { handleContentChangeCallback ( editorRef . current ! . value ) ; refresh ( ) ; } , [ ] ) ; const handleEditorKeyDown = useCallback ( ( event ) => { event . stopPropagation ( ) ; if ( event . code === "Enter" ) { if ( event . metaKey || event . ctrlKey ) { handleCommonConfirmBtnClick ( ) ; } } refresh ( ) ; } , [ ] ) ; const handleCommonConfirmBtnClick = useCallback ( ( ) => { handleConfirmBtnClickCallback ( editorRef . current ! . value ) ; editorRef . current ! . value = "" ; refresh ( ) ; tinyUndoRef . current ?. resetState ( ) ; } , [ ] ) ; const handleCommonCancelBtnClick = useCallback ( ( ) => { handleCancelBtnClickCallback ( ) ; } , [ ] ) ; return ( < div className = { "common-editor-wrapper " + className } > < textarea className = "common-editor-inputer" rows = { 1 } placeholder = { placeholder } ref = { editorRef } onInput = { handleEditorInput } onKeyDown = { handleEditorKeyDown } > < /textarea> < div className = "common-tools-wrapper" > < Only when = { showTools } > < div className = { "common-tools-container" } > { } < /div> < /Only> < div className = "btns-container" > < Only when = { showCancelBtn } > < button className = "action-btn cancel-btn" onClick = { handleCommonCancelBtnClick } > 撤销修改 < /button> < /Only> < Only when = { showConfirmBtn } > < button className = "action-btn confirm-btn" disabled = { ! editorRef . current ?. value } onClick = { handleCommonConfirmBtnClick } > 记下 < span className = "icon-text" > ✍ ️ < /span> < /button> < /Only> < /div> < /div> < /div>); } ) ; export default Editor ;