Преобразования
diff --git a/rsconcept/frontend/src/models/miscellaneous.ts b/rsconcept/frontend/src/models/miscellaneous.ts
index d29c00c8..a1430808 100644
--- a/rsconcept/frontend/src/models/miscellaneous.ts
+++ b/rsconcept/frontend/src/models/miscellaneous.ts
@@ -26,7 +26,12 @@ export enum DependencyMode {
/**
* Represents graph node coloring scheme.
*/
-export type GraphColoringScheme = 'none' | 'status' | 'type';
+export type GraphColoring = 'none' | 'status' | 'type';
+
+/**
+ * Represents graph node sizing scheme.
+ */
+export type GraphSizing = 'none' | 'complex' | 'derived';
/**
* Represents font styles.
diff --git a/rsconcept/frontend/src/models/miscellaneousAPI.ts b/rsconcept/frontend/src/models/miscellaneousAPI.ts
index da35d930..f52851b3 100644
--- a/rsconcept/frontend/src/models/miscellaneousAPI.ts
+++ b/rsconcept/frontend/src/models/miscellaneousAPI.ts
@@ -1,7 +1,7 @@
/**
* Module: API for miscellaneous frontend model types. Future targets for refactoring aimed at extracting modules.
*/
-import { DependencyMode, FontStyle, ILibraryFilter, LibraryFilterStrategy } from './miscellaneous';
+import { DependencyMode, FontStyle, GraphSizing, ILibraryFilter, LibraryFilterStrategy } from './miscellaneous';
import { IConstituenta, IRSForm } from './rsform';
/**
@@ -56,3 +56,16 @@ export function filterFromStrategy(strategy: LibraryFilterStrategy): ILibraryFil
case LibraryFilterStrategy.OWNED: return { is_owned: true };
}
}
+
+/**
+ * Apply {@link GraphSizing} to a given {@link IConstituenta}.
+ */
+export function applyNodeSizing(target: IConstituenta, sizing: GraphSizing): number | undefined {
+ if (sizing === 'none') {
+ return undefined;
+ } else if (sizing === 'complex') {
+ return target.is_simple_expression ? 1 : 2;
+ } else {
+ return target.parent ? 1 : 2;
+ }
+}
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx
index 77ec0d66..60310e8f 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/EditorTermGraph.tsx
@@ -3,15 +3,16 @@
import clsx from 'clsx';
import { AnimatePresence } from 'framer-motion';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
-import { GraphEdge, GraphNode, LayoutTypes } from 'reagraph';
import InfoConstituenta from '@/components/info/InfoConstituenta';
import SelectedCounter from '@/components/info/SelectedCounter';
+import { GraphEdge, GraphLayout, GraphNode } from '@/components/ui/GraphUI';
import Overlay from '@/components/ui/Overlay';
import { useConceptOptions } from '@/context/OptionsContext';
import DlgGraphParams from '@/dialogs/DlgGraphParams';
import useLocalStorage from '@/hooks/useLocalStorage';
-import { GraphColoringScheme, GraphFilterParams } from '@/models/miscellaneous';
+import { GraphColoring, GraphFilterParams, GraphSizing } from '@/models/miscellaneous';
+import { applyNodeSizing } from '@/models/miscellaneousAPI';
import { ConstituentaID, CstType } from '@/models/rsform';
import { colorBgGraphNode } from '@/styling/color';
import { PARAMETER, storage } from '@/utils/constants';
@@ -52,11 +53,9 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
const [hidden, setHidden] = useState
([]);
- const [layout, setLayout] = useLocalStorage(storage.rsgraphLayout, 'treeTd2d');
- const [coloringScheme, setColoringScheme] = useLocalStorage(
- storage.rsgraphColoringScheme,
- 'type'
- );
+ const [layout, setLayout] = useLocalStorage(storage.rsgraphLayout, 'treeTd2d');
+ const [coloring, setColoring] = useLocalStorage(storage.rsgraphColoring, 'type');
+ const [sizing, setSizing] = useLocalStorage(storage.rsgraphSizing, 'derived');
const [orbit, setOrbit] = useState(false);
const is3D = useMemo(() => layout.includes('3d'), [layout]);
@@ -91,15 +90,15 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
if (cst) {
result.push({
id: String(node.id),
- fill: colorBgGraphNode(cst, coloringScheme, colors),
+ fill: colorBgGraphNode(cst, coloring, colors),
label: cst.alias,
subLabel: !filterParams.noText ? cst.term_resolved : undefined,
- size: cst.parent_alias ? 1 : 2
+ size: applyNodeSizing(cst, sizing)
});
}
});
return result;
- }, [controller.schema, coloringScheme, filtered.nodes, filterParams.noText, colors]);
+ }, [controller.schema, coloring, sizing, filtered.nodes, filterParams.noText, colors]);
const edges: GraphEdge[] = useMemo(() => {
const result: GraphEdge[] = [];
@@ -132,7 +131,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
controller.deleteCst();
}
- function handleChangeLayout(newLayout: LayoutTypes) {
+ function handleChangeLayout(newLayout: GraphLayout) {
if (newLayout === layout) {
return;
}
@@ -221,7 +220,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
hideZero
totalCount={controller.schema?.stats?.count_all ?? 0}
selectedCount={controller.selected.length}
- position='top-[0.3rem] left-0'
+ position='top-[4.3rem] sm:top-[0.3rem] left-0'
/>
) : null}
-
-
+
+
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSelectors.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSelectors.tsx
index 15e45f18..7243d502 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSelectors.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/GraphSelectors.tsx
@@ -1,35 +1,46 @@
-import { LayoutTypes } from 'reagraph';
-
+import { GraphLayout } from '@/components/ui/GraphUI';
import SelectSingle from '@/components/ui/SelectSingle';
-import { GraphColoringScheme } from '@/models/miscellaneous';
-import { mapLabelColoring, mapLabelLayout } from '@/utils/labels';
-import { SelectorGraphColoring, SelectorGraphLayout } from '@/utils/selectors';
+import { GraphColoring, GraphSizing } from '@/models/miscellaneous';
+import { mapLabelColoring, mapLabelLayout, mapLabelSizing } from '@/utils/labels';
+import { SelectorGraphColoring, SelectorGraphLayout, SelectorGraphSizing } from '@/utils/selectors';
interface GraphSelectorsProps {
- coloring: GraphColoringScheme;
- layout: LayoutTypes;
+ coloring: GraphColoring;
+ layout: GraphLayout;
+ sizing: GraphSizing;
- setLayout: (newValue: LayoutTypes) => void;
- setColoring: (newValue: GraphColoringScheme) => void;
+ setLayout: (newValue: GraphLayout) => void;
+ setColoring: (newValue: GraphColoring) => void;
+ setSizing: (newValue: GraphSizing) => void;
}
-function GraphSelectors({ coloring, setColoring, layout, setLayout }: GraphSelectorsProps) {
+function GraphSelectors({ coloring, setColoring, layout, setLayout, sizing, setSizing }: GraphSelectorsProps) {
return (
-
-
setColoring(data?.value ?? SelectorGraphColoring[0].value)}
- />
+
setLayout(data?.value ?? SelectorGraphLayout[0].value)}
/>
+ setColoring(data?.value ?? SelectorGraphColoring[0].value)}
+ />
+ setSizing(data?.value ?? SelectorGraphSizing[0].value)}
+ />
);
}
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TermGraph.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TermGraph.tsx
index 684182f6..02c4a358 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TermGraph.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/TermGraph.tsx
@@ -2,7 +2,7 @@
import { useCallback, useLayoutEffect, useMemo, useRef } from 'react';
-import GraphUI, { GraphCanvasRef, GraphEdge, GraphNode, LayoutTypes, useSelection } from '@/components/ui/GraphUI';
+import GraphUI, { GraphCanvasRef, GraphEdge, GraphLayout, GraphNode, useSelection } from '@/components/ui/GraphUI';
import { useConceptOptions } from '@/context/OptionsContext';
import { ConstituentaID } from '@/models/rsform';
import { graphDarkT, graphLightT } from '@/styling/color';
@@ -13,7 +13,7 @@ interface TermGraphProps {
edges: GraphEdge[];
selectedIDs: ConstituentaID[];
- layout: LayoutTypes;
+ layout: GraphLayout;
is3D: boolean;
orbit: boolean;
diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx
index 3a4f955e..22f4ebba 100644
--- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx
+++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/ViewHidden.tsx
@@ -1,47 +1,84 @@
'use client';
-import { useCallback, useMemo } from 'react';
+import clsx from 'clsx';
+import { motion } from 'framer-motion';
+import { useMemo } from 'react';
+import { IconDropArrow, IconDropArrowUp } from '@/components/Icons';
import ConstituentaTooltip from '@/components/info/ConstituentaTooltip';
+import MiniButton from '@/components/ui/MiniButton';
+import Overlay from '@/components/ui/Overlay';
import { useConceptOptions } from '@/context/OptionsContext';
-import { GraphColoringScheme } from '@/models/miscellaneous';
+import useLocalStorage from '@/hooks/useLocalStorage';
+import useWindowSize from '@/hooks/useWindowSize';
+import { GraphColoring } from '@/models/miscellaneous';
import { ConstituentaID, IRSForm } from '@/models/rsform';
+import { animateDropdown, animateHiddenHeader } from '@/styling/animations';
import { colorBgGraphNode } from '@/styling/color';
-import { prefixes } from '@/utils/constants';
+import { prefixes, storage } from '@/utils/constants';
interface ViewHiddenProps {
items: ConstituentaID[];
selected: ConstituentaID[];
schema?: IRSForm;
- coloringScheme: GraphColoringScheme;
+ coloringScheme: GraphColoring;
toggleSelection: (cstID: ConstituentaID) => void;
onEdit: (cstID: ConstituentaID) => void;
}
function ViewHidden({ items, selected, toggleSelection, schema, coloringScheme, onEdit }: ViewHiddenProps) {
- const { colors, noNavigation } = useConceptOptions();
-
- const dismissedHeight = useMemo(() => {
- return !noNavigation ? 'calc(100vh - 28rem - 4px)' : 'calc(100vh - 22.2rem - 4px)';
- }, [noNavigation]);
-
- const dismissedStyle = useCallback(
- (cstID: ConstituentaID) => {
- return selected.includes(cstID) ? { outlineWidth: '2px', outlineStyle: 'solid' } : {};
- },
- [selected]
- );
+ const { colors, calculateHeight } = useConceptOptions();
+ const windowSize = useWindowSize();
+ const localSelected = useMemo(() => items.filter(id => selected.includes(id)), [items, selected]);
+ const [isFolded, setIsFolded] = useLocalStorage(storage.rsgraphFoldHidden, false);
if (!schema || items.length <= 0) {
return null;
}
return (
-
-
- Скрытые конституенты
-
-
+
+
+ : }
+ onClick={() => setIsFolded(prev => !prev)}
+ />
+
+
+ {`Скрытые [${localSelected.length} | ${items.length}]`}
+
+
+
{items.map(cstID => {
const cst = schema.cstByID.get(cstID)!;
const adjustedColoring = coloringScheme === 'none' ? 'status' : coloringScheme;
@@ -54,7 +91,7 @@ function ViewHidden({ items, selected, toggleSelection, schema, coloringScheme,
className='min-w-[3rem] rounded-md text-center cursor-pointer select-none'
style={{
backgroundColor: colorBgGraphNode(cst, adjustedColoring, colors),
- ...dismissedStyle(cstID)
+ ...(localSelected.includes(cstID) ? { outlineWidth: '2px', outlineStyle: 'solid' } : {})
}}
onClick={() => toggleSelection(cstID)}
onDoubleClick={() => onEdit(cstID)}
@@ -65,7 +102,7 @@ function ViewHidden({ items, selected, toggleSelection, schema, coloringScheme,
);
})}
-
+
);
}
diff --git a/rsconcept/frontend/src/styling/animations.ts b/rsconcept/frontend/src/styling/animations.ts
index dc85a695..5282c28b 100644
--- a/rsconcept/frontend/src/styling/animations.ts
+++ b/rsconcept/frontend/src/styling/animations.ts
@@ -74,6 +74,27 @@ export const animateSlideLeft: Variants = {
}
};
+export const animateHiddenHeader: Variants = {
+ open: {
+ translateX: 'calc(6.5rem - 50%)',
+ marginLeft: 0,
+ transition: {
+ type: 'spring',
+ bounce: 0,
+ duration: 0.3
+ }
+ },
+ closed: {
+ translateX: 0,
+ marginLeft: '0.75rem',
+ transition: {
+ type: 'spring',
+ bounce: 0,
+ duration: 0.3
+ }
+ }
+};
+
export const animateDropdown: Variants = {
open: {
clipPath: 'inset(0% 0% 0% 0%)',
diff --git a/rsconcept/frontend/src/styling/color.ts b/rsconcept/frontend/src/styling/color.ts
index ee850cbd..59a54348 100644
--- a/rsconcept/frontend/src/styling/color.ts
+++ b/rsconcept/frontend/src/styling/color.ts
@@ -3,7 +3,7 @@
*/
import { GramData, Grammeme, NounGrams, PartOfSpeech, VerbGrams } from '@/models/language';
-import { GraphColoringScheme } from '@/models/miscellaneous';
+import { GraphColoring } from '@/models/miscellaneous';
import { CstClass, ExpressionStatus, IConstituenta } from '@/models/rsform';
import { ISyntaxTreeNode, TokenID } from '@/models/rslang';
@@ -458,7 +458,7 @@ export function colorFgGrammeme(gram: GramData, colors: IColorTheme): string {
/**
* Determines graph color for {@link IConstituenta}.
*/
-export function colorBgGraphNode(cst: IConstituenta, coloringScheme: GraphColoringScheme, colors: IColorTheme): string {
+export function colorBgGraphNode(cst: IConstituenta, coloringScheme: GraphColoring, colors: IColorTheme): string {
if (coloringScheme === 'type') {
return colorBgCstClass(cst.cst_class, colors);
}
diff --git a/rsconcept/frontend/src/utils/constants.ts b/rsconcept/frontend/src/utils/constants.ts
index 35c03432..078bf71b 100644
--- a/rsconcept/frontend/src/utils/constants.ts
+++ b/rsconcept/frontend/src/utils/constants.ts
@@ -91,7 +91,9 @@ export const storage = {
rsgraphFilter: 'rsgraph.filter_options',
rsgraphLayout: 'rsgraph.layout',
- rsgraphColoringScheme: 'rsgraph.coloring_scheme',
+ rsgraphColoring: 'rsgraph.coloring',
+ rsgraphSizing: 'rsgraph.sizing',
+ rsgraphFoldHidden: 'rsgraph.fold_hidden',
cstFilterMatch: 'cst.filter.match',
cstFilterGraph: 'cst.filter.graph'
diff --git a/rsconcept/frontend/src/utils/labels.ts b/rsconcept/frontend/src/utils/labels.ts
index e0afd477..7e0df2c1 100644
--- a/rsconcept/frontend/src/utils/labels.ts
+++ b/rsconcept/frontend/src/utils/labels.ts
@@ -4,8 +4,17 @@
* Label is a short text used to represent an entity.
* Description is a long description used in tooltips.
*/
+import { GraphLayout } from '@/components/ui/GraphUI';
import { GramData, Grammeme, ReferenceType } from '@/models/language';
-import { CstMatchMode, DependencyMode, HelpTopic, LibraryFilterStrategy, UserAccessMode } from '@/models/miscellaneous';
+import {
+ CstMatchMode,
+ DependencyMode,
+ GraphColoring,
+ GraphSizing,
+ HelpTopic,
+ LibraryFilterStrategy,
+ UserAccessMode
+} from '@/models/miscellaneous';
import { CstClass, CstType, ExpressionStatus, IConstituenta, IRSForm } from '@/models/rsform';
import {
IArgumentInfo,
@@ -284,29 +293,35 @@ export function describeLibraryFilter(strategy: LibraryFilterStrategy): string {
/**
* Retrieves label for graph layout mode.
*/
-export const mapLabelLayout: Map = new Map([
+export const mapLabelLayout: Map = new Map([
+ ['treeTd2d', 'Граф: ДеревоВ 2D'],
+ ['treeTd3d', 'Граф: ДеревоВ 3D'],
['forceatlas2', 'Граф: Атлас 2D'],
['forceDirected2d', 'Граф: Силы 2D'],
['forceDirected3d', 'Граф: Силы 3D'],
- ['treeTd2d', 'Граф: ДеревоВер 2D'],
- ['treeTd3d', 'Граф: ДеревоВер 3D'],
- ['treeLr2d', 'Граф: ДеревоГор 2D'],
- ['treeLr3d', 'Граф: ДеревоГор 3D'],
+ ['treeLr2d', 'Граф: ДеревоГ 2D'],
+ ['treeLr3d', 'Граф: ДеревоГ 3D'],
['radialOut2d', 'Граф: Радиус 2D'],
['radialOut3d', 'Граф: Радиус 3D'],
- ['circular2d', 'Граф: Круговая'],
- ['hierarchicalTd', 'Граф: ИерархияВер'],
- ['hierarchicalLr', 'Граф: ИерархияГор'],
- ['nooverlap', 'Граф: Без перекрытия']
+ ['circular2d', 'Граф: Круговая']
]);
/**
- * Retrieves label for graph coloring mode.
+ * Retrieves label for {@link GraphColoring}.
*/
-export const mapLabelColoring: Map = new Map([
- ['none', 'Цвет: моно'],
- ['status', 'Цвет: статус'],
- ['type', 'Цвет: класс']
+export const mapLabelColoring: Map = new Map([
+ ['none', 'Цвет: Моно'],
+ ['status', 'Цвет: Статус'],
+ ['type', 'Цвет: Класс']
+]);
+
+/**
+ * Retrieves label for {@link GraphSizing}.
+ */
+export const mapLabelSizing: Map = new Map([
+ ['none', 'Узлы: Моно'],
+ ['derived', 'Узлы: Порожденные'],
+ ['complex', 'Узлы: Простые']
]);
/**
diff --git a/rsconcept/frontend/src/utils/selectors.ts b/rsconcept/frontend/src/utils/selectors.ts
index f657cf87..ab6a576e 100644
--- a/rsconcept/frontend/src/utils/selectors.ts
+++ b/rsconcept/frontend/src/utils/selectors.ts
@@ -1,43 +1,32 @@
/**
* Module: Mappings for selector UI elements. Do not confuse with html selectors
*/
-import { LayoutTypes } from 'reagraph';
+import { GraphLayout } from '@/components/ui/GraphUI';
import { type GramData, Grammeme, ReferenceType } from '@/models/language';
import { grammemeCompare } from '@/models/languageAPI';
-import { GraphColoringScheme } from '@/models/miscellaneous';
+import { GraphColoring, GraphSizing } from '@/models/miscellaneous';
import { CstType } from '@/models/rsform';
-import { labelGrammeme, labelReferenceType } from './labels';
+import { labelGrammeme, labelReferenceType, mapLabelColoring, mapLabelLayout, mapLabelSizing } from './labels';
import { labelCstType } from './labels';
/**
* Represents options for GraphLayout selector.
*/
-export const SelectorGraphLayout: { value: LayoutTypes; label: string }[] = [
- { value: 'treeTd2d', label: 'Граф: ДеревоВ 2D' },
- { value: 'treeTd3d', label: 'Граф: ДеревоВ 3D' },
- { value: 'forceatlas2', label: 'Граф: Атлас 2D' },
- { value: 'forceDirected2d', label: 'Граф: Силы 2D' },
- { value: 'forceDirected3d', label: 'Граф: Силы 3D' },
- { value: 'treeLr2d', label: 'Граф: ДеревоГ 2D' },
- { value: 'treeLr3d', label: 'Граф: ДеревоГ 3D' },
- { value: 'radialOut2d', label: 'Граф: Радиус 2D' },
- { value: 'radialOut3d', label: 'Граф: Радиус 3D' }
- // { value: 'circular2d', label: 'circular2d'},
- // { value: 'nooverlap', label: 'nooverlap'},
- // { value: 'hierarchicalTd', label: 'hierarchicalTd'},
- // { value: 'hierarchicalLr', label: 'hierarchicalLr'}
-];
+export const SelectorGraphLayout: { value: GraphLayout; label: string }[] = //
+ [...mapLabelLayout.entries()].map(item => ({ value: item[0], label: item[1] }));
+/**
+ * Represents options for {@link GraphColoring} selector.
+ */
+export const SelectorGraphColoring: { value: GraphColoring; label: string }[] = //
+ [...mapLabelColoring.entries()].map(item => ({ value: item[0], label: item[1] }));
/**
- * Represents options for {@link GraphColoringScheme} selector.
+ * Represents options for {@link GraphSizing} selector.
*/
-export const SelectorGraphColoring: { value: GraphColoringScheme; label: string }[] = [
- { value: 'none', label: 'Цвет: моно' },
- { value: 'status', label: 'Цвет: статус' },
- { value: 'type', label: 'Цвет: класс' }
-];
+export const SelectorGraphSizing: { value: GraphSizing; label: string }[] = //
+ [...mapLabelSizing.entries()].map(item => ({ value: item[0], label: item[1] }));
/**
* Represents options for {@link CstType} selector.