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

232 lines
8.9 KiB
TypeScript
Raw Normal View History

2023-07-25 20:27:29 +03:00
import { type AxiosResponse } from 'axios';
import { useCallback, useLayoutEffect, useState } from 'react';
import { toast } from 'react-toastify';
2023-07-25 20:27:29 +03:00
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';
import { type CstType, EditMode, type INewCstData } from '../../utils/models';
import { createAliasFor, getCstTypeLabel } from '../../utils/staticUI';
2023-07-20 17:11:03 +03:00
import ConstituentsSideList from './ConstituentsSideList';
import CreateCstModal from './CreateCstModal';
2023-07-25 20:27:29 +03:00
import ExpressionEditor from './ExpressionEditor';
2023-07-15 17:46:19 +03:00
function ConstituentEditor() {
2023-07-25 20:27:29 +03:00
const {
activeCst, activeID, schema, setActiveID, processing, isEditable,
cstDelete, cstUpdate, cstCreate
2023-07-18 14:55:40 +03:00
} = useRSForm();
const [showCstModal, setShowCstModal] = 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');
useLayoutEffect(() => {
2023-07-21 00:09:05 +03:00
if (schema?.items && schema?.items.length > 0) {
2023-07-25 20:27:29 +03:00
// TODO: figure out why schema.items could be undef?
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
setActiveID((prev) => (prev ?? schema?.items![0].id ?? undefined));
}
}, [schema, setActiveID])
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();
2023-07-18 14:55:40 +03:00
if (!processing) {
const data = {
2023-07-25 20:27:29 +03:00
alias: alias,
convention: convention,
definition_formal: expression,
definition_text: {
raw: textDefinition,
resolved: ''
2023-07-18 14:55:40 +03:00
},
2023-07-25 20:27:29 +03:00
term: {
raw: term,
resolved: '',
forms: activeCst?.term?.forms ?? []
2023-07-18 14:55:40 +03:00
}
};
2023-07-25 20:27:29 +03:00
cstUpdate(data, () => toast.success('Изменения сохранены'));
2023-07-18 14:55:40 +03:00
}
};
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);
if (index !== -1 && index + 1 < schema.items.length) {
setActiveID(schema.items[index + 1].id);
}
cstDelete(data, () => toast.success('Конституента удалена'));
}, [activeID, schema, setActiveID, cstDelete]);
const handleAddNew = useCallback(
2023-07-25 20:27:29 +03:00
(csttype?: CstType) => {
if (!activeID || !schema?.items) {
return;
}
if (!csttype) {
setShowCstModal(true);
} else {
2023-07-25 20:27:29 +03:00
const data: INewCstData = {
csttype: csttype,
alias: createAliasFor(csttype, schema),
insert_after: activeID
}
2023-07-25 20:27:29 +03:00
cstCreate(data,
(response: AxiosResponse) => {
setActiveID(response.data.new_cst.id);
toast.success(`Конституента добавлена: ${response.data.new_cst.alias as string}`);
});
}
}, [activeID, schema, cstCreate, setActiveID]);
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'>
<CreateCstModal
show={showCstModal}
2023-07-25 20:27:29 +03:00
toggle={() => { setShowCstModal(!showCstModal); }}
onCreate={handleAddNew}
defaultType={activeCst?.cstType as CstType}
/>
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'
disabled={!isEditable}
>
<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'>
<button type='button'
title='Создать конституенты после данной'
className='px-1 py-1 font-bold rounded-full whitespace-nowrap disabled:cursor-not-allowed clr-btn-clear'
disabled={!isEditable}
2023-07-25 20:27:29 +03:00
onClick={() => { handleAddNew(); }}
>
2023-07-25 20:27:29 +03:00
<SmallPlusIcon size={5} color={isEditable ? 'text-green' : ''} />
</button>
<button type='button'
title='Удалить редактируемую конституенту'
className='px-1 py-1 font-bold rounded-full whitespace-nowrap disabled:cursor-not-allowed clr-btn-clear'
disabled={!isEditable}
onClick={handleDelete}
>
2023-07-25 20:27:29 +03:00
<DumpBinIcon size={5} color={isEditable ? 'text-red' : ''} />
</button>
</div>
</div>
<TextArea id='term' label='Термин'
placeholder='Схемный или предметный термин, обозначающий данное понятие или утверждение'
rows={2}
value={term}
disabled={!isEditable}
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
/>
<ExpressionEditor id='expression' label='Формальное выражение'
placeholder='Родоструктурное выражение, задающее формальное определение'
value={expression}
disabled={!isEditable}
2023-07-25 20:27:29 +03:00
isActive={editMode === 'rslang'}
toggleEditMode={() => { setEditMode(EditMode.RSLANG); }}
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}
disabled={!isEditable}
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}
disabled={!isEditable}
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='Сохранить изменения'
disabled={!isEditable}
icon={<SaveIcon size={6} />}
/>
</div>
</form>
2023-07-20 17:11:03 +03:00
<ConstituentsSideList expression={expression}/>
</div>
2023-07-15 17:46:19 +03:00
);
}
2023-07-25 20:27:29 +03:00
export default ConstituentEditor;