mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Refactoring: use cached maps to access constituents
This commit is contained in:
parent
5c52e2a6f8
commit
26fe49b352
|
@ -86,9 +86,9 @@ const RSInput = forwardRef<ReactCodeMirrorRef, RSInputProps>(
|
||||||
EditorView.lineWrapping,
|
EditorView.lineWrapping,
|
||||||
RSLanguage,
|
RSLanguage,
|
||||||
ccBracketMatching(darkMode),
|
ccBracketMatching(darkMode),
|
||||||
...(noTooltip ? [] : [rsHoverTooltip(schema?.items || [])])
|
...(noTooltip || !schema ? [] : [rsHoverTooltip(schema)])
|
||||||
],
|
],
|
||||||
[darkMode, schema?.items, noTooltip]
|
[darkMode, schema, noTooltip]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleInput = useCallback(
|
const handleInput = useCallback(
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Extension } from '@codemirror/state';
|
||||||
import { hoverTooltip } from '@codemirror/view';
|
import { hoverTooltip } from '@codemirror/view';
|
||||||
import { EditorState } from '@uiw/react-codemirror';
|
import { EditorState } from '@uiw/react-codemirror';
|
||||||
|
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IRSForm } from '@/models/rsform';
|
||||||
import { findEnvelopingNodes } from '@/utils/codemirror';
|
import { findEnvelopingNodes } from '@/utils/codemirror';
|
||||||
import { domTooltipConstituenta } from '@/utils/codemirror';
|
import { domTooltipConstituenta } from '@/utils/codemirror';
|
||||||
|
|
||||||
|
@ -25,13 +25,13 @@ function findAliasAt(pos: number, state: EditorState) {
|
||||||
return { alias, start, end };
|
return { alias, start, end };
|
||||||
}
|
}
|
||||||
|
|
||||||
const globalsHoverTooltip = (items: IConstituenta[]) => {
|
const globalsHoverTooltip = (schema: IRSForm) => {
|
||||||
return hoverTooltip((view, pos) => {
|
return hoverTooltip((view, pos) => {
|
||||||
const { alias, start, end } = findAliasAt(pos, view.state);
|
const { alias, start, end } = findAliasAt(pos, view.state);
|
||||||
if (!alias) {
|
if (!alias) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const cst = items.find(cst => cst.alias === alias);
|
const cst = schema.cstByAlias.get(alias);
|
||||||
return {
|
return {
|
||||||
pos: start,
|
pos: start,
|
||||||
end: end,
|
end: end,
|
||||||
|
@ -41,6 +41,6 @@ const globalsHoverTooltip = (items: IConstituenta[]) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export function rsHoverTooltip(items: IConstituenta[]): Extension {
|
export function rsHoverTooltip(schema: IRSForm): Extension {
|
||||||
return [globalsHoverTooltip(items)];
|
return [globalsHoverTooltip(schema)];
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,9 @@ import { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import Label from '@/components/ui/Label';
|
import Label from '@/components/ui/Label';
|
||||||
import { useConceptOptions } from '@/context/OptionsContext';
|
import { useConceptOptions } from '@/context/OptionsContext';
|
||||||
import { useRSForm } from '@/context/RSFormContext';
|
|
||||||
import DlgEditReference from '@/dialogs/DlgEditReference';
|
import DlgEditReference from '@/dialogs/DlgEditReference';
|
||||||
import { ReferenceType } from '@/models/language';
|
import { ReferenceType } from '@/models/language';
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IRSForm } from '@/models/rsform';
|
||||||
import { CodeMirrorWrapper } from '@/utils/codemirror';
|
import { CodeMirrorWrapper } from '@/utils/codemirror';
|
||||||
|
|
||||||
import { NaturalLanguage, ReferenceTokens } from './parse';
|
import { NaturalLanguage, ReferenceTokens } from './parse';
|
||||||
|
@ -53,7 +52,7 @@ interface RefsInputInputProps
|
||||||
extends Pick<ReactCodeMirrorProps, 'id' | 'height' | 'value' | 'className' | 'onFocus' | 'onBlur' | 'placeholder'> {
|
extends Pick<ReactCodeMirrorProps, 'id' | 'height' | 'value' | 'className' | 'onFocus' | 'onBlur' | 'placeholder'> {
|
||||||
label?: string;
|
label?: string;
|
||||||
onChange?: (newValue: string) => void;
|
onChange?: (newValue: string) => void;
|
||||||
items?: IConstituenta[];
|
schema?: IRSForm;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
|
||||||
initialValue?: string;
|
initialValue?: string;
|
||||||
|
@ -62,9 +61,8 @@ interface RefsInputInputProps
|
||||||
}
|
}
|
||||||
|
|
||||||
const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
||||||
({ id, label, disabled, items, initialValue, value, resolved, onFocus, onBlur, onChange, ...restProps }, ref) => {
|
({ id, label, disabled, schema, initialValue, value, resolved, onFocus, onBlur, onChange, ...restProps }, ref) => {
|
||||||
const { darkMode, colors } = useConceptOptions();
|
const { darkMode, colors } = useConceptOptions();
|
||||||
const { schema } = useRSForm();
|
|
||||||
|
|
||||||
const [isFocused, setIsFocused] = useState(false);
|
const [isFocused, setIsFocused] = useState(false);
|
||||||
|
|
||||||
|
@ -104,9 +102,9 @@ const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
||||||
EditorView.lineWrapping,
|
EditorView.lineWrapping,
|
||||||
EditorView.contentAttributes.of({ spellcheck: 'true' }),
|
EditorView.contentAttributes.of({ spellcheck: 'true' }),
|
||||||
NaturalLanguage,
|
NaturalLanguage,
|
||||||
refsHoverTooltip(schema?.items || [], colors)
|
...(schema ? [refsHoverTooltip(schema, colors)] : [])
|
||||||
],
|
],
|
||||||
[schema?.items, colors]
|
[schema, colors]
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleChange(newValue: string) {
|
function handleChange(newValue: string) {
|
||||||
|
@ -170,10 +168,10 @@ const RefsInput = forwardRef<ReactCodeMirrorRef, RefsInputInputProps>(
|
||||||
return (
|
return (
|
||||||
<div className={clsx('flex flex-col gap-2', cursor)}>
|
<div className={clsx('flex flex-col gap-2', cursor)}>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{showEditor ? (
|
{showEditor && schema ? (
|
||||||
<DlgEditReference
|
<DlgEditReference
|
||||||
hideWindow={() => setShowEditor(false)}
|
hideWindow={() => setShowEditor(false)}
|
||||||
items={items ?? []}
|
schema={schema}
|
||||||
initial={{
|
initial={{
|
||||||
type: currentType,
|
type: currentType,
|
||||||
refRaw: refText,
|
refRaw: refText,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Extension } from '@codemirror/state';
|
||||||
import { hoverTooltip } from '@codemirror/view';
|
import { hoverTooltip } from '@codemirror/view';
|
||||||
|
|
||||||
import { parseEntityReference, parseSyntacticReference } from '@/models/languageAPI';
|
import { parseEntityReference, parseSyntacticReference } from '@/models/languageAPI';
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IRSForm } from '@/models/rsform';
|
||||||
import { IColorTheme } from '@/styling/color';
|
import { IColorTheme } from '@/styling/color';
|
||||||
import {
|
import {
|
||||||
domTooltipEntityReference,
|
domTooltipEntityReference,
|
||||||
|
@ -15,7 +15,7 @@ import {
|
||||||
import { ReferenceTokens } from './parse';
|
import { ReferenceTokens } from './parse';
|
||||||
import { RefEntity, RefSyntactic } from './parse/parser.terms';
|
import { RefEntity, RefSyntactic } from './parse/parser.terms';
|
||||||
|
|
||||||
export const globalsHoverTooltip = (items: IConstituenta[], colors: IColorTheme) => {
|
export const globalsHoverTooltip = (schema: IRSForm, colors: IColorTheme) => {
|
||||||
return hoverTooltip((view, pos) => {
|
return hoverTooltip((view, pos) => {
|
||||||
const nodes = findEnvelopingNodes(pos, pos, syntaxTree(view.state), ReferenceTokens);
|
const nodes = findEnvelopingNodes(pos, pos, syntaxTree(view.state), ReferenceTokens);
|
||||||
if (nodes.length !== 1) {
|
if (nodes.length !== 1) {
|
||||||
|
@ -26,7 +26,7 @@ export const globalsHoverTooltip = (items: IConstituenta[], colors: IColorTheme)
|
||||||
const text = view.state.doc.sliceString(start, end);
|
const text = view.state.doc.sliceString(start, end);
|
||||||
if (nodes[0].type.id === RefEntity) {
|
if (nodes[0].type.id === RefEntity) {
|
||||||
const ref = parseEntityReference(text);
|
const ref = parseEntityReference(text);
|
||||||
const cst = items.find(cst => cst.alias === ref.entity);
|
const cst = schema.cstByAlias.get(ref.entity);
|
||||||
return {
|
return {
|
||||||
pos: start,
|
pos: start,
|
||||||
end: end,
|
end: end,
|
||||||
|
@ -61,6 +61,6 @@ export const globalsHoverTooltip = (items: IConstituenta[], colors: IColorTheme)
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export function refsHoverTooltip(items: IConstituenta[], colors: IColorTheme): Extension {
|
export function refsHoverTooltip(schema: IRSForm, colors: IColorTheme): Extension {
|
||||||
return [globalsHoverTooltip(items, colors)];
|
return [globalsHoverTooltip(schema, colors)];
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,16 +36,16 @@ function InfoConstituenta({ data, className, ...restProps }: InfoConstituentaPro
|
||||||
{data.definition_resolved}
|
{data.definition_resolved}
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
{data.derived_from_alias ? (
|
{data.parent_alias ? (
|
||||||
<p>
|
<p>
|
||||||
<b>Основание: </b>
|
<b>Основание: </b>
|
||||||
{data.derived_from_alias}
|
{data.parent_alias}
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
{data.derived_children_alias.length > 0 ? (
|
{data.children_alias.length > 0 ? (
|
||||||
<p>
|
<p>
|
||||||
<b>Порождает: </b>
|
<b>Порождает: </b>
|
||||||
{data.derived_children_alias.join(', ')}
|
{data.children_alias.join(', ')}
|
||||||
</p>
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
{data.convention ? (
|
{data.convention ? (
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { ILibraryItem } from '@/models/library';
|
||||||
import { matchLibraryItem } from '@/models/libraryAPI';
|
import { matchLibraryItem } from '@/models/libraryAPI';
|
||||||
import { ILibraryFilter } from '@/models/miscellaneous';
|
import { ILibraryFilter } from '@/models/miscellaneous';
|
||||||
import { IRSForm, IRSFormCloneData, IRSFormCreateData, IRSFormData } from '@/models/rsform';
|
import { IRSForm, IRSFormCloneData, IRSFormCreateData, IRSFormData } from '@/models/rsform';
|
||||||
import { loadRSFormData } from '@/models/rsformAPI';
|
import { RSFormLoader } from '@/models/RSFormLoader';
|
||||||
import {
|
import {
|
||||||
DataCallback,
|
DataCallback,
|
||||||
deleteLibraryItem,
|
deleteLibraryItem,
|
||||||
|
@ -100,7 +100,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
||||||
setLoading: setProcessing,
|
setLoading: setProcessing,
|
||||||
onError: setError,
|
onError: setError,
|
||||||
onSuccess: data => {
|
onSuccess: data => {
|
||||||
const schema = loadRSFormData(data);
|
const schema = new RSFormLoader(data).produceRSForm();
|
||||||
setCachedTemplates(prev => [...prev, schema]);
|
setCachedTemplates(prev => [...prev, schema]);
|
||||||
callback(schema);
|
callback(schema);
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ function TemplateTab({ state, partialUpdate }: TemplateTabProps) {
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
onChange={data => partialUpdate({ filterCategory: category?.items.find(cst => cst.id === data?.value) })}
|
onChange={data => partialUpdate({ filterCategory: data ? category?.cstByID.get(data?.value) : undefined })}
|
||||||
isClearable
|
isClearable
|
||||||
/>
|
/>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IRSForm } from '@/models/rsform';
|
||||||
import { labelConstituenta } from '@/utils/labels';
|
import { labelConstituenta } from '@/utils/labels';
|
||||||
|
|
||||||
interface ConstituentsListProps {
|
interface ConstituentsListProps {
|
||||||
list: number[];
|
list: number[];
|
||||||
items: IConstituenta[];
|
schema: IRSForm;
|
||||||
prefix: string;
|
prefix: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ConstituentsList({ list, items, title, prefix }: ConstituentsListProps) {
|
function ConstituentsList({ list, schema, title, prefix }: ConstituentsListProps) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{title ? (
|
{title ? (
|
||||||
|
@ -20,7 +20,7 @@ function ConstituentsList({ list, items, title, prefix }: ConstituentsListProps)
|
||||||
) : null}
|
) : null}
|
||||||
<div className={clsx('h-[9rem]', 'px-3', 'overflow-y-auto', 'border', 'whitespace-nowrap')}>
|
<div className={clsx('h-[9rem]', 'px-3', 'overflow-y-auto', 'border', 'whitespace-nowrap')}>
|
||||||
{list.map(id => {
|
{list.map(id => {
|
||||||
const cst = items.find(cst => cst.id === id);
|
const cst = schema.cstByID.get(id);
|
||||||
return cst ? <p key={`${prefix}${cst.id}`}>{labelConstituenta(cst)}</p> : null;
|
return cst ? <p key={`${prefix}${cst.id}`}>{labelConstituenta(cst)}</p> : null;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -38,16 +38,11 @@ function DlgDeleteCst({ hideWindow, selected, schema, onDelete }: DlgDeleteCstPr
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className={clsx('cc-column', 'max-w-[60vw] min-w-[30rem]', 'px-6')}
|
className={clsx('cc-column', 'max-w-[60vw] min-w-[30rem]', 'px-6')}
|
||||||
>
|
>
|
||||||
<ConstituentsList
|
<ConstituentsList title='Выбраны к удалению' list={selected} schema={schema} prefix={prefixes.cst_delete_list} />
|
||||||
title='Выбраны к удалению'
|
|
||||||
list={selected}
|
|
||||||
items={schema.items}
|
|
||||||
prefix={prefixes.cst_delete_list}
|
|
||||||
/>
|
|
||||||
<ConstituentsList
|
<ConstituentsList
|
||||||
title='Зависимые конституенты'
|
title='Зависимые конституенты'
|
||||||
list={expansion}
|
list={expansion}
|
||||||
items={schema.items}
|
schema={schema}
|
||||||
prefix={prefixes.cst_dependant_list}
|
prefix={prefixes.cst_dependant_list}
|
||||||
/>
|
/>
|
||||||
<Checkbox label='Удалить зависимые конституенты' value={expandOut} setValue={value => setExpandOut(value)} />
|
<Checkbox label='Удалить зависимые конституенты' value={expandOut} setValue={value => setExpandOut(value)} />
|
||||||
|
|
|
@ -10,7 +10,7 @@ import Overlay from '@/components/ui/Overlay';
|
||||||
import TabLabel from '@/components/ui/TabLabel';
|
import TabLabel from '@/components/ui/TabLabel';
|
||||||
import { ReferenceType } from '@/models/language';
|
import { ReferenceType } from '@/models/language';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IRSForm } from '@/models/rsform';
|
||||||
import { labelReferenceType } from '@/utils/labels';
|
import { labelReferenceType } from '@/utils/labels';
|
||||||
|
|
||||||
import EntityTab from './EntityTab';
|
import EntityTab from './EntityTab';
|
||||||
|
@ -26,7 +26,7 @@ export interface IReferenceInputState {
|
||||||
|
|
||||||
interface DlgEditReferenceProps {
|
interface DlgEditReferenceProps {
|
||||||
hideWindow: () => void;
|
hideWindow: () => void;
|
||||||
items: IConstituenta[];
|
schema: IRSForm;
|
||||||
initial: IReferenceInputState;
|
initial: IReferenceInputState;
|
||||||
onSave: (newRef: string) => void;
|
onSave: (newRef: string) => void;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ export enum TabID {
|
||||||
SYNTACTIC = 1
|
SYNTACTIC = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferenceProps) {
|
function DlgEditReference({ hideWindow, schema, initial, onSave }: DlgEditReferenceProps) {
|
||||||
const [activeTab, setActiveTab] = useState(initial.type === ReferenceType.ENTITY ? TabID.ENTITY : TabID.SYNTACTIC);
|
const [activeTab, setActiveTab] = useState(initial.type === ReferenceType.ENTITY ? TabID.ENTITY : TabID.SYNTACTIC);
|
||||||
|
|
||||||
const [reference, setReference] = useState('');
|
const [reference, setReference] = useState('');
|
||||||
|
@ -77,7 +77,7 @@ function DlgEditReference({ hideWindow, items, initial, onSave }: DlgEditReferen
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<EntityTab initial={initial} items={items} setReference={setReference} setIsValid={setIsValid} />
|
<EntityTab initial={initial} schema={schema} setReference={setReference} setIsValid={setIsValid} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
|
|
|
@ -10,7 +10,7 @@ import TextInput from '@/components/ui/TextInput';
|
||||||
import { ReferenceType } from '@/models/language';
|
import { ReferenceType } from '@/models/language';
|
||||||
import { parseEntityReference, parseGrammemes } from '@/models/languageAPI';
|
import { parseEntityReference, parseGrammemes } from '@/models/languageAPI';
|
||||||
import { CstMatchMode } from '@/models/miscellaneous';
|
import { CstMatchMode } from '@/models/miscellaneous';
|
||||||
import { IConstituenta } from '@/models/rsform';
|
import { IConstituenta, IRSForm } from '@/models/rsform';
|
||||||
import { matchConstituenta } from '@/models/rsformAPI';
|
import { matchConstituenta } from '@/models/rsformAPI';
|
||||||
import { prefixes } from '@/utils/constants';
|
import { prefixes } from '@/utils/constants';
|
||||||
import { IGrammemeOption, SelectorGrammemes } from '@/utils/selectors';
|
import { IGrammemeOption, SelectorGrammemes } from '@/utils/selectors';
|
||||||
|
@ -20,12 +20,12 @@ import SelectWordForm from './SelectWordForm';
|
||||||
|
|
||||||
interface EntityTabProps {
|
interface EntityTabProps {
|
||||||
initial: IReferenceInputState;
|
initial: IReferenceInputState;
|
||||||
items: IConstituenta[];
|
schema: IRSForm;
|
||||||
setIsValid: React.Dispatch<React.SetStateAction<boolean>>;
|
setIsValid: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
setReference: React.Dispatch<React.SetStateAction<string>>;
|
setReference: React.Dispatch<React.SetStateAction<string>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function EntityTab({ initial, items, setIsValid, setReference }: EntityTabProps) {
|
function EntityTab({ initial, schema, setIsValid, setReference }: EntityTabProps) {
|
||||||
const [selectedCst, setSelectedCst] = useState<IConstituenta | undefined>(undefined);
|
const [selectedCst, setSelectedCst] = useState<IConstituenta | undefined>(undefined);
|
||||||
const [alias, setAlias] = useState('');
|
const [alias, setAlias] = useState('');
|
||||||
const [term, setTerm] = useState('');
|
const [term, setTerm] = useState('');
|
||||||
|
@ -39,7 +39,7 @@ function EntityTab({ initial, items, setIsValid, setReference }: EntityTabProps)
|
||||||
const grams = parseGrammemes(ref.form);
|
const grams = parseGrammemes(ref.form);
|
||||||
setSelectedGrams(SelectorGrammemes.filter(data => grams.includes(data.value)));
|
setSelectedGrams(SelectorGrammemes.filter(data => grams.includes(data.value)));
|
||||||
}
|
}
|
||||||
}, [initial, items]);
|
}, [initial, schema.items]);
|
||||||
|
|
||||||
// Produce result
|
// Produce result
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -49,9 +49,9 @@ function EntityTab({ initial, items, setIsValid, setReference }: EntityTabProps)
|
||||||
|
|
||||||
// Update term when alias changes
|
// Update term when alias changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const cst = items.find(item => item.alias === alias);
|
const cst = schema.cstByAlias.get(alias);
|
||||||
setTerm(cst?.term_resolved ?? '');
|
setTerm(cst?.term_resolved ?? '');
|
||||||
}, [alias, term, items]);
|
}, [alias, term, schema]);
|
||||||
|
|
||||||
function handleSelectConstituenta(cst: IConstituenta) {
|
function handleSelectConstituenta(cst: IConstituenta) {
|
||||||
setAlias(cst.alias);
|
setAlias(cst.alias);
|
||||||
|
@ -64,7 +64,7 @@ function EntityTab({ initial, items, setIsValid, setReference }: EntityTabProps)
|
||||||
id='dlg_reference_entity_picker'
|
id='dlg_reference_entity_picker'
|
||||||
initialFilter={initial.text}
|
initialFilter={initial.text}
|
||||||
value={selectedCst}
|
value={selectedCst}
|
||||||
data={items}
|
data={schema.items}
|
||||||
onSelectValue={handleSelectConstituenta}
|
onSelectValue={handleSelectConstituenta}
|
||||||
prefixID={prefixes.cst_modal_list}
|
prefixID={prefixes.cst_modal_list}
|
||||||
describeFunc={cst => cst.term_resolved}
|
describeFunc={cst => cst.term_resolved}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { type ErrorData } from '@/components/info/InfoError';
|
import { type ErrorData } from '@/components/info/InfoError';
|
||||||
import { IRSForm, IRSFormData } from '@/models/rsform';
|
import { IRSForm, IRSFormData } from '@/models/rsform';
|
||||||
import { loadRSFormData } from '@/models/rsformAPI';
|
import { RSFormLoader } from '@/models/RSFormLoader';
|
||||||
import { getRSFormDetails } from '@/utils/backendAPI';
|
import { getRSFormDetails } from '@/utils/backendAPI';
|
||||||
|
|
||||||
function useRSFormDetails({ target, version }: { target?: string; version?: string }) {
|
function useRSFormDetails({ target, version }: { target?: string; version?: string }) {
|
||||||
|
@ -17,7 +17,7 @@ function useRSFormDetails({ target, version }: { target?: string; version?: stri
|
||||||
setInnerSchema(undefined);
|
setInnerSchema(undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const schema = loadRSFormData(data);
|
const schema = new RSFormLoader(data).produceRSForm();
|
||||||
setInnerSchema(schema);
|
setInnerSchema(schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
178
rsconcept/frontend/src/models/RSFormLoader.ts
Normal file
178
rsconcept/frontend/src/models/RSFormLoader.ts
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
/**
|
||||||
|
* Module: RSForm data loading and processing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Graph } from './Graph';
|
||||||
|
import { ConstituentaID, CstType, IConstituenta, IRSForm, IRSFormData, IRSFormStats } from './rsform';
|
||||||
|
import { inferClass, inferStatus, inferTemplate, isBaseSet, isFunctional } from './rsformAPI';
|
||||||
|
import { ParsingStatus, ValueClass } from './rslang';
|
||||||
|
import { extractGlobals, isSimpleExpression, splitTemplateDefinition } from './rslangAPI';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads data into an {@link IRSForm} based on {@link IRSFormData}.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* This function processes the provided input, initializes the IRSForm, and calculates statistics
|
||||||
|
* based on the loaded data. It also establishes dependencies between concepts in the graph.
|
||||||
|
*/
|
||||||
|
export class RSFormLoader {
|
||||||
|
private schema: IRSFormData;
|
||||||
|
private graph: Graph = new Graph();
|
||||||
|
private cstByAlias: Map<string, IConstituenta> = new Map();
|
||||||
|
private cstByID: Map<ConstituentaID, IConstituenta> = new Map();
|
||||||
|
|
||||||
|
constructor(input: IRSFormData) {
|
||||||
|
this.schema = input;
|
||||||
|
}
|
||||||
|
|
||||||
|
produceRSForm(): IRSForm {
|
||||||
|
this.prepareLookups();
|
||||||
|
this.createGraph();
|
||||||
|
this.inferCstAttributes();
|
||||||
|
|
||||||
|
const result = this.schema as IRSForm;
|
||||||
|
result.stats = this.calculateStats();
|
||||||
|
result.graph = this.graph;
|
||||||
|
result.cstByAlias = this.cstByAlias;
|
||||||
|
result.cstByID = this.cstByID;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private prepareLookups() {
|
||||||
|
this.schema.items.forEach(cst => {
|
||||||
|
this.cstByAlias.set(cst.alias, cst as IConstituenta);
|
||||||
|
this.cstByID.set(cst.id, cst as IConstituenta);
|
||||||
|
this.graph.addNode(cst.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private createGraph() {
|
||||||
|
this.schema.items.forEach(cst => {
|
||||||
|
const dependencies = extractGlobals(cst.definition_formal);
|
||||||
|
dependencies.forEach(alias => {
|
||||||
|
const source = this.cstByAlias.get(alias);
|
||||||
|
if (source) {
|
||||||
|
this.graph.addEdge(source.id, cst.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private inferCstAttributes() {
|
||||||
|
this.graph.topologicalOrder().forEach(cstID => {
|
||||||
|
const cst = this.cstByID.get(cstID)!;
|
||||||
|
cst.status = inferStatus(cst.parse.status, cst.parse.valueClass);
|
||||||
|
cst.is_template = inferTemplate(cst.definition_formal);
|
||||||
|
cst.cst_class = inferClass(cst.cst_type, cst.is_template);
|
||||||
|
cst.children = [];
|
||||||
|
cst.children_alias = [];
|
||||||
|
cst.is_simple_expression = this.inferSimpleExpression(cst);
|
||||||
|
if (!cst.is_simple_expression || cst.cst_type === CstType.STRUCTURED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cst.parent = this.inferParent(cst);
|
||||||
|
if (cst.parent) {
|
||||||
|
const parent = this.cstByID.get(cst.parent)!;
|
||||||
|
cst.parent_alias = parent.alias;
|
||||||
|
parent.children.push(cst.id);
|
||||||
|
parent.children_alias.push(cst.alias);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private inferSimpleExpression(target: IConstituenta): boolean {
|
||||||
|
if (target.cst_type === CstType.STRUCTURED || isBaseSet(target.cst_type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const dependencies = this.graph.at(target.id)!.inputs;
|
||||||
|
const hasComplexDependency = dependencies.some(id => {
|
||||||
|
const cst = this.cstByID.get(id)!;
|
||||||
|
return cst.is_template && !cst.is_simple_expression;
|
||||||
|
});
|
||||||
|
if (hasComplexDependency) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const expression = isFunctional(target.cst_type)
|
||||||
|
? splitTemplateDefinition(target.definition_formal).body
|
||||||
|
: target.definition_formal;
|
||||||
|
return isSimpleExpression(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
private inferParent(target: IConstituenta): ConstituentaID | undefined {
|
||||||
|
const sources = this.extractSources(target);
|
||||||
|
if (sources.size !== 1 || sources.has(target.id)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return sources.values().next().value as ConstituentaID;
|
||||||
|
}
|
||||||
|
|
||||||
|
private extractSources(target: IConstituenta): Set<ConstituentaID> {
|
||||||
|
const sources: Set<ConstituentaID> = new Set();
|
||||||
|
if (!isFunctional(target.cst_type)) {
|
||||||
|
const node = this.graph.at(target.id)!;
|
||||||
|
node.inputs.forEach(id => {
|
||||||
|
const parent = this.cstByID.get(id)!;
|
||||||
|
if (!parent.is_template || !parent.is_simple_expression) {
|
||||||
|
sources.add(parent.parent ?? id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expression = splitTemplateDefinition(target.definition_formal);
|
||||||
|
const bodyDependencies = extractGlobals(expression.body);
|
||||||
|
bodyDependencies.forEach(alias => {
|
||||||
|
const parent = this.cstByAlias.get(alias);
|
||||||
|
if (parent && (!parent.is_template || !parent.is_simple_expression)) {
|
||||||
|
sources.add(this.cstByID.get(parent.id)!.parent ?? parent.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const needCheckHead = () => {
|
||||||
|
if (sources.size === 0) {
|
||||||
|
return true;
|
||||||
|
} else if (sources.size !== 1) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
const base = this.cstByID.get(sources.values().next().value as ConstituentaID)!;
|
||||||
|
return !isFunctional(base.cst_type) || splitTemplateDefinition(base.definition_formal).head !== expression.head;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (needCheckHead()) {
|
||||||
|
const headDependencies = extractGlobals(expression.head);
|
||||||
|
headDependencies.forEach(alias => {
|
||||||
|
const parent = this.cstByAlias.get(alias);
|
||||||
|
if (parent && !isBaseSet(parent.cst_type) && (!parent.is_template || !parent.is_simple_expression)) {
|
||||||
|
sources.add(parent.parent ?? parent.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateStats(): IRSFormStats {
|
||||||
|
const items = this.schema.items;
|
||||||
|
return {
|
||||||
|
count_all: items.length,
|
||||||
|
count_errors: items.reduce((sum, cst) => sum + (cst.parse.status === ParsingStatus.INCORRECT ? 1 : 0), 0),
|
||||||
|
count_property: items.reduce((sum, cst) => sum + (cst.parse.valueClass === ValueClass.PROPERTY ? 1 : 0), 0),
|
||||||
|
count_incalculable: items.reduce(
|
||||||
|
(sum, cst) =>
|
||||||
|
sum + (cst.parse.status === ParsingStatus.VERIFIED && cst.parse.valueClass === ValueClass.INVALID ? 1 : 0),
|
||||||
|
0
|
||||||
|
),
|
||||||
|
|
||||||
|
count_text_term: items.reduce((sum, cst) => sum + (cst.term_raw ? 1 : 0), 0),
|
||||||
|
count_definition: items.reduce((sum, cst) => sum + (cst.definition_raw ? 1 : 0), 0),
|
||||||
|
count_convention: items.reduce((sum, cst) => sum + (cst.convention ? 1 : 0), 0),
|
||||||
|
|
||||||
|
count_base: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.BASE ? 1 : 0), 0),
|
||||||
|
count_constant: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.CONSTANT ? 1 : 0), 0),
|
||||||
|
count_structured: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.STRUCTURED ? 1 : 0), 0),
|
||||||
|
count_axiom: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.AXIOM ? 1 : 0), 0),
|
||||||
|
count_term: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.TERM ? 1 : 0), 0),
|
||||||
|
count_function: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.FUNCTION ? 1 : 0), 0),
|
||||||
|
count_predicate: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.PREDICATE ? 1 : 0), 0),
|
||||||
|
count_theorem: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.THEOREM ? 1 : 0), 0)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,16 +90,9 @@ export interface ICstTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents Constituenta.
|
* Represents Constituenta data from server.
|
||||||
*/
|
*/
|
||||||
export interface IConstituenta extends IConstituentaMeta {
|
export interface IConstituentaData extends IConstituentaMeta {
|
||||||
cst_class: CstClass;
|
|
||||||
status: ExpressionStatus;
|
|
||||||
is_template: boolean;
|
|
||||||
derived_from: ConstituentaID;
|
|
||||||
derived_from_alias?: string;
|
|
||||||
derived_children: number[];
|
|
||||||
derived_children_alias: string[];
|
|
||||||
parse: {
|
parse: {
|
||||||
status: ParsingStatus;
|
status: ParsingStatus;
|
||||||
valueClass: ValueClass;
|
valueClass: ValueClass;
|
||||||
|
@ -109,6 +102,20 @@ export interface IConstituenta extends IConstituentaMeta {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents Constituenta.
|
||||||
|
*/
|
||||||
|
export interface IConstituenta extends IConstituentaData {
|
||||||
|
cst_class: CstClass;
|
||||||
|
status: ExpressionStatus;
|
||||||
|
is_template: boolean;
|
||||||
|
is_simple_expression: boolean;
|
||||||
|
parent?: ConstituentaID;
|
||||||
|
parent_alias?: string;
|
||||||
|
children: number[];
|
||||||
|
children_alias: string[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents Constituenta list.
|
* Represents Constituenta list.
|
||||||
*/
|
*/
|
||||||
|
@ -223,12 +230,16 @@ export interface IRSForm extends ILibraryItemEx {
|
||||||
items: IConstituenta[];
|
items: IConstituenta[];
|
||||||
stats: IRSFormStats;
|
stats: IRSFormStats;
|
||||||
graph: Graph;
|
graph: Graph;
|
||||||
|
cstByAlias: Map<string, IConstituenta>;
|
||||||
|
cstByID: Map<ConstituentaID, IConstituenta>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents data for {@link IRSForm} provided by backend.
|
* Represents data for {@link IRSForm} provided by backend.
|
||||||
*/
|
*/
|
||||||
export interface IRSFormData extends Omit<IRSForm, 'stats' | 'graph'> {}
|
export interface IRSFormData extends ILibraryItemEx {
|
||||||
|
items: IConstituentaData[];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents data, used for creating {@link IRSForm}.
|
* Represents data, used for creating {@link IRSForm}.
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
* Module: API for formal representation for systems of concepts.
|
* Module: API for formal representation for systems of concepts.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Graph } from '@/models/Graph';
|
|
||||||
import { TextMatcher } from '@/utils/utils';
|
import { TextMatcher } from '@/utils/utils';
|
||||||
|
|
||||||
import { CstMatchMode } from './miscellaneous';
|
import { CstMatchMode } from './miscellaneous';
|
||||||
|
@ -13,124 +12,9 @@ import {
|
||||||
CstType,
|
CstType,
|
||||||
ExpressionStatus,
|
ExpressionStatus,
|
||||||
IConstituenta,
|
IConstituenta,
|
||||||
IRSForm,
|
IRSForm
|
||||||
IRSFormData,
|
|
||||||
IRSFormStats
|
|
||||||
} from './rsform';
|
} from './rsform';
|
||||||
import { ParsingStatus, ValueClass } from './rslang';
|
import { ParsingStatus, ValueClass } from './rslang';
|
||||||
import { extractGlobals, isSimpleExpression, splitTemplateDefinition } from './rslangAPI';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads data into an {@link IRSForm} based on {@link IRSFormData}.
|
|
||||||
*
|
|
||||||
* @remarks
|
|
||||||
* This function processes the provided input, initializes the IRSForm, and calculates statistics
|
|
||||||
* based on the loaded data. It also establishes dependencies between concepts in the graph.
|
|
||||||
*/
|
|
||||||
export function loadRSFormData(input: IRSFormData): IRSForm {
|
|
||||||
const result = input as IRSForm;
|
|
||||||
result.graph = new Graph();
|
|
||||||
result.stats = calculateStats(result.items);
|
|
||||||
|
|
||||||
result.items.forEach(cst => {
|
|
||||||
cst.derived_from = cst.id;
|
|
||||||
cst.derived_children = [];
|
|
||||||
cst.derived_children_alias = [];
|
|
||||||
cst.status = inferStatus(cst.parse.status, cst.parse.valueClass);
|
|
||||||
cst.is_template = inferTemplate(cst.definition_formal);
|
|
||||||
cst.cst_class = inferClass(cst.cst_type, cst.is_template);
|
|
||||||
result.graph.addNode(cst.id);
|
|
||||||
const dependencies = extractGlobals(cst.definition_formal);
|
|
||||||
dependencies.forEach(value => {
|
|
||||||
const source = input.items.find(cst => cst.alias === value);
|
|
||||||
if (source) {
|
|
||||||
result.graph.addEdge(source.id, cst.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// Calculate derivation of constituents based on formal definition analysis
|
|
||||||
const derivationLookup: Map<ConstituentaID, ConstituentaID> = new Map();
|
|
||||||
result.graph.topologicalOrder().forEach(id => {
|
|
||||||
derivationLookup.set(id, id);
|
|
||||||
const cst = result.items.find(item => item.id === id)!;
|
|
||||||
if (cst.cst_type === CstType.STRUCTURED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const resolvedInput: Set<ConstituentaID> = new Set();
|
|
||||||
let definition = '';
|
|
||||||
if (!isFunctional(cst.cst_type)) {
|
|
||||||
const node = result.graph.at(id)!;
|
|
||||||
node.inputs.forEach(id => resolvedInput.add(derivationLookup.get(id)!));
|
|
||||||
definition = cst.definition_formal;
|
|
||||||
} else {
|
|
||||||
const expression = splitTemplateDefinition(cst.definition_formal);
|
|
||||||
definition = expression.body;
|
|
||||||
const bodyDependencies = extractGlobals(definition);
|
|
||||||
bodyDependencies.forEach(alias => {
|
|
||||||
const targetCst = result.items.find(item => item.alias === alias);
|
|
||||||
if (targetCst) {
|
|
||||||
resolvedInput.add(derivationLookup.get(targetCst.id)!);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const needCheckHead = () => {
|
|
||||||
if (resolvedInput.size === 0) {
|
|
||||||
return true;
|
|
||||||
} else if (resolvedInput.size !== 1) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
const base = result.items.find(item => item.id === resolvedInput.values().next().value)!;
|
|
||||||
return (
|
|
||||||
!isFunctional(base.cst_type) || splitTemplateDefinition(base.definition_formal).head !== expression.head
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (needCheckHead()) {
|
|
||||||
const headDependencies = extractGlobals(expression.head);
|
|
||||||
headDependencies.forEach(alias => {
|
|
||||||
const targetCst = result.items.find(item => item.alias === alias);
|
|
||||||
if (targetCst && !isBaseSet(targetCst.cst_type)) {
|
|
||||||
resolvedInput.add(derivationLookup.get(targetCst.id)!);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (resolvedInput.size === 1 && isSimpleExpression(definition)) {
|
|
||||||
const parent = result.items.find(item => item.id === resolvedInput.values().next().value)!;
|
|
||||||
cst.derived_from = parent.id;
|
|
||||||
cst.derived_from_alias = parent.alias;
|
|
||||||
parent.derived_children_alias.push(cst.alias);
|
|
||||||
parent.derived_children.push(cst.id);
|
|
||||||
derivationLookup.set(cst.id, parent.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculateStats(items: IConstituenta[]): IRSFormStats {
|
|
||||||
return {
|
|
||||||
count_all: items.length,
|
|
||||||
count_errors: items.reduce((sum, cst) => sum + (cst.parse?.status === ParsingStatus.INCORRECT ? 1 : 0), 0),
|
|
||||||
count_property: items.reduce((sum, cst) => sum + (cst.parse?.valueClass === ValueClass.PROPERTY ? 1 : 0), 0),
|
|
||||||
count_incalculable: items.reduce(
|
|
||||||
(sum, cst) =>
|
|
||||||
sum + (cst.parse.status === ParsingStatus.VERIFIED && cst.parse.valueClass === ValueClass.INVALID ? 1 : 0),
|
|
||||||
0
|
|
||||||
),
|
|
||||||
|
|
||||||
count_text_term: items.reduce((sum, cst) => sum + (cst.term_raw ? 1 : 0), 0),
|
|
||||||
count_definition: items.reduce((sum, cst) => sum + (cst.definition_raw ? 1 : 0), 0),
|
|
||||||
count_convention: items.reduce((sum, cst) => sum + (cst.convention ? 1 : 0), 0),
|
|
||||||
|
|
||||||
count_base: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.BASE ? 1 : 0), 0),
|
|
||||||
count_constant: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.CONSTANT ? 1 : 0), 0),
|
|
||||||
count_structured: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.STRUCTURED ? 1 : 0), 0),
|
|
||||||
count_axiom: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.AXIOM ? 1 : 0), 0),
|
|
||||||
count_term: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.TERM ? 1 : 0), 0),
|
|
||||||
count_function: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.FUNCTION ? 1 : 0), 0),
|
|
||||||
count_predicate: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.PREDICATE ? 1 : 0), 0),
|
|
||||||
count_theorem: items.reduce((sum, cst) => sum + (cst.cst_type === CstType.THEOREM ? 1 : 0), 0)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a given target {@link IConstituenta} matches the specified query using the provided matching mode.
|
* Checks if a given target {@link IConstituenta} matches the specified query using the provided matching mode.
|
||||||
|
@ -232,9 +116,10 @@ export function inferClass(type: CstType, isTemplate: boolean): CstClass {
|
||||||
export function createMockConstituenta(id: ConstituentaID, alias: string, comment: string): IConstituenta {
|
export function createMockConstituenta(id: ConstituentaID, alias: string, comment: string): IConstituenta {
|
||||||
return {
|
return {
|
||||||
id: id,
|
id: id,
|
||||||
derived_from: id,
|
parent: id,
|
||||||
derived_children: [],
|
children: [],
|
||||||
derived_children_alias: [],
|
children_alias: [],
|
||||||
|
is_simple_expression: false,
|
||||||
order: -1,
|
order: -1,
|
||||||
schema: -1,
|
schema: -1,
|
||||||
alias: alias,
|
alias: alias,
|
||||||
|
@ -269,7 +154,7 @@ export function isMockCst(cst: IConstituenta) {
|
||||||
/**
|
/**
|
||||||
* Apply filter based on start {@link IConstituenta} type.
|
* Apply filter based on start {@link IConstituenta} type.
|
||||||
*/
|
*/
|
||||||
export function applyFilterCategory(start: IConstituenta, schema: IRSFormData): IConstituenta[] {
|
export function applyFilterCategory(start: IConstituenta, schema: IRSForm): IConstituenta[] {
|
||||||
const nextCategory = schema.items.find(cst => cst.order > start.order && cst.cst_type === CATEGORY_CST_TYPE);
|
const nextCategory = schema.items.find(cst => cst.order > start.order && cst.cst_type === CATEGORY_CST_TYPE);
|
||||||
return schema.items.filter(cst => cst.order >= start.order && (!nextCategory || cst.order < nextCategory.order));
|
return schema.items.filter(cst => cst.order >= start.order && (!nextCategory || cst.order < nextCategory.order));
|
||||||
}
|
}
|
||||||
|
@ -367,7 +252,7 @@ export function isFunctional(type: CstType): boolean {
|
||||||
* Validate new alias against {@link CstType} and {@link IRSForm}.
|
* Validate new alias against {@link CstType} and {@link IRSForm}.
|
||||||
*/
|
*/
|
||||||
export function validateNewAlias(alias: string, type: CstType, schema: IRSForm): boolean {
|
export function validateNewAlias(alias: string, type: CstType, schema: IRSForm): boolean {
|
||||||
return alias.length >= 2 && alias[0] == getCstTypePrefix(type) && !schema.items.find(cst => cst.alias === alias);
|
return alias.length >= 2 && alias[0] == getCstTypePrefix(type) && !schema.cstByAlias.has(alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -146,7 +146,7 @@ function FormConstituenta({
|
||||||
id='cst_term'
|
id='cst_term'
|
||||||
label='Термин'
|
label='Термин'
|
||||||
placeholder='Обозначение, используемое в текстовых определениях'
|
placeholder='Обозначение, используемое в текстовых определениях'
|
||||||
items={schema?.items}
|
schema={schema}
|
||||||
value={term}
|
value={term}
|
||||||
initialValue={state?.term_raw ?? ''}
|
initialValue={state?.term_raw ?? ''}
|
||||||
resolved={state?.term_resolved ?? ''}
|
resolved={state?.term_resolved ?? ''}
|
||||||
|
@ -198,7 +198,7 @@ function FormConstituenta({
|
||||||
label='Текстовое определение'
|
label='Текстовое определение'
|
||||||
placeholder='Текстовая интерпретация формального выражения'
|
placeholder='Текстовая интерпретация формального выражения'
|
||||||
height='3.8rem'
|
height='3.8rem'
|
||||||
items={schema?.items}
|
schema={schema}
|
||||||
value={textDefinition}
|
value={textDefinition}
|
||||||
initialValue={state?.definition_raw ?? ''}
|
initialValue={state?.definition_raw ?? ''}
|
||||||
resolved={state?.definition_resolved ?? ''}
|
resolved={state?.definition_resolved ?? ''}
|
||||||
|
|
|
@ -62,8 +62,8 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
||||||
|
|
||||||
const [hoverID, setHoverID] = useState<ConstituentaID | undefined>(undefined);
|
const [hoverID, setHoverID] = useState<ConstituentaID | undefined>(undefined);
|
||||||
const hoverCst = useMemo(() => {
|
const hoverCst = useMemo(() => {
|
||||||
return controller.schema?.items.find(cst => cst.id === hoverID);
|
return hoverID && controller.schema?.cstByID.get(hoverID);
|
||||||
}, [controller.schema?.items, hoverID]);
|
}, [controller.schema?.cstByID, hoverID]);
|
||||||
|
|
||||||
const [toggleResetView, setToggleResetView] = useState(false);
|
const [toggleResetView, setToggleResetView] = useState(false);
|
||||||
|
|
||||||
|
@ -87,14 +87,14 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
filtered.nodes.forEach(node => {
|
filtered.nodes.forEach(node => {
|
||||||
const cst = controller.schema!.items.find(cst => cst.id === node.id);
|
const cst = controller.schema!.cstByID.get(node.id);
|
||||||
if (cst) {
|
if (cst) {
|
||||||
result.push({
|
result.push({
|
||||||
id: String(node.id),
|
id: String(node.id),
|
||||||
fill: colorBgGraphNode(cst, coloringScheme, colors),
|
fill: colorBgGraphNode(cst, coloringScheme, colors),
|
||||||
label: cst.alias,
|
label: cst.alias,
|
||||||
subLabel: !filterParams.noText ? cst.term_resolved : undefined,
|
subLabel: !filterParams.noText ? cst.term_resolved : undefined,
|
||||||
size: cst.derived_from_alias ? 1 : 2
|
size: cst.parent_alias ? 1 : 2
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -121,9 +121,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
||||||
if (!controller.schema) {
|
if (!controller.schema) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const definition = controller.selected
|
const definition = controller.selected.map(id => controller.schema!.cstByID.get(id)!.alias).join(' ');
|
||||||
.map(id => controller.schema!.items.find(cst => cst.id === id)!.alias)
|
|
||||||
.join(' ');
|
|
||||||
controller.createCst(controller.nothingSelected ? CstType.BASE : CstType.TERM, false, definition);
|
controller.createCst(controller.nothingSelected ? CstType.BASE : CstType.TERM, false, definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,8 @@ function TermGraph({
|
||||||
ref: graphRef,
|
ref: graphRef,
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
edges: edges,
|
edges: edges,
|
||||||
type: 'multi'
|
type: 'multi',
|
||||||
|
focusOnSelect: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleHoverIn = useCallback(
|
const handleHoverIn = useCallback(
|
||||||
|
|
|
@ -33,7 +33,7 @@ function ViewHidden({ items, selected, toggleSelection, schema, coloringScheme,
|
||||||
[selected]
|
[selected]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (items.length <= 0) {
|
if (!schema || items.length <= 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
@ -43,7 +43,7 @@ function ViewHidden({ items, selected, toggleSelection, schema, coloringScheme,
|
||||||
</p>
|
</p>
|
||||||
<div className='flex flex-wrap justify-center gap-2 py-2 overflow-y-auto' style={{ maxHeight: dismissedHeight }}>
|
<div className='flex flex-wrap justify-center gap-2 py-2 overflow-y-auto' style={{ maxHeight: dismissedHeight }}>
|
||||||
{items.map(cstID => {
|
{items.map(cstID => {
|
||||||
const cst = schema!.items.find(cst => cst.id === cstID)!;
|
const cst = schema.cstByID.get(cstID)!;
|
||||||
const adjustedColoring = coloringScheme === 'none' ? 'status' : coloringScheme;
|
const adjustedColoring = coloringScheme === 'none' ? 'status' : coloringScheme;
|
||||||
const id = `${prefixes.cst_hidden_list}${cst.alias}`;
|
const id = `${prefixes.cst_hidden_list}${cst.alias}`;
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -48,7 +48,7 @@ function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams)
|
||||||
}
|
}
|
||||||
if (params.foldDerived) {
|
if (params.foldDerived) {
|
||||||
schema.items.forEach(cst => {
|
schema.items.forEach(cst => {
|
||||||
if (cst.derived_from_alias) {
|
if (cst.parent_alias) {
|
||||||
graph.foldNode(cst.id);
|
graph.foldNode(cst.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -205,7 +205,7 @@ export const RSEditState = ({
|
||||||
items: deleted
|
items: deleted
|
||||||
};
|
};
|
||||||
|
|
||||||
const deletedNames = deleted.map(id => model.schema?.items.find(cst => cst.id === id)?.alias).join(', ');
|
const deletedNames = deleted.map(id => model.schema!.cstByID.get(id)!.alias).join(', ');
|
||||||
const isEmpty = deleted.length === model.schema.items.length;
|
const isEmpty = deleted.length === model.schema.items.length;
|
||||||
const nextActive = isEmpty ? undefined : getNextActiveOnDelete(activeCst?.id, model.schema.items, deleted);
|
const nextActive = isEmpty ? undefined : getNextActiveOnDelete(activeCst?.id, model.schema.items, deleted);
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ function RSTabs() {
|
||||||
if (!schema || selected.length === 0) {
|
if (!schema || selected.length === 0) {
|
||||||
return undefined;
|
return undefined;
|
||||||
} else {
|
} else {
|
||||||
return schema.items.find(cst => cst.id === selected.at(-1));
|
return schema.cstByID.get(selected.at(-1)!);
|
||||||
}
|
}
|
||||||
}, [schema, selected]);
|
}, [schema, selected]);
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ function RSTabs() {
|
||||||
setIsModified(false);
|
setIsModified(false);
|
||||||
if (activeTab === RSTabID.CST_EDIT) {
|
if (activeTab === RSTabID.CST_EDIT) {
|
||||||
const cstID = Number(cstQuery);
|
const cstID = Number(cstQuery);
|
||||||
if (cstID && schema && schema.items.find(cst => cst.id === cstID)) {
|
if (cstID && schema && schema.cstByID.has(cstID)) {
|
||||||
setSelected([cstID]);
|
setSelected([cstID]);
|
||||||
} else if (schema && schema?.items.length > 0) {
|
} else if (schema && schema?.items.length > 0) {
|
||||||
setSelected([schema.items[0].id]);
|
setSelected([schema.items[0].id]);
|
||||||
|
|
|
@ -110,13 +110,13 @@ function ConstituentsTable({ items, activeID, onOpenEdit, maxHeight, denseThresh
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
when: (cst: IConstituenta) => cst.derived_from === activeID && cst.id !== activeID,
|
when: (cst: IConstituenta) => cst.parent === activeID && cst.id !== activeID,
|
||||||
style: {
|
style: {
|
||||||
backgroundColor: colors.bgOrange50
|
backgroundColor: colors.bgOrange50
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
when: (cst: IConstituenta) => activeID !== undefined && cst.derived_children.includes(activeID),
|
when: (cst: IConstituenta) => activeID !== undefined && cst.children.includes(activeID),
|
||||||
style: {
|
style: {
|
||||||
backgroundColor: colors.bgGreen50
|
backgroundColor: colors.bgGreen50
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,15 +170,15 @@ export function domTooltipConstituenta(cst?: IConstituenta) {
|
||||||
dom.appendChild(convention);
|
dom.appendChild(convention);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cst.derived_from_alias) {
|
if (cst.parent_alias) {
|
||||||
const derived = document.createElement('p');
|
const derived = document.createElement('p');
|
||||||
derived.innerHTML = `<b>Основание:</b> ${cst.derived_from_alias}`;
|
derived.innerHTML = `<b>Основание:</b> ${cst.parent_alias}`;
|
||||||
dom.appendChild(derived);
|
dom.appendChild(derived);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cst.derived_children_alias.length > 0) {
|
if (cst.children_alias.length > 0) {
|
||||||
const children = document.createElement('p');
|
const children = document.createElement('p');
|
||||||
children.innerHTML = `<b>Порождает:</b> ${cst.derived_children_alias.join(', ')}`;
|
children.innerHTML = `<b>Порождает:</b> ${cst.children_alias.join(', ')}`;
|
||||||
dom.appendChild(children);
|
dom.appendChild(children);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -292,8 +292,8 @@ export const mapLabelLayout: Map<string, string> = new Map([
|
||||||
['treeTd3d', 'Граф: ДеревоВер 3D'],
|
['treeTd3d', 'Граф: ДеревоВер 3D'],
|
||||||
['treeLr2d', 'Граф: ДеревоГор 2D'],
|
['treeLr2d', 'Граф: ДеревоГор 2D'],
|
||||||
['treeLr3d', 'Граф: ДеревоГор 3D'],
|
['treeLr3d', 'Граф: ДеревоГор 3D'],
|
||||||
['radialOut2d', 'Граф: Радиальная 2D'],
|
['radialOut2d', 'Граф: Радиус 2D'],
|
||||||
['radialOut3d', 'Граф: Радиальная 3D'],
|
['radialOut3d', 'Граф: Радиус 3D'],
|
||||||
['circular2d', 'Граф: Круговая'],
|
['circular2d', 'Граф: Круговая'],
|
||||||
['hierarchicalTd', 'Граф: ИерархияВер'],
|
['hierarchicalTd', 'Граф: ИерархияВер'],
|
||||||
['hierarchicalLr', 'Граф: ИерархияГор'],
|
['hierarchicalLr', 'Граф: ИерархияГор'],
|
||||||
|
|
|
@ -22,8 +22,8 @@ export const SelectorGraphLayout: { value: LayoutTypes; label: string }[] = [
|
||||||
{ value: 'forceDirected3d', label: 'Граф: Силы 3D' },
|
{ value: 'forceDirected3d', label: 'Граф: Силы 3D' },
|
||||||
{ value: 'treeLr2d', label: 'Граф: ДеревоГ 2D' },
|
{ value: 'treeLr2d', label: 'Граф: ДеревоГ 2D' },
|
||||||
{ value: 'treeLr3d', label: 'Граф: ДеревоГ 3D' },
|
{ value: 'treeLr3d', label: 'Граф: ДеревоГ 3D' },
|
||||||
{ value: 'radialOut2d', label: 'Граф: Радиальная 2D' },
|
{ value: 'radialOut2d', label: 'Граф: Радиус 2D' },
|
||||||
{ value: 'radialOut3d', label: 'Граф: Радиальная 3D' }
|
{ value: 'radialOut3d', label: 'Граф: Радиус 3D' }
|
||||||
// { value: 'circular2d', label: 'circular2d'},
|
// { value: 'circular2d', label: 'circular2d'},
|
||||||
// { value: 'nooverlap', label: 'nooverlap'},
|
// { value: 'nooverlap', label: 'nooverlap'},
|
||||||
// { value: 'hierarchicalTd', label: 'hierarchicalTd'},
|
// { value: 'hierarchicalTd', label: 'hierarchicalTd'},
|
||||||
|
|
Loading…
Reference in New Issue
Block a user