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 { useAuth } from '../../context/AuthContext'; import { useRSForm } from '../../context/RSFormContext'; import useCheckExpression from '../../hooks/useCheckExpression'; import { TokenID } from '../../utils/enums'; import { IConstituenta, IRSErrorDescription, SyntaxTree } from '../../utils/models'; import { getCstExpressionPrefix, getTypificationLabel } from '../../utils/staticUI'; import ParsingResult from './elements/ParsingResult'; import RSInput from './elements/RSInput'; import RSLocalButton from './elements/RSLocalButton'; import RSTokenButton from './elements/RSTokenButton'; import StatusBar from './elements/StatusBar'; import { getSymbolSubstitute, TextWrapper } from './elements/textEditing'; interface EditorRSExpressionProps { id: string activeCst?: IConstituenta label: string isActive: boolean disabled?: boolean placeholder?: string onShowAST: (expression: string, ast: SyntaxTree) => void toggleEditMode: () => void setTypification: (typificaiton: string) => void value: string onChange: (newValue: string) => void setValue: (expression: string) => void } function EditorRSExpression({ id, activeCst, label, disabled, isActive, placeholder, value, setValue, onShowAST, toggleEditMode, setTypification, onChange }: EditorRSExpressionProps) { const { user } = useAuth(); const { schema } = useRSForm(); const [isModified, setIsModified] = useState(false); const { parseData, checkExpression, resetParse, loading } = useCheckExpression({ schema }); const expressionCtrl = useRef(null); useLayoutEffect(() => { setIsModified(false); resetParse(); }, [activeCst, resetParse]); function handleFocusIn() { toggleEditMode() } function handleChange(event: React.ChangeEvent) { onChange(event.target.value); setIsModified(true); } function handleCheckExpression() { if (!activeCst) { return; } const prefix = getCstExpressionPrefix(activeCst); const expression = prefix + value; checkExpression(expression, parse => { if (parse.errors.length > 0) { const errorPosition = parse.errors[0].position - prefix.length expressionCtrl.current!.selectionStart = errorPosition; expressionCtrl.current!.selectionEnd = errorPosition; } expressionCtrl.current!.focus(); setIsModified(false); setTypification(getTypificationLabel({ isValid: parse.parseResult, resultType: parse.typification, args: parse.args })); }); } const onShowError = useCallback( (error: IRSErrorDescription) => { if (!activeCst || !expressionCtrl.current) { return; } const errorPosition = error.position - getCstExpressionPrefix(activeCst).length expressionCtrl.current.selectionStart = errorPosition; expressionCtrl.current.selectionEnd = errorPosition; expressionCtrl.current.focus(); }, [activeCst]); 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 handleInput = useCallback( (event: React.KeyboardEvent) => { if (!expressionCtrl.current) { return; } const text = new TextWrapper(expressionCtrl.current); if (event.shiftKey && event.key === '*' && !event.altKey) { text.insertToken(TokenID.DECART); } else if (event.altKey) { if (!text.processAltKey(event.key)) { return; } } else if (!event.ctrlKey) { const newSymbol = getSymbolSubstitute(event.key); if (!newSymbol) { return; } text.replaceWith(newSymbol); } else { return; } event.preventDefault(); text.finalize(); setValue(text.value); setIsModified(true); }, [expressionCtrl, setValue]); const EditButtons = useMemo(() => { return (
); }, [handleEdit]); return (