mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
F: Add schemas coloring option for TermGraph
This commit is contained in:
parent
e0eb06b27f
commit
fcfc2e909b
|
@ -126,7 +126,7 @@ class InheritanceDataSerializer(serializers.Serializer):
|
||||||
''' Serializer: inheritance data. '''
|
''' Serializer: inheritance data. '''
|
||||||
child = serializers.IntegerField()
|
child = serializers.IntegerField()
|
||||||
child_source = serializers.IntegerField()
|
child_source = serializers.IntegerField()
|
||||||
parent = serializers.IntegerField()
|
parent = serializers.IntegerField() # type: ignore
|
||||||
parent_source = serializers.IntegerField()
|
parent_source = serializers.IntegerField()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,8 @@ import TextURL from '@/components/ui/TextURL';
|
||||||
import Tooltip, { PlacesType } from '@/components/ui/Tooltip';
|
import Tooltip, { PlacesType } from '@/components/ui/Tooltip';
|
||||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
import { HelpTopic } from '@/models/miscellaneous';
|
import { HelpTopic } from '@/models/miscellaneous';
|
||||||
|
import TopicPage from '@/pages/ManualsPage/TopicPage';
|
||||||
|
|
||||||
import TopicPage from '../../pages/ManualsPage/TopicPage';
|
|
||||||
import { IconHelp } from '../Icons';
|
import { IconHelp } from '../Icons';
|
||||||
import { CProps } from '../props';
|
import { CProps } from '../props';
|
||||||
|
|
||||||
|
|
|
@ -60,10 +60,14 @@ export class RSFormLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
private inferCstAttributes() {
|
private inferCstAttributes() {
|
||||||
const parent_schemas = new Map<ConstituentaID, LibraryItemID>();
|
const schemaByCst = new Map<ConstituentaID, LibraryItemID>();
|
||||||
|
const parents: LibraryItemID[] = [];
|
||||||
this.schema.inheritance.forEach(item => {
|
this.schema.inheritance.forEach(item => {
|
||||||
if (item.child_source === this.schema.id) {
|
if (item.child_source === this.schema.id) {
|
||||||
parent_schemas.set(item.child, item.parent_source);
|
schemaByCst.set(item.child, item.parent_source);
|
||||||
|
if (!parents.includes(item.parent_source)) {
|
||||||
|
parents.push(item.parent_source);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const inherit_children = new Set(this.schema.inheritance.map(item => item.child));
|
const inherit_children = new Set(this.schema.inheritance.map(item => item.child));
|
||||||
|
@ -75,7 +79,8 @@ export class RSFormLoader {
|
||||||
cst.cst_class = inferClass(cst.cst_type, cst.is_template);
|
cst.cst_class = inferClass(cst.cst_type, cst.is_template);
|
||||||
cst.spawn = [];
|
cst.spawn = [];
|
||||||
cst.spawn_alias = [];
|
cst.spawn_alias = [];
|
||||||
cst.parent_schema = parent_schemas.get(cst.id);
|
cst.parent_schema = schemaByCst.get(cst.id);
|
||||||
|
cst.parent_schema_index = cst.parent_schema ? parents.indexOf(cst.parent_schema) + 1 : 0;
|
||||||
cst.is_inherited = inherit_children.has(cst.id);
|
cst.is_inherited = inherit_children.has(cst.id);
|
||||||
cst.has_inherited_children = inherit_parents.has(cst.id);
|
cst.has_inherited_children = inherit_parents.has(cst.id);
|
||||||
cst.is_simple_expression = this.inferSimpleExpression(cst);
|
cst.is_simple_expression = this.inferSimpleExpression(cst);
|
||||||
|
|
|
@ -48,7 +48,7 @@ export interface OssNodeInternal {
|
||||||
/**
|
/**
|
||||||
* Represents graph node coloring scheme.
|
* Represents graph node coloring scheme.
|
||||||
*/
|
*/
|
||||||
export type GraphColoring = 'none' | 'status' | 'type';
|
export type GraphColoring = 'none' | 'status' | 'type' | 'schemas';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents graph node sizing scheme.
|
* Represents graph node sizing scheme.
|
||||||
|
|
|
@ -111,6 +111,10 @@ export interface IConstituenta extends IConstituentaData {
|
||||||
/** Indicates if this {@link IConstituenta} has a simple expression. */
|
/** Indicates if this {@link IConstituenta} has a simple expression. */
|
||||||
is_simple_expression: boolean;
|
is_simple_expression: boolean;
|
||||||
|
|
||||||
|
/** Index of {@link LibraryItemID} that contains this cst (or inheritance parent).
|
||||||
|
* 0 - not inherited, 1 - inherited by 1st schema, 2 - inherited by 2nd schema, etc.
|
||||||
|
*/
|
||||||
|
parent_schema_index: number;
|
||||||
/** {@link LibraryItemID} that contains parent of this inherited {@link IConstituenta}. */
|
/** {@link LibraryItemID} that contains parent of this inherited {@link IConstituenta}. */
|
||||||
parent_schema?: LibraryItemID;
|
parent_schema?: LibraryItemID;
|
||||||
/** Indicates if this {@link IConstituenta} is inherited. */
|
/** Indicates if this {@link IConstituenta} is inherited. */
|
||||||
|
|
|
@ -132,6 +132,7 @@ export function createMockConstituenta(id: ConstituentaID, alias: string, commen
|
||||||
definition_raw: '',
|
definition_raw: '',
|
||||||
definition_resolved: '',
|
definition_resolved: '',
|
||||||
status: ExpressionStatus.INCORRECT,
|
status: ExpressionStatus.INCORRECT,
|
||||||
|
parent_schema_index: 0,
|
||||||
is_template: false,
|
is_template: false,
|
||||||
is_inherited: false,
|
is_inherited: false,
|
||||||
has_inherited_children: false,
|
has_inherited_children: false,
|
||||||
|
|
|
@ -261,6 +261,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
||||||
const selectors = useMemo(
|
const selectors = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<GraphSelectors
|
<GraphSelectors
|
||||||
|
schema={controller.schema}
|
||||||
coloring={coloring}
|
coloring={coloring}
|
||||||
layout={layout}
|
layout={layout}
|
||||||
sizing={sizing}
|
sizing={sizing}
|
||||||
|
@ -269,7 +270,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
||||||
setSizing={setSizing}
|
setSizing={setSizing}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
[coloring, layout, sizing, handleChangeLayout, setColoring, setSizing]
|
[coloring, controller.schema, layout, sizing, handleChangeLayout, setColoring, setSizing]
|
||||||
);
|
);
|
||||||
const viewHidden = useMemo(
|
const viewHidden = useMemo(
|
||||||
() => (
|
() => (
|
||||||
|
|
|
@ -3,10 +3,14 @@ import { GraphLayout } from '@/components/ui/GraphUI';
|
||||||
import Overlay from '@/components/ui/Overlay';
|
import Overlay from '@/components/ui/Overlay';
|
||||||
import SelectSingle from '@/components/ui/SelectSingle';
|
import SelectSingle from '@/components/ui/SelectSingle';
|
||||||
import { GraphColoring, GraphSizing, HelpTopic } from '@/models/miscellaneous';
|
import { GraphColoring, GraphSizing, HelpTopic } from '@/models/miscellaneous';
|
||||||
|
import { IRSForm } from '@/models/rsform';
|
||||||
import { mapLabelColoring, mapLabelLayout, mapLabelSizing } from '@/utils/labels';
|
import { mapLabelColoring, mapLabelLayout, mapLabelSizing } from '@/utils/labels';
|
||||||
import { SelectorGraphColoring, SelectorGraphLayout, SelectorGraphSizing } from '@/utils/selectors';
|
import { SelectorGraphColoring, SelectorGraphLayout, SelectorGraphSizing } from '@/utils/selectors';
|
||||||
|
|
||||||
|
import SchemasGuide from './SchemasGuide';
|
||||||
|
|
||||||
interface GraphSelectorsProps {
|
interface GraphSelectorsProps {
|
||||||
|
schema?: IRSForm;
|
||||||
coloring: GraphColoring;
|
coloring: GraphColoring;
|
||||||
layout: GraphLayout;
|
layout: GraphLayout;
|
||||||
sizing: GraphSizing;
|
sizing: GraphSizing;
|
||||||
|
@ -16,7 +20,7 @@ interface GraphSelectorsProps {
|
||||||
setSizing: (newValue: GraphSizing) => void;
|
setSizing: (newValue: GraphSizing) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function GraphSelectors({ coloring, setColoring, layout, setLayout, sizing, setSizing }: GraphSelectorsProps) {
|
function GraphSelectors({ schema, coloring, setColoring, layout, setLayout, sizing, setSizing }: GraphSelectorsProps) {
|
||||||
return (
|
return (
|
||||||
<div className='border rounded-b-none select-none clr-input rounded-t-md'>
|
<div className='border rounded-b-none select-none clr-input rounded-t-md'>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
|
@ -30,6 +34,7 @@ function GraphSelectors({ coloring, setColoring, layout, setLayout, sizing, setS
|
||||||
<Overlay position='right-[2.5rem] top-[0.5rem]'>
|
<Overlay position='right-[2.5rem] top-[0.5rem]'>
|
||||||
{coloring === 'status' ? <BadgeHelp topic={HelpTopic.UI_CST_STATUS} className='min-w-[25rem]' /> : null}
|
{coloring === 'status' ? <BadgeHelp topic={HelpTopic.UI_CST_STATUS} className='min-w-[25rem]' /> : null}
|
||||||
{coloring === 'type' ? <BadgeHelp topic={HelpTopic.UI_CST_CLASS} className='min-w-[25rem]' /> : null}
|
{coloring === 'type' ? <BadgeHelp topic={HelpTopic.UI_CST_CLASS} className='min-w-[25rem]' /> : null}
|
||||||
|
{coloring === 'schemas' && !!schema ? <SchemasGuide schema={schema} /> : null}
|
||||||
</Overlay>
|
</Overlay>
|
||||||
<SelectSingle
|
<SelectSingle
|
||||||
className='my-1'
|
className='my-1'
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { IconHelp } from '@/components/Icons';
|
||||||
|
import Tooltip from '@/components/ui/Tooltip';
|
||||||
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||||
|
import { useLibrary } from '@/context/LibraryContext';
|
||||||
|
import { LibraryItemID } from '@/models/library';
|
||||||
|
import { IRSForm } from '@/models/rsform';
|
||||||
|
import { colorBgSchemas } from '@/styling/color';
|
||||||
|
import { globals, prefixes } from '@/utils/constants';
|
||||||
|
|
||||||
|
interface SchemasGuideProps {
|
||||||
|
schema: IRSForm;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SchemasGuide({ schema }: SchemasGuideProps) {
|
||||||
|
const { colors } = useConceptOptions();
|
||||||
|
const library = useLibrary();
|
||||||
|
|
||||||
|
const schemas = useMemo(() => {
|
||||||
|
const processed = new Set<LibraryItemID>();
|
||||||
|
const aliases: string[] = [];
|
||||||
|
const indexes: number[] = [];
|
||||||
|
schema.items.forEach(cst => {
|
||||||
|
if (cst.parent_schema && !processed.has(cst.parent_schema)) {
|
||||||
|
const item = library.items.find(item => item.id === cst.parent_schema);
|
||||||
|
if (item) {
|
||||||
|
aliases.push(item.alias);
|
||||||
|
} else {
|
||||||
|
aliases.push(`Схема ${cst.parent_schema_index}`);
|
||||||
|
}
|
||||||
|
processed.add(cst.parent_schema);
|
||||||
|
indexes.push(cst.parent_schema_index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const result: string[] = [];
|
||||||
|
for (let i = 1; i <= aliases.length; i++) {
|
||||||
|
const trueIndex = indexes.findIndex(index => index === i);
|
||||||
|
result.push(aliases[trueIndex]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}, [schema, library.items]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div tabIndex={-1} id={globals.graph_schemas} className='p-1'>
|
||||||
|
<IconHelp size='1.25rem' className='icon-primary' />
|
||||||
|
<Tooltip
|
||||||
|
anchorSelect={`#${globals.graph_schemas}`}
|
||||||
|
layer='z-modalTooltip'
|
||||||
|
place='right'
|
||||||
|
className='max-w-[25rem] break-words text-base'
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
className='min-w-[0.6rem] min-h-[0.6rem] border inline-block mr-1 rounded-full'
|
||||||
|
style={{ backgroundColor: colorBgSchemas(0, colors) }}
|
||||||
|
/>
|
||||||
|
Текущая схема
|
||||||
|
</div>
|
||||||
|
{schemas.map((alias, index) => (
|
||||||
|
<div key={`${prefixes.schemas_list}${index}`}>
|
||||||
|
<span
|
||||||
|
className='min-w-[0.6rem] min-h-[0.6rem] border inline-block mr-1 rounded-full'
|
||||||
|
style={{ backgroundColor: colorBgSchemas(index + 1, colors) }}
|
||||||
|
/>
|
||||||
|
{alias}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SchemasGuide;
|
|
@ -1,6 +1,6 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { RefObject, useCallback, useLayoutEffect, useMemo } from 'react';
|
import { RefObject, useCallback, useLayoutEffect } from 'react';
|
||||||
|
|
||||||
import GraphUI, {
|
import GraphUI, {
|
||||||
CollapseProps,
|
CollapseProps,
|
||||||
|
@ -107,12 +107,8 @@ function TermGraph({
|
||||||
setSelections(newSelections);
|
setSelections(newSelections);
|
||||||
}, [selectedIDs, setSelections, nodes]);
|
}, [selectedIDs, setSelections, nodes]);
|
||||||
|
|
||||||
const canvasWidth = useMemo(() => {
|
|
||||||
return 'calc(100vw - 1rem)';
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='relative outline-none' style={{ width: canvasWidth, height: mainHeight }}>
|
<div className='relative outline-none w-[100dvw]' style={{ height: mainHeight }}>
|
||||||
<GraphUI
|
<GraphUI
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
|
|
|
@ -416,6 +416,23 @@ export function colorBgCstClass(cstClass: CstClass, colors: IColorTheme): string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines background color for {@link IConstituenta} depending on its parent schema index.
|
||||||
|
*/
|
||||||
|
export function colorBgSchemas(schema_index: number, colors: IColorTheme): string {
|
||||||
|
if (schema_index === 0) {
|
||||||
|
return colors.bgGreen;
|
||||||
|
}
|
||||||
|
// prettier-ignore
|
||||||
|
switch (schema_index % 4) {
|
||||||
|
case 1: return colors.bgPurple;
|
||||||
|
case 2: return colors.bgOrange;
|
||||||
|
case 3: return colors.bgTeal;
|
||||||
|
case 0: return colors.bgBlue;
|
||||||
|
}
|
||||||
|
return colors.bgBlue;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines background color for {@link GramData}.
|
* Determines background color for {@link GramData}.
|
||||||
*/
|
*/
|
||||||
|
@ -462,5 +479,8 @@ export function colorBgGraphNode(cst: IConstituenta, coloringScheme: GraphColori
|
||||||
if (coloringScheme === 'status') {
|
if (coloringScheme === 'status') {
|
||||||
return colorBgCstStatus(cst.status, colors);
|
return colorBgCstStatus(cst.status, colors);
|
||||||
}
|
}
|
||||||
|
if (coloringScheme === 'schemas') {
|
||||||
|
return colorBgSchemas(cst.parent_schema_index, colors);
|
||||||
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,7 +143,8 @@ export const globals = {
|
||||||
email_tooltip: 'email_tooltip',
|
email_tooltip: 'email_tooltip',
|
||||||
main_scroll: 'main_scroll',
|
main_scroll: 'main_scroll',
|
||||||
library_item_editor: 'library_item_editor',
|
library_item_editor: 'library_item_editor',
|
||||||
constituenta_editor: 'constituenta_editor'
|
constituenta_editor: 'constituenta_editor',
|
||||||
|
graph_schemas: 'graph_schemas_tooltip'
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -165,6 +166,7 @@ export const prefixes = {
|
||||||
cst_source_list: 'cst_source_list_',
|
cst_source_list: 'cst_source_list_',
|
||||||
cst_delete_list: 'cst_delete_list_',
|
cst_delete_list: 'cst_delete_list_',
|
||||||
cst_dependant_list: 'cst_dependant_list_',
|
cst_dependant_list: 'cst_dependant_list_',
|
||||||
|
schemas_list: 'schemas_list_',
|
||||||
operation_list: 'operation_list_',
|
operation_list: 'operation_list_',
|
||||||
csttype_list: 'csttype_',
|
csttype_list: 'csttype_',
|
||||||
policy_list: 'policy_list_',
|
policy_list: 'policy_list_',
|
||||||
|
|
|
@ -315,7 +315,8 @@ export const mapLabelLayout = new Map<GraphLayout, string>([
|
||||||
export const mapLabelColoring = new Map<GraphColoring, string>([
|
export const mapLabelColoring = new Map<GraphColoring, string>([
|
||||||
['none', 'Цвет: Моно'],
|
['none', 'Цвет: Моно'],
|
||||||
['status', 'Цвет: Статус'],
|
['status', 'Цвет: Статус'],
|
||||||
['type', 'Цвет: Класс']
|
['type', 'Цвет: Класс'],
|
||||||
|
['schemas', 'Цвет: Схемы']
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue
Block a user