import { type AxiosResponse } from 'axios'; import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { toast } from 'react-toastify'; import Button from '../../components/Common/Button'; import Label from '../../components/Common/Label'; import { Loader } from '../../components/Common/Loader'; import { useRSForm } from '../../context/RSFormContext'; import useCheckExpression from '../../hooks/useCheckExpression'; import { CstType, TokenID } from '../../utils/models'; import ParsingResult from './ParsingResult'; import RSLocalButton from './RSLocalButton'; import RSTokenButton from './RSTokenButton'; import StatusBar from './StatusBar'; import { getSymbolSubstitute, TextWrapper } from './textEditing'; interface ExpressionEditorProps { id: string label: string isActive: boolean disabled?: boolean placeholder?: string value: string onChange: (event: React.ChangeEvent) => void toggleEditMode: () => void setTypification: (typificaiton: string) => void setValue: (expression: string) => void } function ExpressionEditor({ id, label, disabled, isActive, placeholder, value, setValue, toggleEditMode, setTypification, onChange }: ExpressionEditorProps) { const { schema, activeCst } = useRSForm(); const [isModified, setIsModified] = useState(false); const { parseData, checkExpression, resetParse, loading } = useCheckExpression({ schema }); const expressionCtrl = useRef(null); useLayoutEffect(() => { setIsModified(false); resetParse(); }, [activeCst, resetParse]); const handleCheckExpression = useCallback(() => { if (!activeCst) { return; } const prefix = activeCst?.alias + (activeCst?.cstType === CstType.STRUCTURED ? '::=' : ':=='); const expression = prefix + value; checkExpression(expression, (response: AxiosResponse) => { // TODO: update cursor position setIsModified(false); setTypification(response.data.typification); toast.success('проверка завершена'); }).catch(console.error); }, [value, checkExpression, activeCst, setTypification]); const handleEdit = useCallback((id: TokenID, key?: string) => { if (!expressionCtrl.current) { toast.error('Нет доступа к полю редактирования формального выражения'); return; } const text = new TextWrapper(expressionCtrl.current); if (id === TokenID.ID_LOCAL) { text.insertChar(key ?? 'unknown_local'); } else { text.insertToken(id); } text.finalize(); text.focus(); setValue(text.value); setIsModified(true); }, [setValue]); const handleChange = useCallback((event: React.ChangeEvent) => { onChange(event); setIsModified(true); }, [setIsModified, onChange]); const handleInput = useCallback((event: React.KeyboardEvent) => { if (!expressionCtrl.current) { return; } if (event.altKey) { const text = new TextWrapper(expressionCtrl.current); if (text.processAltKey(event.key)) { event.preventDefault(); text.finalize(); setValue(text.value); setIsModified(true); } } else if (!event.ctrlKey) { const newSymbol = getSymbolSubstitute(event.key); if (newSymbol) { event.preventDefault(); const text = new TextWrapper(expressionCtrl.current); text.replaceWith(newSymbol); text.finalize(); setValue(text.value); setIsModified(true); } } }, [expressionCtrl, setValue]); const handleFocusIn = useCallback(() => { toggleEditMode() }, [toggleEditMode]); const EditButtons = useMemo(() => { return (
); }, [handleEdit]) return (