import type * as monaco from 'monaco-editor'; import React from 'react'; import { connect } from 'react-redux'; import { load } from 'vj/components/monaco/loader'; interface ScratchpadOptions { value?: string; language?: string; handleUpdateCode?: (str: string, event: monaco.editor.IModelContentChangedEvent) => void; mainSize?: number; recordSize?: number; pretestSize?: number; } export default connect((state: any) => ({ value: state.editor.code, language: window.LANGS[state.editor.lang]?.monaco, mainSize: state.ui.main.size, pretestSize: state.ui.pretest.size, recordSize: state.ui.records.size, }), (dispatch) => ({ handleUpdateCode: (code: string) => { dispatch({ type: 'SCRATCHPAD_EDITOR_UPDATE_CODE', payload: code, }); }, }))(class MonacoEditor extends React.PureComponent { disposable: monaco.IDisposable[] = []; __prevent_trigger_change_event = false; model: monaco.editor.ITextModel; editor: monaco.editor.IStandaloneCodeEditor; containerElement: HTMLElement; async componentDidMount() { const value = this.props.value || ''; const { language } = this.props; const { monaco, registerAction, customOptions } = await load([language]); const uri = monaco.Uri.parse(`hydro://${UiContext.pdoc.pid || UiContext.pdoc.docId}.${language}`); this.model = monaco.editor.getModel(uri) || monaco.editor.createModel(value, language, uri); if (this.containerElement) { const config: monaco.editor.IStandaloneEditorConstructionOptions = { theme: 'vs-light', ...customOptions, fontFamily: UserContext.codeFontFamily, lineNumbers: 'on', glyphMargin: true, lightbulb: { enabled: true }, model: this.model, }; this.editor = monaco.editor.create(this.containerElement, config); registerAction(this.editor, this.model); this.disposable.push( this.editor.onDidChangeModelContent((event) => { if (!this.__prevent_trigger_change_event) { this.props.handleUpdateCode?.(this.editor.getValue({ lineEnding: '\n', preserveBOM: false }), event); } }), ); (window as any).editor = this.editor; (window as any).monaco = monaco; window.Hydro.bus.emit('scratchpadEditorCreate', this.editor, monaco); } } async componentDidUpdate(prevProps) { const { value, language, mainSize, recordSize, pretestSize, } = this.props; const { monaco } = await load([language]); const { editor, model } = this; const { LF } = monaco.editor.EndOfLinePreference; if (model && editor && this.props.value != null && this.props.value !== model.getValue(LF, false)) { this.__prevent_trigger_change_event = true; editor.pushUndoStop(); model.pushEditOperations( [], [ { range: model.getFullModelRange(), text: value!, }, ], () => null, ); editor.pushUndoStop(); this.__prevent_trigger_change_event = false; } if (model && editor && prevProps.language !== language) { const val = model.getValue(LF, false); model.dispose(); const uri = monaco.Uri.parse(`hydro://${UiContext.pdoc.pid || UiContext.pdoc.docId}.${language}`); this.model = monaco.editor.getModel(uri) || monaco.editor.createModel(val, language, uri); editor.setModel(this.model); } if (editor) { if (prevProps.mainSize !== mainSize || prevProps.recordSize !== recordSize || prevProps.pretestSize !== pretestSize) editor.layout(); } } componentWillUnmount() { if (this.editor) this.editor.dispose(); if (this.model) this.model.dispose(); this.disposable.map((i) => i.dispose()); } assignRef = (component) => { this.containerElement = component; }; render() { return (
); } });