import React, { useEffect, useRef, useImperativeHandle } from "react"; import classNames from "classnames"; import { useConfig } from "../util"; import { StyledProps } from "../_type"; import { useDefaultValue } from "../form"; import { useIsomorphicLayoutEffect } from "../_util/use-isomorphic-layout-effect"; type Obj = Record; export interface MonacoDiffEditorProps extends StyledProps { /** * [monaco-editor](https://microsoft.github.io/monaco-editor/index.html) * * @docType typeof monaco */ monaco: M; /** * 原始文本 */ original: string; /** * 编辑器高度 * * @default "100%" */ height?: React.CSSProperties["height"]; /** * 编辑器宽度 * * @default "100%" */ width?: React.CSSProperties["width"]; /** * 编辑器初始语言 */ language?: string; /** * 编辑器初始设置,详见 [IDiffEditorConstructionOptions](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IDiffEditorConstructionOptions.html) * * @docType monaco.editor.IDiffEditorConstructionOptions */ options?: Parameters[1]; /** * override language service, like editorService, [storageService](https://github.com/microsoft/vscode/blob/ff1e16eebb93af79fd6d7af1356c4003a120c563/src/vs/platform/storage/common/storage.ts#L37) ,详见 [IEditorOverrideServices](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IEditorOverrideServices.html) * * @docType monaco.editor.IEditorOverrideServices * @since 2.7.4 */ override?: Parameters[2]; /** * 默认变更文本 */ defaultValue?: string; /** * 变更文本 */ value?: string; /** * 变更文本变化回调 */ onChange?: (value: string, context: { event: any }) => void; } export const MonacoDiffEditor = React.forwardRef(function MonacoEditor< M extends Obj >( { monaco, original, options = {}, override = {}, language, className, style, width, height, ...props }: MonacoDiffEditorProps, ref: React.Ref<{ editor: ReturnType; }> ) { const { classPrefix } = useConfig(); const { value, onChange } = useDefaultValue(props as any, ""); const containerRef = useRef(null); const editorRef = useRef>(null); const changeEventRef = useRef(null); // https://github.com/react-monaco-editor/react-monaco-editor/blob/master/src/editor.js const preventTriggerChangeEvent = useRef(false); useEffect(() => { if (editorRef.current) { editorRef.current.layout(); } }, [width, height]); useEffect(() => { if (editorRef.current) { const { original: originalModel } = editorRef.current.getModel(); originalModel.setValue(original); } }, [original, monaco]); useEffect(() => { if (editorRef.current) { const { modified: modifiedModel, original: originalModel, } = editorRef.current.getModel(); monaco.editor.setModelLanguage(modifiedModel, language); monaco.editor.setModelLanguage(originalModel, language); } }, [language, monaco]); useEffect(() => { if (editorRef.current) { const editor = editorRef.current; const { modified: modifiedModel } = editor.getModel(); if (value != null && value !== modifiedModel.getValue()) { preventTriggerChangeEvent.current = true; editor.getModifiedEditor().pushUndoStop(); modifiedModel.pushEditOperations( [], [ { range: modifiedModel.getFullModelRange(), text: value, }, ], undefined ); editor.getModifiedEditor().pushUndoStop(); preventTriggerChangeEvent.current = false; } } }, [value, monaco]); useIsomorphicLayoutEffect(() => { const editor = monaco.editor.createDiffEditor( containerRef.current, { ...(options as any), }, override ); editorRef.current = editor; const originalModel = monaco.editor.createModel(original, language); const modifiedModel = monaco.editor.createModel(value, language); editor.setModel({ original: originalModel, modified: modifiedModel, }); changeEventRef.current = modifiedModel.onDidChangeContent(event => { if (!preventTriggerChangeEvent.current) { onChange(modifiedModel.getValue(), { event }); } }); return () => { editorRef.current.dispose(); originalModel.dispose(); modifiedModel.dispose(); if (changeEventRef.current) { changeEventRef.current.dispose(); } }; }, []); // eslint-disable-line react-hooks/exhaustive-deps useImperativeHandle(ref, () => ({ editor: editorRef.current, })); return (
); }) as (( props: MonacoDiffEditorProps & { ref?: React.Ref<{ editor: ReturnType; }>; } ) => React.ReactElement) & { displayName: string; }; MonacoDiffEditor.displayName = "MonacoDiffEditor";