@finos/legend-data-cube
Version:
114 lines • 6.03 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/**
* Copyright (c) 2020-present, Goldman Sachs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { cn } from '@finos/legend-art';
import { getBaseCodeEditorOptions, CODE_EDITOR_LANGUAGE, CODE_EDITOR_THEME, PURE_CODE_EDITOR_WORD_SEPARATORS, } from '@finos/legend-code-editor';
import { debounce, guaranteeNonNullable, isNonNullable, } from '@finos/legend-shared';
import { observer } from 'mobx-react-lite';
import { useRef, useState, useMemo, useEffect } from 'react';
import { MONACO_EDITOR_OVERFLOW_WIDGETS_ROOT_ID, getCodeSuggestions, } from '../../core/DataCubePureCodeEditorUtils.js';
import { editor as monacoEditorAPI, languages as monacoLanguagesAPI, } from 'monaco-editor';
import { runInAction } from 'mobx';
export const DataCubeCodeEditor = observer((props) => {
const { state } = props;
const editorRef = useRef(null);
const [editor, setEditor] = useState();
const suggestionsProvider = useRef(undefined);
const debouncedCheckReturnType = useMemo(() => debounce(() => {
state.getReturnType().catch((error) => {
state.alertHandler(error);
});
}, 500), [state]);
useEffect(() => {
if (!editor && editorRef.current) {
const element = editorRef.current;
const widgetRoot = document.getElementById(MONACO_EDITOR_OVERFLOW_WIDGETS_ROOT_ID);
const newEditor = monacoEditorAPI.create(element, {
...getBaseCodeEditorOptions(),
fontSize: 12,
language: CODE_EDITOR_LANGUAGE.PURE,
theme: CODE_EDITOR_THEME.GITHUB_LIGHT,
wordSeparators: PURE_CODE_EDITOR_WORD_SEPARATORS,
// Make sure the widgets (tooltips, menus) are not clipped by the container bounds
// and fix the problem where widgets are rendered with position=fixed not working well with parent
// containers (i.e. the draggable window) which has been transformed
// See https://dev.to/salilnaik/the-uncanny-relationship-between-position-fixed-and-transform-property-32f6
// See https://github.com/microsoft/monaco-editor/issues/2793#issuecomment-999337740
fixedOverflowWidgets: true,
...(isNonNullable(widgetRoot)
? { overflowWidgetsDomNode: widgetRoot }
: {}),
});
// NOTE: since engine suggestions are computed based on the current text content
// we put it in this block to simplify the flow and really to "bend" monaco-editor
// suggestion provider to our needs. But we also need to make sure this suggestion
// provider is scoped to the current editor only by checking the editor model
suggestionsProvider.current?.dispose();
suggestionsProvider.current =
monacoLanguagesAPI.registerCompletionItemProvider(CODE_EDITOR_LANGUAGE.PURE, {
// NOTE: this is a hack to fetch suggestions from engine for every keystroke
triggerCharacters: [...PURE_CODE_EDITOR_WORD_SEPARATORS, '$'],
provideCompletionItems: async (model, position, context) => {
let suggestions = [];
if (model.uri === state.editorModelUri) {
suggestions = suggestions.concat(await getCodeSuggestions(position, model, state.codePrefix, state.engine, guaranteeNonNullable(state.model), state.queryLambda));
}
return { suggestions };
},
});
newEditor.setModel(state.editorModel);
newEditor.onDidChangeModelContent(() => {
state.currentlyEditing = true;
const currentVal = newEditor.getValue();
if (currentVal !== state.code) {
runInAction(() => {
state.code = currentVal;
});
// clear error on content change/typing
state.clearError();
state.setReturnType(undefined);
debouncedCheckReturnType.cancel();
debouncedCheckReturnType();
}
});
// focus on the editor initially and set the cursor to the end
// since we're trying to create a new column
newEditor.focus();
newEditor.setPosition({
lineNumber: 1,
column: state.code.length + 1,
});
state.setEditor(newEditor);
setEditor(newEditor);
}
}, [state, editor, debouncedCheckReturnType]);
// clean up
useEffect(() => () => {
if (editor) {
editor.dispose();
suggestionsProvider.current?.dispose();
}
}, [editor]);
useEffect(() => {
state.editor?.updateOptions({
readOnly: state.finalizationState.isInProgress,
});
}, [state, state.finalizationState.isInProgress]);
return (_jsxs("div", { className: cn('relative h-full w-full border border-neutral-200', {
'border-red-500': state.hasErrors,
}), children: [_jsx("div", { className: "absolute left-0 top-0 h-full w-full overflow-hidden" }), _jsx("div", { ref: editorRef, style: { height: '100%', width: '100%' } })] }));
});
//# sourceMappingURL=DataCubeCodeEditor.js.map