mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Implementing templated expressions pt1
This commit is contained in:
parent
90c8303ee7
commit
6e610b5806
|
@ -9,15 +9,17 @@ extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'className' | 'title'>
|
|||
colorClass?: string
|
||||
dense?: boolean
|
||||
noBorder?: boolean
|
||||
noOutline?: boolean
|
||||
}
|
||||
|
||||
function TextInput({
|
||||
id, required, label, dense, tooltip, noBorder,
|
||||
id, required, label, dense, tooltip, noBorder, noOutline,
|
||||
dimensions = 'w-full',
|
||||
colorClass = 'clr-input',
|
||||
...props
|
||||
}: TextInputProps) {
|
||||
const borderClass = noBorder ? '': 'border';
|
||||
const outlineClass = noOutline ? '': 'clr-outline';
|
||||
return (
|
||||
<div className={`flex ${dense ? 'items-center gap-4 ' + dimensions : 'flex-col items-start gap-2'}`}>
|
||||
{label &&
|
||||
|
@ -27,7 +29,7 @@ function TextInput({
|
|||
/>}
|
||||
<input id={id}
|
||||
title={tooltip}
|
||||
className={`px-3 py-2 leading-tight ${borderClass} truncate hover:text-clip clr-outline ${colorClass} ${dense ? 'w-full' : dimensions}`}
|
||||
className={`px-3 py-2 leading-tight truncate hover:text-clip ${outlineClass} ${borderClass} ${colorClass} ${dense ? 'w-full' : dimensions}`}
|
||||
required={required}
|
||||
{...props}
|
||||
/>
|
||||
|
|
|
@ -6,7 +6,7 @@ function HelpConstituenta() {
|
|||
<div>
|
||||
<h1>Подсказки</h1>
|
||||
<p><b className='text-warning'>Изменения сохраняются ПОСЛЕ нажатия на соответствующую кнопку снизу или по центру</b></p>
|
||||
<p><b>Клик на формальное выражение</b> - обратите внимание на кнопки снизу<br/>Горячие клавиши указаны в подсказках при наведении</p>
|
||||
<p><b>Формальное определение</b> - обратите внимание на кнопки снизу<br/>Горячие клавиши указаны в подсказках при наведении</p>
|
||||
<p><b>Поля Термин и Определение</b> - Ctrl + Пробел открывает диалог редактирования отсылок<br/>Перед открытием диалога переместите текстовый курсор на заменяемое слово или ссылку</p>
|
||||
<p><b>Список конституент справа</b> - обратите внимание на настройки фильтрации</p>
|
||||
<p>- слева от ввода текста настраивается набор атрибутов конституенты</p>
|
||||
|
|
|
@ -4,8 +4,8 @@ import { ErrorInfo } from '../components/BackendError';
|
|||
import { matchLibraryItem } from '../models/library';
|
||||
import { ILibraryItem } from '../models/library';
|
||||
import { ILibraryFilter } from '../models/miscelanious';
|
||||
import { IRSFormCreateData, IRSFormData } from '../models/rsform';
|
||||
import { DataCallback, deleteLibraryItem, getLibrary, getTemplates, postCloneLibraryItem, postNewRSForm } from '../utils/backendAPI';
|
||||
import { IRSForm, IRSFormCreateData, IRSFormData, loadRSFormData } from '../models/rsform';
|
||||
import { DataCallback, deleteLibraryItem, getLibrary, getRSFormDetails, getTemplates, postCloneLibraryItem, postNewRSForm } from '../utils/backendAPI';
|
||||
import { useAuth } from './AuthContext';
|
||||
|
||||
interface ILibraryContext {
|
||||
|
@ -17,6 +17,7 @@ interface ILibraryContext {
|
|||
setError: (error: ErrorInfo) => void
|
||||
|
||||
applyFilter: (params: ILibraryFilter) => ILibraryItem[]
|
||||
retrieveTemplate: (templateID: number, callback: (schema: IRSForm) => void) => void
|
||||
createItem: (data: IRSFormCreateData, callback?: DataCallback<ILibraryItem>) => void
|
||||
cloneItem: (target: number, data: IRSFormCreateData, callback: DataCallback<IRSFormData>) => void
|
||||
destroyItem: (target: number, callback?: () => void) => void
|
||||
|
@ -38,13 +39,15 @@ interface LibraryStateProps {
|
|||
}
|
||||
|
||||
export const LibraryState = ({ children }: LibraryStateProps) => {
|
||||
const [ items, setItems ] = useState<ILibraryItem[]>([])
|
||||
const [ templates, setTemplates ] = useState<ILibraryItem[]>([])
|
||||
const [ items, setItems ] = useState<ILibraryItem[]>([]);
|
||||
const [ templates, setTemplates ] = useState<ILibraryItem[]>([]);
|
||||
const [ loading, setLoading ] = useState(false);
|
||||
const [ processing, setProcessing ] = useState(false);
|
||||
const [ error, setError ] = useState<ErrorInfo>(undefined);
|
||||
const { user } = useAuth();
|
||||
|
||||
const [ cachedTemplates, setCachedTemplates ] = useState<IRSForm[]>([]);
|
||||
|
||||
const applyFilter = useCallback(
|
||||
(params: ILibraryFilter) => {
|
||||
let result = items;
|
||||
|
@ -69,6 +72,26 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
return result;
|
||||
}, [items, user]);
|
||||
|
||||
const retrieveTemplate = useCallback(
|
||||
(templateID: number, callback: (schema: IRSForm) => void) => {
|
||||
const cached = cachedTemplates.find(schema => schema.id == templateID);
|
||||
if (cached) {
|
||||
callback(cached);
|
||||
return;
|
||||
}
|
||||
setError(undefined);
|
||||
getRSFormDetails(String(templateID), {
|
||||
showError: true,
|
||||
setLoading: setLoading,
|
||||
onError: error => setError(error),
|
||||
onSuccess: data => {
|
||||
const schema = loadRSFormData(data);
|
||||
setCachedTemplates(prev => ([...prev, schema]));
|
||||
callback(schema);
|
||||
}
|
||||
});
|
||||
}, [cachedTemplates]);
|
||||
|
||||
const reload = useCallback(
|
||||
(callback?: () => void) => {
|
||||
setItems([]);
|
||||
|
@ -150,7 +173,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
return (
|
||||
<LibraryContext.Provider value={{
|
||||
items, templates, loading, processing, error, setError,
|
||||
applyFilter, createItem, cloneItem, destroyItem
|
||||
applyFilter, createItem, cloneItem, destroyItem, retrieveTemplate
|
||||
}}>
|
||||
{ children }
|
||||
</LibraryContext.Provider>
|
||||
|
|
|
@ -77,7 +77,7 @@ function DlgCreateCst({ hideWindow, initial, schema, onCreate }: DlgCreateCstPro
|
|||
spellCheck
|
||||
onChange={event => updateCstData({ term_raw: event.target.value })}
|
||||
/>
|
||||
<RSInput id='expression' label='Формальное выражение'
|
||||
<RSInput id='expression' label='Формальное определение'
|
||||
placeholder='Родоструктурное выражение, задающее формальное определение'
|
||||
editable
|
||||
height='4.8rem'
|
||||
|
|
|
@ -179,7 +179,6 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
|||
</div>);
|
||||
}, [handleSelectGrams, selectedGrams]);
|
||||
|
||||
|
||||
const columnsConstituenta = useMemo(
|
||||
() => [
|
||||
constituentaHelper.accessor('alias', {
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
import { Dispatch } from 'react';
|
||||
|
||||
import SelectSingle from '../../components/Common/SelectSingle';
|
||||
import TextArea from '../../components/Common/TextArea';
|
||||
import TextInput from '../../components/Common/TextInput';
|
||||
import RSInput from '../../components/RSInput';
|
||||
import { CstType, ICstCreateData } from '../../models/rsform';
|
||||
import { labelCstType } from '../../utils/labels';
|
||||
import { SelectorCstType } from '../../utils/selectors';
|
||||
|
||||
interface ConstituentaTabProps {
|
||||
state: ICstCreateData
|
||||
partialUpdate: Dispatch<Partial<ICstCreateData>>
|
||||
}
|
||||
|
||||
function ConstituentaTab({state, partialUpdate}: ConstituentaTabProps) {
|
||||
return (
|
||||
<div className='flex flex-col gap-2'>
|
||||
<div className='flex justify-center w-full gap-6'>
|
||||
<SelectSingle
|
||||
className='my-2 min-w-[15rem] self-center'
|
||||
options={SelectorCstType}
|
||||
placeholder='Выберите тип'
|
||||
value={{ value: state.cst_type, label: labelCstType(state.cst_type) }}
|
||||
onChange={data => partialUpdate({ cst_type: data?.value ?? CstType.TERM})}
|
||||
/>
|
||||
<TextInput id='alias' label='Имя'
|
||||
dense
|
||||
dimensions='w-[7rem]'
|
||||
value={state.alias}
|
||||
onChange={event => partialUpdate({ alias: event.target.value})}
|
||||
/>
|
||||
</div>
|
||||
<TextArea id='term' label='Термин'
|
||||
placeholder='Схемный или предметный термин, обозначающий данное понятие или утверждение'
|
||||
rows={2}
|
||||
value={state.term_raw}
|
||||
spellCheck
|
||||
onChange={event => partialUpdate({ term_raw: event.target.value })}
|
||||
/>
|
||||
<RSInput id='expression' label='Формальное определение'
|
||||
placeholder='Родоструктурное выражение, задающее формальное определение'
|
||||
editable
|
||||
height='4.8rem'
|
||||
value={state.definition_formal}
|
||||
onChange={value => partialUpdate({definition_formal: value})}
|
||||
/>
|
||||
<TextArea id='definition' label='Текстовое определение'
|
||||
placeholder='Лингвистическая интерпретация формального выражения'
|
||||
rows={2}
|
||||
value={state.definition_raw}
|
||||
spellCheck
|
||||
onChange={event => partialUpdate({ definition_raw: event.target.value })}
|
||||
/>
|
||||
<TextArea id='convention' label='Конвенция / Комментарий'
|
||||
placeholder='Договоренность об интерпретации неопределяемого понятия
Комментарий к производному понятию'
|
||||
rows={2}
|
||||
value={state.convention}
|
||||
spellCheck
|
||||
onChange={event => partialUpdate({ convention: event.target.value })}
|
||||
/>
|
||||
</div>);
|
||||
}
|
||||
|
||||
export default ConstituentaTab;
|
222
rsconcept/frontend/src/dialogs/DlgTemplates/TemplateTab.tsx
Normal file
222
rsconcept/frontend/src/dialogs/DlgTemplates/TemplateTab.tsx
Normal file
|
@ -0,0 +1,222 @@
|
|||
import { Dispatch, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import SelectSingle from '../../components/Common/SelectSingle';
|
||||
import TextArea from '../../components/Common/TextArea';
|
||||
import TextInput from '../../components/Common/TextInput';
|
||||
import DataTable, { createColumnHelper,IConditionalStyle } from '../../components/DataTable';
|
||||
import ConstituentaTooltip from '../../components/Help/ConstituentaTooltip';
|
||||
import { MagnifyingGlassIcon } from '../../components/Icons';
|
||||
import RSInput from '../../components/RSInput';
|
||||
import { useLibrary } from '../../context/LibraryContext';
|
||||
import { useConceptTheme } from '../../context/ThemeContext';
|
||||
import { CstMatchMode } from '../../models/miscelanious';
|
||||
import { applyFilterCategory, CATEGORY_CST_TYPE, IConstituenta, IRSForm, matchConstituenta } from '../../models/rsform';
|
||||
import { colorfgCstStatus } from '../../utils/color';
|
||||
import { prefixes } from '../../utils/constants';
|
||||
|
||||
export interface ITemplateState {
|
||||
templateID?: number
|
||||
prototype?: IConstituenta
|
||||
filterCategory?: IConstituenta
|
||||
filterText: string
|
||||
}
|
||||
|
||||
interface TemplateTabProps {
|
||||
state: ITemplateState
|
||||
partialUpdate: Dispatch<Partial<ITemplateState>>
|
||||
}
|
||||
|
||||
const constituentaHelper = createColumnHelper<IConstituenta>();
|
||||
|
||||
function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
||||
const { colors } = useConceptTheme();
|
||||
|
||||
const { templates, retrieveTemplate } = useLibrary();
|
||||
const [ selectedSchema, setSelectedSchema ] = useState<IRSForm | undefined>(undefined);
|
||||
|
||||
const [filteredData, setFilteredData] = useState<IConstituenta[]>([]);
|
||||
|
||||
const prototypeInfo = useMemo(
|
||||
() => {
|
||||
if (!state.prototype) {
|
||||
return '';
|
||||
} else {
|
||||
return `${state.prototype?.term_raw}${state.prototype?.definition_raw ? ` — ${state.prototype?.definition_raw}` : ''}`;
|
||||
}
|
||||
}, [state.prototype]);
|
||||
|
||||
const templateSelector = useMemo(
|
||||
() => templates.map(
|
||||
(template) => ({
|
||||
value: template.id,
|
||||
label: template.title
|
||||
})
|
||||
), [templates]);
|
||||
|
||||
const categorySelector = useMemo(
|
||||
(): {value: number, label: string}[] => {
|
||||
if (!selectedSchema) {
|
||||
return [];
|
||||
}
|
||||
return selectedSchema.items
|
||||
.filter(cst => cst.cst_type === CATEGORY_CST_TYPE)
|
||||
.map(cst => ({
|
||||
value: cst.id,
|
||||
label: cst.term_raw
|
||||
}));
|
||||
}, [selectedSchema]);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (templates.length > 0 && !state.templateID) {
|
||||
partialUpdate({ templateID: templates[0].id });
|
||||
}
|
||||
}, [templates, state.templateID, partialUpdate]);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (!state.templateID) {
|
||||
setSelectedSchema(undefined);
|
||||
} else {
|
||||
retrieveTemplate(state.templateID, setSelectedSchema);
|
||||
}
|
||||
}, [state.templateID, retrieveTemplate]);
|
||||
|
||||
// Filter constituents
|
||||
useEffect(
|
||||
() => {
|
||||
if (!selectedSchema) {
|
||||
return;
|
||||
}
|
||||
let data = selectedSchema.items;
|
||||
if (state.filterCategory) {
|
||||
data = applyFilterCategory(state.filterCategory, selectedSchema);
|
||||
}
|
||||
if (state.filterText) {
|
||||
data = data.filter(cst => matchConstituenta(state.filterText, cst, CstMatchMode.TERM));
|
||||
}
|
||||
setFilteredData(data);
|
||||
}, [state.filterText, state.filterCategory, selectedSchema]);
|
||||
|
||||
function handleSelectTemplate(cst: IConstituenta) {
|
||||
partialUpdate( { prototype: cst } );
|
||||
}
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
constituentaHelper.accessor('alias', {
|
||||
id: 'alias',
|
||||
header: 'Имя',
|
||||
size: 65,
|
||||
minSize: 65,
|
||||
cell: props => {
|
||||
const cst = props.row.original;
|
||||
return (<>
|
||||
<div
|
||||
id={`${prefixes.cst_template_ist}${cst.alias}`}
|
||||
className='min-w-[3.1rem] max-w-[3.1rem] px-1 text-center rounded-md whitespace-nowrap'
|
||||
style={{
|
||||
borderWidth: '1px',
|
||||
borderColor: colorfgCstStatus(cst.status, colors),
|
||||
color: colorfgCstStatus(cst.status, colors),
|
||||
fontWeight: 600
|
||||
}}
|
||||
>
|
||||
{cst.alias}
|
||||
</div>
|
||||
<ConstituentaTooltip data={cst} anchor={`#${prefixes.cst_template_ist}${cst.alias}`} />
|
||||
</>);
|
||||
}
|
||||
}),
|
||||
constituentaHelper.accessor('term_resolved', {
|
||||
id: 'term',
|
||||
header: 'Термин',
|
||||
size: 600,
|
||||
minSize: 350,
|
||||
maxSize: 600
|
||||
})
|
||||
], [colors]);
|
||||
|
||||
const conditionalRowStyles = useMemo(
|
||||
(): IConditionalStyle<IConstituenta>[] => [
|
||||
{
|
||||
when: (cst: IConstituenta) => cst.id === state.prototype?.id,
|
||||
style: {
|
||||
backgroundColor: colors.bgSelected
|
||||
},
|
||||
}
|
||||
], [state.prototype, colors]);
|
||||
|
||||
return (
|
||||
<div className='flex flex-col gap-2 mt-2'>
|
||||
<div className='flex justify-between gap-4'>
|
||||
<SelectSingle
|
||||
className='w-full'
|
||||
options={categorySelector}
|
||||
placeholder='Выберите категорию'
|
||||
value={state.filterCategory && selectedSchema ? {
|
||||
value: state.filterCategory.id,
|
||||
label: state.filterCategory.term_raw
|
||||
} : null}
|
||||
onChange={data => partialUpdate({filterCategory: selectedSchema?.items.find(cst => cst.id === data?.value) })}
|
||||
isClearable
|
||||
/>
|
||||
<SelectSingle
|
||||
className='min-w-[15rem]'
|
||||
options={templateSelector}
|
||||
placeholder='Выберите источник'
|
||||
value={state.templateID ? { value: state.templateID, label: templates.find(item => item.id == state.templateID)!.title }: null}
|
||||
onChange={data => partialUpdate({templateID: (data ? data.value : undefined)})}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className='relative'>
|
||||
<div className='absolute inset-y-0 flex items-center pl-3 pointer-events-none text-controls'>
|
||||
<MagnifyingGlassIcon />
|
||||
</div>
|
||||
<TextInput
|
||||
dimensions='w-full pl-10'
|
||||
placeholder='Поиск'
|
||||
noOutline
|
||||
value={state.filterText}
|
||||
onChange={event => partialUpdate({ filterText: event.target.value} )}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='border min-h-[15.5rem] max-h-[15.5rem] text-sm overflow-y-auto select-none'>
|
||||
<DataTable
|
||||
data={filteredData}
|
||||
columns={columns}
|
||||
conditionalRowStyles={conditionalRowStyles}
|
||||
|
||||
headPosition='0'
|
||||
dense
|
||||
|
||||
noDataComponent={
|
||||
<span className='flex flex-col justify-center p-2 text-center min-h-[5rem]'>
|
||||
<p>Список конституент пуст</p>
|
||||
<p>Измените параметры фильтра</p>
|
||||
</span>
|
||||
}
|
||||
|
||||
onRowClicked={handleSelectTemplate}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<TextArea id='term'
|
||||
rows={2}
|
||||
disabled
|
||||
placeholder='Шаблон конституенты не выбран'
|
||||
value={prototypeInfo}
|
||||
spellCheck
|
||||
/>
|
||||
<RSInput id='expression'
|
||||
height='4.8rem'
|
||||
placeholder='Выберите шаблон из списка'
|
||||
editable={false}
|
||||
value={state.prototype?.definition_formal}
|
||||
/>
|
||||
</div>);
|
||||
}
|
||||
|
||||
export default TemplateTab;
|
|
@ -2,18 +2,14 @@ import { useEffect, useLayoutEffect, useState } from 'react';
|
|||
|
||||
import ConceptTooltip from '../../components/Common/ConceptTooltip';
|
||||
import Modal, { ModalProps } from '../../components/Common/Modal';
|
||||
import SelectSingle from '../../components/Common/SelectSingle';
|
||||
import SwitchButton from '../../components/Common/SwitchButton';
|
||||
import TextArea from '../../components/Common/TextArea';
|
||||
import TextInput from '../../components/Common/TextInput';
|
||||
import HelpRSTemplates from '../../components/Help/HelpRSTemplates';
|
||||
import { HelpIcon } from '../../components/Icons';
|
||||
import RSInput from '../../components/RSInput';
|
||||
import usePartialUpdate from '../../hooks/usePartialUpdate';
|
||||
import { CstType,ICstCreateData, IRSForm } from '../../models/rsform';
|
||||
import { labelCstType } from '../../utils/labels';
|
||||
import { CstType, ICstCreateData, IRSForm } from '../../models/rsform';
|
||||
import { createAliasFor, validateCstAlias } from '../../utils/misc';
|
||||
import { SelectorCstType } from '../../utils/selectors';
|
||||
import ConstituentaTab from './ConstituentaTab';
|
||||
import TemplateTab, { ITemplateState } from './TemplateTab';
|
||||
|
||||
interface DlgTemplatesProps
|
||||
extends Pick<ModalProps, 'hideWindow'> {
|
||||
|
@ -23,7 +19,7 @@ extends Pick<ModalProps, 'hideWindow'> {
|
|||
|
||||
function DlgTemplates({ hideWindow, schema, onCreate }: DlgTemplatesProps) {
|
||||
const [validated, setValidated] = useState(false);
|
||||
const [cstData, updateCstData] = usePartialUpdate({
|
||||
const [cstData, updateCstData] = usePartialUpdate<ICstCreateData>({
|
||||
cst_type: CstType.TERM,
|
||||
insert_after: null,
|
||||
alias: '',
|
||||
|
@ -33,6 +29,9 @@ function DlgTemplates({ hideWindow, schema, onCreate }: DlgTemplatesProps) {
|
|||
term_raw: '',
|
||||
term_forms: []
|
||||
});
|
||||
const [ templateData, updateTemplateData ] = usePartialUpdate<ITemplateState>({
|
||||
filterText: ''
|
||||
});
|
||||
|
||||
const [ showAttributes, setShowAttributes ] = useState(false);
|
||||
|
||||
|
@ -48,6 +47,24 @@ function DlgTemplates({ hideWindow, schema, onCreate }: DlgTemplatesProps) {
|
|||
setValidated(validateCstAlias(cstData.alias, cstData.cst_type, schema));
|
||||
}, [cstData.alias, cstData.cst_type, schema]);
|
||||
|
||||
useLayoutEffect(
|
||||
() => {
|
||||
if (!templateData.prototype) {
|
||||
updateCstData({
|
||||
definition_raw: '',
|
||||
definition_formal: '',
|
||||
term_raw: ''
|
||||
});
|
||||
} else {
|
||||
updateCstData({
|
||||
cst_type: templateData.prototype.cst_type,
|
||||
definition_raw: templateData.prototype.definition_raw,
|
||||
definition_formal: templateData.prototype.definition_formal,
|
||||
term_raw: templateData.prototype.term_raw
|
||||
});
|
||||
}
|
||||
}, [templateData.prototype, updateCstData]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title='Создание конституенты из шаблона'
|
||||
|
@ -87,56 +104,16 @@ function DlgTemplates({ hideWindow, schema, onCreate }: DlgTemplatesProps) {
|
|||
</div>
|
||||
|
||||
{ !showAttributes &&
|
||||
<div>
|
||||
Выбор шаблона и параметров
|
||||
</div>}
|
||||
<TemplateTab
|
||||
state={templateData}
|
||||
partialUpdate={updateTemplateData}
|
||||
/>}
|
||||
|
||||
{ showAttributes &&
|
||||
<div>
|
||||
<div className='flex justify-center w-full gap-6'>
|
||||
<SelectSingle
|
||||
className='my-2 min-w-[15rem] self-center'
|
||||
options={SelectorCstType}
|
||||
placeholder='Выберите тип'
|
||||
value={{ value: cstData.cst_type, label: labelCstType(cstData.cst_type) }}
|
||||
onChange={data => updateCstData({ cst_type: data?.value ?? CstType.TERM})}
|
||||
/>
|
||||
<TextInput id='alias' label='Имя'
|
||||
dense
|
||||
dimensions='w-[7rem]'
|
||||
value={cstData.alias}
|
||||
onChange={event => updateCstData({ alias: event.target.value})}
|
||||
/>
|
||||
</div>
|
||||
<TextArea id='term' label='Термин'
|
||||
placeholder='Схемный или предметный термин, обозначающий данное понятие или утверждение'
|
||||
rows={2}
|
||||
value={cstData.term_raw}
|
||||
spellCheck
|
||||
onChange={event => updateCstData({ term_raw: event.target.value })}
|
||||
/>
|
||||
<RSInput id='expression' label='Формальное выражение'
|
||||
placeholder='Родоструктурное выражение, задающее формальное определение'
|
||||
editable
|
||||
height='4.8rem'
|
||||
value={cstData.definition_formal}
|
||||
onChange={value => updateCstData({definition_formal: value})}
|
||||
/>
|
||||
<TextArea id='definition' label='Текстовое определение'
|
||||
placeholder='Лингвистическая интерпретация формального выражения'
|
||||
rows={2}
|
||||
value={cstData.definition_raw}
|
||||
spellCheck
|
||||
onChange={event => updateCstData({ definition_raw: event.target.value })}
|
||||
/>
|
||||
<TextArea id='convention' label='Конвенция / Комментарий'
|
||||
placeholder='Договоренность об интерпретации неопределяемого понятия
Комментарий к производному понятию'
|
||||
rows={2}
|
||||
value={cstData.convention}
|
||||
spellCheck
|
||||
onChange={event => updateCstData({ convention: event.target.value })}
|
||||
/>
|
||||
</div>}
|
||||
<ConstituentaTab
|
||||
state={cstData}
|
||||
partialUpdate={updateCstData}
|
||||
/>}
|
||||
</div>
|
||||
</Modal>);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import { type ErrorInfo } from '../components/BackendError';
|
||||
import { IRSForm, IRSFormData,loadRSFormData } from '../models/rsform'
|
||||
import { IRSForm, IRSFormData, loadRSFormData } from '../models/rsform'
|
||||
import { getRSFormDetails } from '../utils/backendAPI';
|
||||
|
||||
export function useRSFormDetails({ target }: { target?: string }) {
|
||||
|
|
|
@ -16,6 +16,9 @@ export enum CstType {
|
|||
THEOREM = 'theorem'
|
||||
}
|
||||
|
||||
// CstType constant for category dividers in TemplateSchemas. TODO: create separate sctructure for templates
|
||||
export const CATEGORY_CST_TYPE = CstType.THEOREM;
|
||||
|
||||
export enum CstClass {
|
||||
BASIC = 'basic',
|
||||
DERIVED = 'derived',
|
||||
|
@ -307,3 +310,18 @@ export function createMockConstituenta(schema: number, id: number, alias: string
|
|||
};
|
||||
}
|
||||
|
||||
export function applyFilterCategory(target: IConstituenta, schema: IRSFormData): IConstituenta[] {
|
||||
const nextCategory = schema.items.find(
|
||||
cst => (
|
||||
cst.order > target.order &&
|
||||
cst.cst_type === CATEGORY_CST_TYPE
|
||||
)
|
||||
);
|
||||
return schema.items.filter(
|
||||
cst => (
|
||||
cst.order > target.order &&
|
||||
(!nextCategory || cst.order <= nextCategory.order)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -229,7 +229,7 @@ function EditorConstituenta({
|
|||
colorClass='clr-app'
|
||||
disabled
|
||||
/>
|
||||
<EditorRSExpression id='expression' label='Формальное выражение'
|
||||
<EditorRSExpression id='expression' label='Формальное определение'
|
||||
activeCst={activeCst}
|
||||
placeholder='Родоструктурное выражение, задающее формальное определение'
|
||||
value={expression}
|
||||
|
|
|
@ -118,7 +118,7 @@ function EditorRSExpression({
|
|||
return (
|
||||
<div className='flex flex-col items-start w-full'>
|
||||
<div className='relative w-full'>
|
||||
<div className='absolute top-[-0.2rem] left-[10.5rem]'>
|
||||
<div className='absolute top-[-0.2rem] left-[11rem]'>
|
||||
<MiniButton
|
||||
tooltip='Дерево разбора выражения'
|
||||
noHover
|
||||
|
@ -141,7 +141,7 @@ function EditorRSExpression({
|
|||
<div className='w-full max-h-[5rem] min-h-[5rem] flex'>
|
||||
<div className='flex flex-col'>
|
||||
<Button
|
||||
tooltip='Проверить формальное выражение'
|
||||
tooltip='Проверить формальное определение'
|
||||
text='Проверить'
|
||||
dimensions='w-fit h-[3rem] z-pop'
|
||||
colorClass='clr-btn-default'
|
||||
|
|
|
@ -34,6 +34,7 @@ export const globalIDs = {
|
|||
|
||||
export const prefixes = {
|
||||
cst_list: 'cst-list-',
|
||||
cst_template_ist: 'cst-template-list-',
|
||||
cst_wordform_list: 'cst-wordform-list-',
|
||||
cst_status_list: 'cst-status-list-',
|
||||
cst_match_mode_list: 'cst-match-mode-list-',
|
||||
|
|
Loading…
Reference in New Issue
Block a user