UNPKG

@finos/legend-application-pure-ide

Version:
131 lines 7.99 kB
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 { useEffect, useRef, useState } from 'react'; import { observer } from 'mobx-react-lite'; import { editor as monacoEditorAPI } from 'monaco-editor'; import { useApplicationStore, useCommands } from '@finos/legend-application'; import { clsx, Dialog, WordWrapIcon } from '@finos/legend-art'; import { usePureIDEStore } from '../PureIDEStoreProvider.js'; import { at, returnUndefOnError } from '@finos/legend-shared'; import { CODE_EDITOR_THEME, getBaseCodeEditorOptions, moveCursorToPosition, } from '@finos/legend-code-editor'; const POSITION_PATTERN = /[0-9]+(?::[0-9]+)?/; const getPositionFromGoToLinePromptInputValue = (val) => { const parts = val.split(':'); if (parts.length < 1 || parts.length > 2) { return [1, undefined]; } return [ returnUndefOnError(() => parseInt(at(parts, 0))) ?? 1, returnUndefOnError(() => parseInt(at(parts, -1))), ]; }; export const GoToLinePrompt = observer((props) => { const { fileEditorState } = props; const [value, setValue] = useState(''); const inputRef = useRef(null); // validation const isValidValue = Boolean(value.match(POSITION_PATTERN)); const currentEditorCursorPosition = fileEditorState.textEditorState.editor?.getPosition(); const [currentLine, currentColumn] = getPositionFromGoToLinePromptInputValue(value); const isValidLineNumber = 1 <= currentLine && currentLine <= fileEditorState.textEditorState.model.getLineCount(); const error = !isValidValue ? 'Invalid value (format [line:column] - e.g. 123:45)' : !isValidLineNumber ? `Invalid line number` : undefined; // actions const closeModal = () => fileEditorState.setShowGoToLinePrompt(false); const onValueChange = (event) => setValue(event.target.value); const create = (event) => { event.preventDefault(); closeModal(); fileEditorState.textEditorState.setForcedCursorPosition({ lineNumber: currentLine, column: currentColumn ?? 1, }); }; const handleEnter = () => inputRef.current?.focus(); return (_jsx(Dialog, { open: true, onClose: closeModal, TransitionProps: { onEnter: handleEnter, }, classes: { container: 'command-modal__container' }, PaperProps: { classes: { root: 'command-modal__inner-container' } }, children: _jsxs("div", { className: "modal modal--dark command-modal", children: [_jsx("div", { className: "modal__title", children: "Go to..." }), _jsxs("div", { className: "command-modal__content", children: [_jsx("form", { className: "command-modal__content__form", onSubmit: create, children: _jsxs("div", { className: "input-group command-modal__content__input", children: [_jsx("input", { ref: inputRef, className: "input input--dark", onChange: onValueChange, placeholder: currentEditorCursorPosition ? `Current Line: ${currentEditorCursorPosition.lineNumber}, Col: ${currentEditorCursorPosition.column}. Type a line between 1 and ${fileEditorState.textEditorState.model.getLineCount()} to navigate to` : undefined, value: value, spellCheck: false }), value !== '' && error && (_jsx("div", { className: "input-group__error-message", children: error }))] }) }), _jsx("button", { className: "command-modal__content__submit-btn btn--dark", disabled: value === '' || Boolean(error), onClick: create, children: "Go" })] })] }) })); }); export const GenericFileEditor = observer((props) => { const { editorState } = props; const ideStore = usePureIDEStore(); const applicationStore = useApplicationStore(); const textInputRef = useRef(null); const [editor, setEditor] = useState(); useEffect(() => { if (!editor && textInputRef.current) { const element = textInputRef.current; const newEditor = monacoEditorAPI.create(element, { ...getBaseCodeEditorOptions(), theme: CODE_EDITOR_THEME.DEFAULT_DARK, wordWrap: editorState.textEditorState.wrapText ? 'on' : 'off', readOnly: editorState.file.RO, }); newEditor.onDidChangeModelContent(() => { const currentVal = newEditor.getValue(); if (currentVal !== editorState.file.content) { // the assertion above is to ensure we don't accidentally clear error on initialization of the editor editorState.clearError(); // clear error on content change/typing } editorState.file.setContent(currentVal); }); // manual trigger to support cursor observability newEditor.onDidChangeCursorPosition(() => { editorState.textEditorState.notifyCursorObserver(); }); newEditor.onDidChangeCursorSelection(() => { editorState.textEditorState.notifyCursorObserver(); }); // Restore the editor model and view state newEditor.setModel(editorState.textEditorState.model); if (editorState.textEditorState.viewState) { newEditor.restoreViewState(editorState.textEditorState.viewState); } newEditor.focus(); // focus on the editor initially editorState.textEditorState.setEditor(newEditor); setEditor(newEditor); } }, [ideStore, applicationStore, editorState, editor]); useCommands(editorState); useEffect(() => { if (editor) { if (editorState.textEditorState.forcedCursorPosition) { moveCursorToPosition(editor, editorState.textEditorState.forcedCursorPosition); editorState.textEditorState.setForcedCursorPosition(undefined); } } }, [editor, editorState, editorState.textEditorState.forcedCursorPosition]); // clean up useEffect(() => () => { if (editor) { // persist editor view state (cursor, scroll, etc.) to restore on re-open editorState.textEditorState.setViewState(editor.saveViewState() ?? undefined); // NOTE: dispose the editor to prevent potential memory-leak editor.dispose(); } }, [editorState, editor]); return (_jsxs("div", { className: "panel editor-group file-editor", children: [_jsx("div", { className: "panel__header file-editor__header", children: _jsxs("div", { className: "file-editor__header__actions", children: [_jsx("button", { className: clsx('file-editor__header__action', { 'file-editor__header__action--active': editorState.textEditorState.wrapText, }), tabIndex: -1, onClick: () => editorState.textEditorState.setWrapText(!editorState.textEditorState.wrapText), title: "Toggle Text Wrap", children: _jsx(WordWrapIcon, { className: "file-editor__icon--text-wrap" }) }), editorState.showGoToLinePrompt && (_jsx(GoToLinePrompt, { fileEditorState: editorState }))] }) }), _jsx("div", { className: "panel__content file-editor__content", children: _jsx("div", { className: "code-editor__container", children: _jsx("div", { className: "code-editor__body", ref: textInputRef }) }) })] })); }); //# sourceMappingURL=GenericFileEditor.js.map