/
h0tnanny
/
IotPlatform
Обзор
Документация
Войти
/
h0tnanny
/
IotPlatform
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
client/src/components/WorkflowEditor/CodeEditor.tsx
195 строк
7 KB
h0tnanny
fix readonly permissions
01 фев 2026, 23:32
01 фев 2026, 23:32
01880f6
Код
Авторство
О чём код?
import { useRef, useEffect } from 'react'; import Editor from '@monaco-editor/react'; import type { editor } from 'monaco-editor'; import styles from './CodeEditor.module.scss'; interface Variable { name: string; type: string; } interface CodeEditorProps { value: string; onChange: (value: string) => void; onKeyDown?: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void; rows?: number; variables?: Variable[]; readOnly?: boolean; } export function CodeEditor({ value, onChange, onKeyDown, rows = 6, variables = [], readOnly = false }: CodeEditorProps) { const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null); const monacoRef = useRef<any>(null); const completionProviderRef = useRef<any>(null); const handleEditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: any) => { editorRef.current = editor; monacoRef.current = monaco; // Регистрируем провайдер автодополнения registerCompletionProvider(monaco); // Добавляем обработчик клавиш для совместимости с существующим API editor.onKeyDown((e) => { // Создаем псевдо-событие для совместимости if (onKeyDown) { const fakeEvent = { ctrlKey: e.ctrlKey, metaKey: e.metaKey, code: e.code, key: e.browserEvent.key, preventDefault: () => e.preventDefault(), currentTarget: { selectionStart: editor.getPosition()?.column || 0, }, } as any; onKeyDown(fakeEvent); } }); }; // Регистрация провайдера автодополнения const registerCompletionProvider = (monaco: any) => { // Удаляем предыдущий провайдер, если есть if (completionProviderRef.current) { completionProviderRef.current.dispose(); } // Регистрируем новый провайдер completionProviderRef.current = monaco.languages.registerCompletionItemProvider('javascript', { triggerCharacters: ['.'], provideCompletionItems: (model: any, position: any) => { const textUntilPosition = model.getValueInRange({ startLineNumber: position.lineNumber, startColumn: 1, endLineNumber: position.lineNumber, endColumn: position.column, }); const word = model.getWordUntilPosition(position); const range = { startLineNumber: position.lineNumber, endLineNumber: position.lineNumber, startColumn: word.startColumn, endColumn: word.endColumn, }; const suggestions: any[] = []; // Проверяем, что перед курсором написано "variables." const matchVariables = textUntilPosition.match(/variables\.(\w*)$/); if (matchVariables) { // Создаем подсказки для каждой переменной variables.forEach((variable) => { suggestions.push({ label: variable.name, kind: monaco.languages.CompletionItemKind.Field, detail: variable.type, documentation: `Переменная типа ${variable.type}`, insertText: variable.name, range: range, sortText: '0' + variable.name, // Приоритет }); }); } else { // Базовые подсказки для variables suggestions.push({ label: 'variables', kind: monaco.languages.CompletionItemKind.Variable, detail: 'Объект с переменными workflow', documentation: 'Доступ к переменным: variables.имя_переменной', insertText: 'variables.', range: range, command: { id: 'editor.action.triggerSuggest', title: 'Trigger Suggest' }, }); // Добавляем контекст suggestions.push({ label: 'context', kind: monaco.languages.CompletionItemKind.Variable, detail: 'Контекст выполнения workflow', documentation: 'Доступ к контексту выполнения', insertText: 'context', range: range, }); } return { suggestions }; }, }); }; // Обновляем провайдер при изменении списка переменных useEffect(() => { if (monacoRef.current) { registerCompletionProvider(monacoRef.current); } }, [variables]); // Очистка при размонтировании useEffect(() => { return () => { if (completionProviderRef.current) { completionProviderRef.current.dispose(); } }; }, []); const handleChange = (value: string | undefined) => { onChange(value || ''); }; // Вычисляем высоту редактора на основе количества строк const height = rows ? rows * 19 + 10 : 150; return ( <div className={styles.codeEditor}> <Editor height={`${height}px`} defaultLanguage="javascript" value={value} onChange={handleChange} onMount={handleEditorDidMount} theme="vs" options={{ readOnly, minimap: { enabled: false }, fontSize: 13, lineNumbers: 'on', scrollBeyondLastLine: false, automaticLayout: true, tabSize: 2, insertSpaces: true, wordWrap: 'on', contextmenu: true, quickSuggestions: { other: true, comments: false, strings: true, }, suggestOnTriggerCharacters: true, acceptSuggestionOnEnter: 'on', snippetSuggestions: 'inline', parameterHints: { enabled: true }, formatOnPaste: true, formatOnType: true, autoClosingBrackets: 'always', autoClosingQuotes: 'always', folding: true, foldingStrategy: 'indentation', showFoldingControls: 'mouseover', matchBrackets: 'always', renderLineHighlight: 'all', scrollbar: { vertical: 'auto', horizontal: 'auto', useShadows: false, verticalScrollbarSize: 10, horizontalScrollbarSize: 10, }, }} loading={<div className={styles.loading}>Загрузка редактора...</div>} /> </div> ); }