ConceptPortal-public/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta.tsx

229 lines
8.9 KiB
TypeScript
Raw Normal View History

2023-07-25 22:29:33 +03:00
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { toast } from 'react-toastify';
2023-07-25 20:27:29 +03:00
2023-07-27 22:04:25 +03:00
import MiniButton from '../../components/Common/MiniButton';
import SubmitButton from '../../components/Common/SubmitButton';
2023-07-25 20:27:29 +03:00
import TextArea from '../../components/Common/TextArea';
import { DumpBinIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
import { useRSForm } from '../../context/RSFormContext';
2023-07-29 15:37:49 +03:00
import { type CstType, EditMode, ICstUpdateData, SyntaxTree } from '../../utils/models';
import { getCstTypeLabel } from '../../utils/staticUI';
2023-07-28 00:03:37 +03:00
import EditorRSExpression from './EditorRSExpression';
import ViewSideConstituents from './elements/ViewSideConstituents';
2023-07-27 22:04:25 +03:00
import { RSTabsList } from './RSTabs';
2023-07-15 17:46:19 +03:00
2023-07-29 03:31:21 +03:00
interface EditorConstituentaProps {
onShowAST: (ast: SyntaxTree) => void
onShowCreateCst: (selectedID: number | undefined, type: CstType | undefined) => void
2023-07-29 03:31:21 +03:00
}
2023-07-29 15:37:49 +03:00
function EditorConstituenta({ onShowAST, onShowCreateCst }: EditorConstituentaProps) {
const navigate = useNavigate();
2023-07-25 20:27:29 +03:00
const {
activeCst, activeID, schema, setActiveID, processing, isEditable,
2023-07-29 15:37:49 +03:00
cstDelete, cstUpdate
2023-07-18 14:55:40 +03:00
} = useRSForm();
2023-07-25 22:29:33 +03:00
const [isModified, setIsModified] = useState(false);
2023-07-20 17:11:03 +03:00
const [editMode, setEditMode] = useState(EditMode.TEXT);
const [alias, setAlias] = useState('');
const [type, setType] = useState('');
const [term, setTerm] = useState('');
const [textDefinition, setTextDefinition] = useState('');
const [expression, setExpression] = useState('');
const [convention, setConvention] = useState('');
2023-07-20 17:11:03 +03:00
const [typification, setTypification] = useState('N/A');
2023-07-25 22:29:33 +03:00
const isEnabled = useMemo(() => activeCst && isEditable, [activeCst, isEditable]);
useLayoutEffect(() => {
if (schema && schema?.items.length > 0) {
setActiveID((prev) => (prev ?? schema.items[0].id ?? undefined));
}
2023-07-25 22:29:33 +03:00
}, [schema, setActiveID]);
useLayoutEffect(() => {
if (!activeCst) {
setIsModified(false);
return;
}
setIsModified(
activeCst.term.raw !== term ||
activeCst.definition.text.raw !== textDefinition ||
2023-07-25 22:29:33 +03:00
activeCst.convention !== convention ||
activeCst.definition.formal !== expression
2023-07-25 22:29:33 +03:00
);
}, [activeCst, activeCst?.term, activeCst?.definition.formal,
activeCst?.definition.text.raw, activeCst?.convention,
term, textDefinition, expression, convention]);
useLayoutEffect(() => {
if (activeCst) {
setAlias(activeCst.alias);
setType(getCstTypeLabel(activeCst.cstType));
2023-07-25 20:27:29 +03:00
setConvention(activeCst.convention ?? '');
setTerm(activeCst.term?.raw ?? '');
setTextDefinition(activeCst.definition?.text?.raw ?? '');
setExpression(activeCst.definition?.formal ?? '');
setTypification(activeCst?.parse?.typification || 'N/A');
}
}, [activeCst]);
2023-07-25 20:27:29 +03:00
const handleSubmit =
(event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
if (!activeID || processing) {
return;
2023-07-18 14:55:40 +03:00
}
const data: ICstUpdateData = {
id: activeID,
alias: alias,
convention: convention,
definition_formal: expression,
definition_raw: textDefinition,
term_raw: term
};
cstUpdate(data, () => { toast.success('Изменения сохранены'); });
};
const handleDelete = useCallback(
2023-07-25 20:27:29 +03:00
() => {
if (!activeID || !schema?.items || !window.confirm('Вы уверены, что хотите удалить конституенту?')) {
return;
}
2023-07-25 20:27:29 +03:00
const data = {
items: [{ id: activeID }]
}
const index = schema.items.findIndex((cst) => cst.id === activeID);
2023-07-28 00:03:37 +03:00
let newActive: number | undefined = undefined
if (index !== -1 && index + 1 < schema.items.length) {
2023-07-28 00:03:37 +03:00
newActive = schema.items[index + 1].id;
}
cstDelete(data, () => toast.success('Конституента удалена'));
2023-07-28 00:03:37 +03:00
if (newActive) navigate(`/rsforms/${schema.id}?tab=${RSTabsList.CST_EDIT}&active=${newActive}`);
}, [activeID, schema, cstDelete, navigate]);
const handleAddNew = useCallback(
2023-07-29 15:37:49 +03:00
() => {
if (!activeID || !schema) {
return;
}
2023-07-29 15:37:49 +03:00
onShowCreateCst(activeID, activeCst?.cstType);
}, [activeID, activeCst?.cstType, schema, onShowCreateCst]);
const handleRename = useCallback(() => {
toast.info('Переименование в разработке');
}, []);
const handleChangeType = useCallback(() => {
toast.info('Изменение типа в разработке');
}, []);
2023-07-15 17:46:19 +03:00
return (
<div className='flex items-start w-full gap-2'>
2023-07-20 17:11:03 +03:00
<form onSubmit={handleSubmit} className='flex-grow min-w-[50rem] max-w-min px-4 py-2 border'>
<div className='flex items-start justify-between'>
<button type='submit'
title='Сохранить изменения'
className='px-1 py-1 font-bold rounded whitespace-nowrap disabled:cursor-not-allowed clr-btn-primary'
2023-07-25 22:29:33 +03:00
disabled={!isModified || !isEnabled}
>
<SaveIcon size={5} />
</button>
<div className='flex items-start justify-center w-full gap-4'>
<span className='mr-12'>
2023-07-25 20:27:29 +03:00
<label
title='Переименовать конституенту'
className='font-semibold underline cursor-pointer'
onClick={handleRename}
>
ID
</label>
<b className='ml-2'>{alias}</b>
</span>
<span>
2023-07-25 20:27:29 +03:00
<label
title='Изменить тип конституенты'
className='font-semibold underline cursor-pointer'
onClick={handleChangeType}
>
Тип
</label>
<span className='ml-2'>{type}</span>
</span>
</div>
<div className='flex justify-end'>
2023-07-27 22:04:25 +03:00
<MiniButton
tooltip='Создать конституенты после данной'
2023-07-25 22:29:33 +03:00
disabled={!isEnabled}
2023-07-29 15:37:49 +03:00
onClick={handleAddNew}
2023-07-27 22:04:25 +03:00
icon={<SmallPlusIcon size={5} color={isEnabled ? 'text-green' : ''} />}
/>
<MiniButton
tooltip='Удалить редактируемую конституенту'
2023-07-25 22:29:33 +03:00
disabled={!isEnabled}
onClick={handleDelete}
2023-07-27 22:04:25 +03:00
icon={<DumpBinIcon size={5} color={isEnabled ? 'text-red' : ''} />}
/>
</div>
</div>
<TextArea id='term' label='Термин'
placeholder='Схемный или предметный термин, обозначающий данное понятие или утверждение'
rows={2}
value={term}
2023-07-25 22:29:33 +03:00
disabled={!isEnabled}
2023-07-20 17:11:03 +03:00
spellCheck
2023-07-25 20:27:29 +03:00
onChange={event => { setTerm(event.target.value); }}
onFocus={() => { setEditMode(EditMode.TEXT); }}
2023-07-20 17:11:03 +03:00
/>
<TextArea id='typification' label='Типизация'
rows={1}
value={typification}
disabled
/>
2023-07-28 00:03:37 +03:00
<EditorRSExpression id='expression' label='Формальное выражение'
placeholder='Родоструктурное выражение, задающее формальное определение'
value={expression}
2023-07-25 22:29:33 +03:00
disabled={!isEnabled}
isActive={editMode === EditMode.RSLANG}
2023-07-25 20:27:29 +03:00
toggleEditMode={() => { setEditMode(EditMode.RSLANG); }}
2023-07-29 03:31:21 +03:00
onShowAST={onShowAST}
2023-07-25 20:27:29 +03:00
onChange={event => { setExpression(event.target.value); }}
setValue={setExpression}
2023-07-20 17:11:03 +03:00
setTypification={setTypification}
/>
<TextArea id='definition' label='Текстовое определение'
placeholder='Лингвистическая интерпретация формального выражения'
rows={4}
value={textDefinition}
2023-07-25 22:29:33 +03:00
disabled={!isEnabled}
2023-07-20 17:11:03 +03:00
spellCheck
2023-07-25 20:27:29 +03:00
onChange={event => { setTextDefinition(event.target.value); }}
onFocus={() => { setEditMode(EditMode.TEXT); }}
/>
<TextArea id='convention' label='Конвенция / Комментарий'
placeholder='Договоренность об интерпретации неопределяемого понятия&#x000D;&#x000A;Комментарий к производному понятию'
rows={4}
value={convention}
2023-07-25 22:29:33 +03:00
disabled={!isEnabled}
2023-07-20 17:11:03 +03:00
spellCheck
2023-07-25 20:27:29 +03:00
onChange={event => { setConvention(event.target.value); }}
onFocus={() => { setEditMode(EditMode.TEXT); }}
/>
<div className='flex justify-center w-full mt-2'>
<SubmitButton
text='Сохранить изменения'
2023-07-25 22:29:33 +03:00
disabled={!isModified || !isEnabled}
icon={<SaveIcon size={6} />}
/>
</div>
</form>
2023-07-28 00:03:37 +03:00
<ViewSideConstituents expression={expression}/>
2023-07-20 17:11:03 +03:00
</div>
2023-07-15 17:46:19 +03:00
);
}
2023-07-28 00:03:37 +03:00
export default EditorConstituenta;