2023-08-31 17:25:42 +03:00
|
|
|
|
import { Dispatch, SetStateAction, useLayoutEffect, useMemo, useState } from 'react';
|
2023-07-16 20:25:55 +03:00
|
|
|
|
import { toast } from 'react-toastify';
|
2023-07-25 20:27:29 +03:00
|
|
|
|
|
2023-08-02 21:35:24 +03:00
|
|
|
|
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
2023-07-27 22:04:25 +03:00
|
|
|
|
import MiniButton from '../../components/Common/MiniButton';
|
2023-08-23 22:57:25 +03:00
|
|
|
|
import ReferenceInput from '../../components/Common/ReferenceInput';
|
2023-07-16 20:25:55 +03:00
|
|
|
|
import SubmitButton from '../../components/Common/SubmitButton';
|
2023-07-25 20:27:29 +03:00
|
|
|
|
import TextArea from '../../components/Common/TextArea';
|
2023-08-23 18:11:42 +03:00
|
|
|
|
import HelpConstituenta from '../../components/Help/HelpConstituenta';
|
2023-08-22 23:45:59 +03:00
|
|
|
|
import { DumpBinIcon, HelpIcon, PenIcon, SaveIcon, SmallPlusIcon } from '../../components/Icons';
|
2023-07-25 20:27:29 +03:00
|
|
|
|
import { useRSForm } from '../../context/RSFormContext';
|
2023-09-10 20:17:18 +03:00
|
|
|
|
import useWindowSize from '../../hooks/useWindowSize';
|
2023-09-11 20:31:54 +03:00
|
|
|
|
import { EditMode } from '../../models/miscelanious';
|
|
|
|
|
import { CstType, IConstituenta, ICstCreateData, ICstRenameData, ICstUpdateData } from '../../models/rsform';
|
|
|
|
|
import { SyntaxTree } from '../../models/rslang';
|
2023-08-22 23:45:59 +03:00
|
|
|
|
import { getCstTypificationLabel } from '../../utils/staticUI';
|
2023-07-28 00:03:37 +03:00
|
|
|
|
import EditorRSExpression from './EditorRSExpression';
|
|
|
|
|
import ViewSideConstituents from './elements/ViewSideConstituents';
|
2023-07-15 17:46:19 +03:00
|
|
|
|
|
2023-08-13 13:18:50 +03:00
|
|
|
|
// Max height of content for left enditor pane
|
|
|
|
|
const UNFOLDED_HEIGHT = '59.1rem';
|
|
|
|
|
|
2023-09-10 20:17:18 +03:00
|
|
|
|
const SIDELIST_HIDE_THRESHOLD = 1000;
|
|
|
|
|
|
2023-07-29 03:31:21 +03:00
|
|
|
|
interface EditorConstituentaProps {
|
2023-08-22 22:38:27 +03:00
|
|
|
|
activeID?: number
|
2023-09-11 20:31:54 +03:00
|
|
|
|
activeCst?: IConstituenta | undefined
|
2023-08-22 22:38:27 +03:00
|
|
|
|
onOpenEdit: (cstID: number) => void
|
2023-08-01 21:55:18 +03:00
|
|
|
|
onShowAST: (expression: string, ast: SyntaxTree) => void
|
2023-08-16 18:32:37 +03:00
|
|
|
|
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void
|
2023-08-23 01:36:17 +03:00
|
|
|
|
onRenameCst: (initial: ICstRenameData) => void
|
2023-09-11 20:31:54 +03:00
|
|
|
|
onEditTerm: () => void
|
2023-08-22 22:38:27 +03:00
|
|
|
|
onDeleteCst: (selected: number[], callback?: (items: number[]) => void) => void
|
2023-08-31 17:25:42 +03:00
|
|
|
|
isModified: boolean
|
|
|
|
|
setIsModified: Dispatch<SetStateAction<boolean>>
|
2023-07-29 03:31:21 +03:00
|
|
|
|
}
|
|
|
|
|
|
2023-08-31 17:25:42 +03:00
|
|
|
|
function EditorConstituenta({
|
2023-09-11 20:31:54 +03:00
|
|
|
|
isModified, setIsModified, activeID, activeCst, onEditTerm,
|
2023-08-31 17:25:42 +03:00
|
|
|
|
onShowAST, onCreateCst, onRenameCst, onOpenEdit, onDeleteCst
|
|
|
|
|
}: EditorConstituentaProps) {
|
2023-09-10 20:17:18 +03:00
|
|
|
|
const windowSize = useWindowSize();
|
2023-09-11 20:31:54 +03:00
|
|
|
|
const { schema, processing, isEditable, cstUpdate, isForceAdmin } = useRSForm();
|
2023-08-27 22:08:18 +03:00
|
|
|
|
|
2023-07-20 17:11:03 +03:00
|
|
|
|
const [editMode, setEditMode] = useState(EditMode.TEXT);
|
|
|
|
|
|
2023-07-16 20:25:55 +03:00
|
|
|
|
const [alias, setAlias] = 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-16 20:25:55 +03:00
|
|
|
|
|
2023-07-25 22:29:33 +03:00
|
|
|
|
const isEnabled = useMemo(() => activeCst && isEditable, [activeCst, isEditable]);
|
|
|
|
|
|
2023-08-27 16:35:17 +03:00
|
|
|
|
useLayoutEffect(
|
|
|
|
|
() => {
|
2023-07-25 22:29:33 +03:00
|
|
|
|
if (!activeCst) {
|
|
|
|
|
setIsModified(false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
setIsModified(
|
2023-08-29 15:17:16 +03:00
|
|
|
|
activeCst.term_raw !== term ||
|
|
|
|
|
activeCst.definition_raw !== textDefinition ||
|
2023-07-25 22:29:33 +03:00
|
|
|
|
activeCst.convention !== convention ||
|
2023-08-29 15:17:16 +03:00
|
|
|
|
activeCst.definition_formal !== expression
|
2023-07-25 22:29:33 +03:00
|
|
|
|
);
|
2023-08-31 17:25:42 +03:00
|
|
|
|
return () => setIsModified(false);
|
2023-08-29 15:17:16 +03:00
|
|
|
|
}, [activeCst, activeCst?.term_raw, activeCst?.definition_formal,
|
|
|
|
|
activeCst?.definition_raw, activeCst?.convention,
|
2023-08-27 22:08:18 +03:00
|
|
|
|
term, textDefinition, expression, convention, setIsModified]);
|
2023-07-16 20:25:55 +03:00
|
|
|
|
|
2023-08-27 16:35:17 +03:00
|
|
|
|
useLayoutEffect(
|
|
|
|
|
() => {
|
2023-07-24 22:34:03 +03:00
|
|
|
|
if (activeCst) {
|
|
|
|
|
setAlias(activeCst.alias);
|
2023-08-29 15:17:16 +03:00
|
|
|
|
setConvention(activeCst.convention || '');
|
|
|
|
|
setTerm(activeCst.term_raw || '');
|
|
|
|
|
setTextDefinition(activeCst.definition_raw || '');
|
|
|
|
|
setExpression(activeCst.definition_formal || '');
|
2023-08-06 23:13:45 +03:00
|
|
|
|
setTypification(activeCst ? getCstTypificationLabel(activeCst) : 'N/A');
|
2023-07-16 20:25:55 +03:00
|
|
|
|
}
|
2023-08-05 01:14:41 +03:00
|
|
|
|
}, [activeCst, onOpenEdit, schema]);
|
2023-07-25 20:27:29 +03:00
|
|
|
|
|
2023-08-27 19:30:37 +03:00
|
|
|
|
function handleSubmit(event?: React.FormEvent<HTMLFormElement>) {
|
|
|
|
|
if (event) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
}
|
2023-07-26 23:11:00 +03:00
|
|
|
|
if (!activeID || processing) {
|
|
|
|
|
return;
|
2023-07-18 14:55:40 +03:00
|
|
|
|
}
|
2023-07-26 23:11:00 +03:00
|
|
|
|
const data: ICstUpdateData = {
|
|
|
|
|
id: activeID,
|
|
|
|
|
alias: alias,
|
|
|
|
|
convention: convention,
|
|
|
|
|
definition_formal: expression,
|
|
|
|
|
definition_raw: textDefinition,
|
|
|
|
|
term_raw: term
|
|
|
|
|
};
|
2023-08-27 16:35:17 +03:00
|
|
|
|
cstUpdate(data, () => toast.success('Изменения сохранены'));
|
2023-08-02 18:24:17 +03:00
|
|
|
|
}
|
2023-07-16 20:25:55 +03:00
|
|
|
|
|
2023-08-02 18:24:17 +03:00
|
|
|
|
function handleDelete() {
|
|
|
|
|
if (!schema || !activeID) {
|
2023-07-23 21:38:04 +03:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-08-02 18:24:17 +03:00
|
|
|
|
onDeleteCst([activeID]);
|
|
|
|
|
}
|
2023-07-23 21:38:04 +03:00
|
|
|
|
|
2023-08-02 18:24:17 +03:00
|
|
|
|
function handleCreateCst() {
|
2023-07-29 15:37:49 +03:00
|
|
|
|
if (!activeID || !schema) {
|
2023-07-23 21:38:04 +03:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-08-16 18:32:37 +03:00
|
|
|
|
const data: ICstCreateData = {
|
|
|
|
|
insert_after: activeID,
|
2023-08-29 15:17:16 +03:00
|
|
|
|
cst_type: activeCst?.cst_type ?? CstType.BASE,
|
2023-08-16 18:32:37 +03:00
|
|
|
|
alias: '',
|
|
|
|
|
term_raw: '',
|
|
|
|
|
definition_formal: '',
|
|
|
|
|
definition_raw: '',
|
|
|
|
|
convention: '',
|
|
|
|
|
};
|
|
|
|
|
onCreateCst(data);
|
2023-08-02 18:24:17 +03:00
|
|
|
|
}
|
2023-07-23 21:38:04 +03:00
|
|
|
|
|
2023-08-02 18:24:17 +03:00
|
|
|
|
function handleRename() {
|
2023-08-23 01:36:17 +03:00
|
|
|
|
if (!activeID || !activeCst) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const data: ICstRenameData = {
|
|
|
|
|
id: activeID,
|
|
|
|
|
alias: activeCst?.alias,
|
2023-08-29 15:17:16 +03:00
|
|
|
|
cst_type: activeCst.cst_type
|
2023-08-23 01:36:17 +03:00
|
|
|
|
};
|
|
|
|
|
onRenameCst(data);
|
2023-08-02 18:24:17 +03:00
|
|
|
|
}
|
2023-07-16 20:25:55 +03:00
|
|
|
|
|
2023-07-15 17:46:19 +03:00
|
|
|
|
return (
|
2023-08-13 13:18:50 +03:00
|
|
|
|
<div className='flex items-stretch w-full gap-2 mb-2 justify-stretch'>
|
2023-09-03 18:26:50 +03:00
|
|
|
|
<form onSubmit={handleSubmit} className='min-w-[50rem] max-w-min px-4 py-2 border-y border-r'>
|
2023-08-22 23:45:59 +03:00
|
|
|
|
<div className='relative'>
|
2023-09-11 20:31:54 +03:00
|
|
|
|
<div className='absolute top-0 left-0'>
|
|
|
|
|
<MiniButton
|
|
|
|
|
tooltip='Сохранить изменения'
|
|
|
|
|
disabled={!isModified || !isEnabled}
|
|
|
|
|
icon={<SaveIcon size={6} color={isModified && isEnabled ? 'text-primary' : ''}/>}
|
|
|
|
|
onClick={() => handleSubmit()}
|
|
|
|
|
/>
|
2023-08-22 23:45:59 +03:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className='relative'>
|
|
|
|
|
<div className='absolute top-0 right-0 flex justify-end'>
|
|
|
|
|
<MiniButton
|
|
|
|
|
tooltip='Удалить редактируемую конституенту'
|
|
|
|
|
disabled={!isEnabled}
|
|
|
|
|
onClick={handleDelete}
|
2023-09-03 18:26:50 +03:00
|
|
|
|
icon={<DumpBinIcon size={5} color={isEnabled ? 'text-warning' : ''} />}
|
2023-08-22 23:45:59 +03:00
|
|
|
|
/>
|
|
|
|
|
<MiniButton
|
|
|
|
|
tooltip='Создать конституенты после данной'
|
|
|
|
|
disabled={!isEnabled}
|
|
|
|
|
onClick={handleCreateCst}
|
2023-09-03 18:26:50 +03:00
|
|
|
|
icon={<SmallPlusIcon size={5} color={isEnabled ? 'text-success' : ''} />}
|
2023-08-22 23:45:59 +03:00
|
|
|
|
/>
|
|
|
|
|
<div id='cst-help' className='flex items-center ml-[6px]'>
|
|
|
|
|
<HelpIcon color='text-primary' size={5} />
|
2023-07-22 03:18:48 +03:00
|
|
|
|
</div>
|
2023-08-22 23:45:59 +03:00
|
|
|
|
<ConceptTooltip anchorSelect='#cst-help'>
|
2023-08-23 18:11:42 +03:00
|
|
|
|
<HelpConstituenta />
|
2023-08-22 23:45:59 +03:00
|
|
|
|
</ConceptTooltip>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2023-09-03 18:26:50 +03:00
|
|
|
|
<div className='flex items-center justify-center w-full gap-1 pr-10'>
|
2023-08-22 23:45:59 +03:00
|
|
|
|
<div className='font-semibold w-fit'>
|
|
|
|
|
<span className=''>Конституента </span>
|
|
|
|
|
<span className='ml-4'>{alias}</span>
|
2023-07-22 03:18:48 +03:00
|
|
|
|
</div>
|
2023-08-22 23:45:59 +03:00
|
|
|
|
<MiniButton
|
2023-09-11 20:31:54 +03:00
|
|
|
|
tooltip='Переименовать конституенту'
|
2023-08-22 23:45:59 +03:00
|
|
|
|
disabled={!isEnabled}
|
|
|
|
|
noHover
|
|
|
|
|
onClick={handleRename}
|
|
|
|
|
icon={<PenIcon size={4} color={isEnabled ? 'text-primary' : ''} />}
|
|
|
|
|
/>
|
2023-07-16 20:25:55 +03:00
|
|
|
|
</div>
|
2023-09-11 20:31:54 +03:00
|
|
|
|
{isForceAdmin && <div className='relative'>
|
|
|
|
|
<div className='absolute left-[3.2rem] top-[0.5rem]'>
|
|
|
|
|
<MiniButton
|
|
|
|
|
tooltip='Редактировать словоформы термина'
|
|
|
|
|
disabled={!isEnabled}
|
|
|
|
|
noHover
|
|
|
|
|
onClick={onEditTerm}
|
|
|
|
|
icon={<PenIcon size={4} color={isEnabled ? 'text-primary' : ''} />}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>}
|
2023-08-23 22:57:25 +03:00
|
|
|
|
<ReferenceInput id='term' label='Термин'
|
2023-09-11 20:31:54 +03:00
|
|
|
|
placeholder='Обозначение, используемое в текстовых определениях данной схемы'
|
2023-07-16 20:25:55 +03:00
|
|
|
|
rows={2}
|
|
|
|
|
value={term}
|
2023-08-29 15:17:16 +03:00
|
|
|
|
initialValue={activeCst?.term_raw ?? ''}
|
|
|
|
|
resolved={activeCst?.term_resolved ?? ''}
|
2023-07-25 22:29:33 +03:00
|
|
|
|
disabled={!isEnabled}
|
2023-07-20 17:11:03 +03:00
|
|
|
|
spellCheck
|
2023-08-10 18:35:49 +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}
|
2023-09-03 18:26:50 +03:00
|
|
|
|
colorClass='clr-app'
|
2023-07-20 17:11:03 +03:00
|
|
|
|
disabled
|
2023-07-16 20:25:55 +03:00
|
|
|
|
/>
|
2023-07-28 00:03:37 +03:00
|
|
|
|
<EditorRSExpression id='expression' label='Формальное выражение'
|
2023-08-02 21:35:24 +03:00
|
|
|
|
activeCst={activeCst}
|
2023-07-16 20:25:55 +03:00
|
|
|
|
placeholder='Родоструктурное выражение, задающее формальное определение'
|
|
|
|
|
value={expression}
|
2023-07-25 22:29:33 +03:00
|
|
|
|
disabled={!isEnabled}
|
2023-07-26 23:11:00 +03:00
|
|
|
|
isActive={editMode === EditMode.RSLANG}
|
2023-08-10 18:35:49 +03:00
|
|
|
|
toggleEditMode={() => setEditMode(EditMode.RSLANG)}
|
2023-07-29 03:31:21 +03:00
|
|
|
|
onShowAST={onShowAST}
|
2023-08-10 18:35:49 +03:00
|
|
|
|
onChange={newValue => setExpression(newValue)}
|
2023-07-20 17:11:03 +03:00
|
|
|
|
setTypification={setTypification}
|
2023-07-16 20:25:55 +03:00
|
|
|
|
/>
|
2023-08-23 22:57:25 +03:00
|
|
|
|
<ReferenceInput id='definition' label='Текстовое определение'
|
2023-07-16 20:25:55 +03:00
|
|
|
|
placeholder='Лингвистическая интерпретация формального выражения'
|
|
|
|
|
rows={4}
|
|
|
|
|
value={textDefinition}
|
2023-08-29 15:17:16 +03:00
|
|
|
|
initialValue={activeCst?.definition_raw ?? ''}
|
|
|
|
|
resolved={activeCst?.definition_resolved ?? ''}
|
2023-07-25 22:29:33 +03:00
|
|
|
|
disabled={!isEnabled}
|
2023-07-20 17:11:03 +03:00
|
|
|
|
spellCheck
|
2023-08-27 16:35:17 +03:00
|
|
|
|
onChange={event => setTextDefinition(event.target.value)}
|
|
|
|
|
onFocus={() => setEditMode(EditMode.TEXT)}
|
2023-07-16 20:25:55 +03:00
|
|
|
|
/>
|
|
|
|
|
<TextArea id='convention' label='Конвенция / Комментарий'
|
2023-07-22 03:18:48 +03:00
|
|
|
|
placeholder='Договоренность об интерпретации неопределяемого понятия
Комментарий к производному понятию'
|
2023-07-16 20:25:55 +03:00
|
|
|
|
rows={4}
|
|
|
|
|
value={convention}
|
2023-07-25 22:29:33 +03:00
|
|
|
|
disabled={!isEnabled}
|
2023-07-20 17:11:03 +03:00
|
|
|
|
spellCheck
|
2023-08-27 16:35:17 +03:00
|
|
|
|
onChange={event => setConvention(event.target.value)}
|
|
|
|
|
onFocus={() => setEditMode(EditMode.TEXT)}
|
2023-07-16 20:25:55 +03:00
|
|
|
|
/>
|
2023-08-27 15:39:49 +03:00
|
|
|
|
<div className='flex justify-center w-full mt-4 mb-2'>
|
2023-07-22 03:18:48 +03:00
|
|
|
|
<SubmitButton
|
|
|
|
|
text='Сохранить изменения'
|
2023-07-25 22:29:33 +03:00
|
|
|
|
disabled={!isModified || !isEnabled}
|
2023-07-22 03:18:48 +03:00
|
|
|
|
icon={<SaveIcon size={6} />}
|
|
|
|
|
/>
|
2023-07-21 18:44:14 +03:00
|
|
|
|
</div>
|
2023-07-16 20:25:55 +03:00
|
|
|
|
</form>
|
2023-09-10 20:17:18 +03:00
|
|
|
|
{(windowSize.width ?? 0) >= SIDELIST_HIDE_THRESHOLD &&
|
2023-08-15 21:22:21 +03:00
|
|
|
|
<div className='self-stretch w-full pb-1 border'>
|
2023-08-13 13:18:50 +03:00
|
|
|
|
<ViewSideConstituents
|
|
|
|
|
expression={expression}
|
|
|
|
|
baseHeight={UNFOLDED_HEIGHT}
|
|
|
|
|
activeID={activeID}
|
|
|
|
|
onOpenEdit={onOpenEdit}
|
|
|
|
|
/>
|
2023-09-10 20:17:18 +03:00
|
|
|
|
</div>}
|
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;
|