diff --git a/rsconcept/frontend/src/features/rsform/components/toolbar-graph-selection.tsx b/rsconcept/frontend/src/features/rsform/components/toolbar-graph-selection.tsx
index f1fe756a..5e454480 100644
--- a/rsconcept/frontend/src/features/rsform/components/toolbar-graph-selection.tsx
+++ b/rsconcept/frontend/src/features/rsform/components/toolbar-graph-selection.tsx
@@ -1,4 +1,5 @@
import { MiniButton } from '@/components/control';
+import { Dropdown, DropdownButton, useDropdown } from '@/components/dropdown';
import {
IconGraphCollapse,
IconGraphCore,
@@ -7,6 +8,7 @@ import {
IconGraphInverse,
IconGraphMaximize,
IconGraphOutputs,
+ IconGraphSelection,
IconPredecessor,
IconReset
} from '@/components/icons';
@@ -31,6 +33,7 @@ export function ToolbarGraphSelection({
onChange,
...restProps
}: ToolbarGraphSelectionProps) {
+ const menu = useDropdown();
const emptySelection = selected.length === 0;
function handleSelectCore() {
@@ -43,61 +46,77 @@ export function ToolbarGraphSelection({
}
return (
-
+
}
onClick={() => onChange([])}
disabled={emptySelection}
/>
+
+ }
+ onClick={menu.toggle}
+ disabled={emptySelection}
+ />
+
+ }
+ onClick={() => onChange([...selected, ...graph.expandAllInputs(selected)])}
+ disabled={emptySelection}
+ />
+ }
+ onClick={() => onChange([...selected, ...graph.expandAllOutputs(selected)])}
+ disabled={emptySelection}
+ />
+
+ }
+ onClick={() => onChange([...selected, ...graph.expandInputs(selected)])}
+ disabled={emptySelection}
+ />
+ }
+ onClick={() => onChange([...selected, ...graph.expandOutputs(selected)])}
+ disabled={emptySelection}
+ />
+ }
+ onClick={() => onChange(graph.maximizePart(selected))}
+ disabled={emptySelection}
+ />
+
+
+
}
- onClick={() => onChange([...selected, ...graph.expandAllInputs(selected)])}
- disabled={emptySelection}
+ title='Выделить ядро'
+ icon={
}
+ onClick={handleSelectCore}
/>
}
- onClick={() => onChange([...selected, ...graph.expandAllOutputs(selected)])}
- disabled={emptySelection}
- />
-
}
- onClick={() => onChange(graph.maximizePart(selected))}
- disabled={emptySelection}
- />
-
}
- onClick={() => onChange([...selected, ...graph.expandInputs(selected)])}
- disabled={emptySelection}
- />
-
}
- onClick={() => onChange([...selected, ...graph.expandOutputs(selected)])}
- disabled={emptySelection}
+ title='Выделить собственные'
+ icon={
}
+ onClick={handleSelectOwned}
/>
}
onClick={() => onChange([...graph.nodes.keys()].filter(item => !selected.includes(item)))}
/>
-
}
- onClick={handleSelectCore}
- />
- {isOwned ? (
-
}
- onClick={handleSelectOwned}
- />
- ) : null}
);
}
diff --git a/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-term-graph/tg-flow.tsx b/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-term-graph/tg-flow.tsx
index 60d30aec..1c64ad7d 100644
--- a/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-term-graph/tg-flow.tsx
+++ b/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-term-graph/tg-flow.tsx
@@ -3,7 +3,7 @@
import { useEffect, useRef } from 'react';
import { type Edge, MarkerType, type Node, useEdgesState, useNodesState, useOnSelectionChange } from 'reactflow';
-import { DiagramFlow, useReactFlow, useStoreApi } from '@/components/flow/diagram-flow';
+import { DiagramFlow, useReactFlow } from '@/components/flow/diagram-flow';
import { useMainHeight } from '@/stores/app-layout';
import { PARAMETER } from '@/utils/constants';
import { withPreventDefault } from '@/utils/utils';
@@ -12,10 +12,7 @@ import { useMutatingRSForm } from '../../../backend/use-mutating-rsform';
import { TGEdgeTypes } from '../../../components/term-graph/graph/tg-edge-types';
import { TGNodeTypes } from '../../../components/term-graph/graph/tg-node-types';
import { SelectColoring } from '../../../components/term-graph/select-coloring';
-import { ToolbarFocusedCst } from '../../../components/term-graph/toolbar-focused-cst';
-import { ToolbarGraphSelection } from '../../../components/toolbar-graph-selection';
import { applyLayout, type TGNodeData } from '../../../models/graph-api';
-import { isBasicConcept } from '../../../models/rsform-api';
import { useTermGraphStore } from '../../../stores/term-graph';
import { useRSEdit } from '../rsedit-context';
@@ -36,20 +33,9 @@ export const flowOptions = {
export function TGFlow() {
const mainHeight = useMainHeight();
const { fitView, viewportInitialized } = useReactFlow();
- const store = useStoreApi();
- const { addSelectedNodes } = store.getState();
const isProcessing = useMutatingRSForm();
- const {
- isContentEditable,
- schema,
- selected,
- setSelected,
- promptDeleteCst,
- focusCst,
- setFocus,
- deselectAll,
- navigateCst
- } = useRSEdit();
+ const { isContentEditable, schema, selected, setSelected, promptDeleteCst, focusCst, setFocus, navigateCst } =
+ useRSEdit();
const [nodes, setNodes, onNodesChange] = useNodesState
([]);
const [edges, setEdges] = useEdgesState([]);
@@ -59,11 +45,8 @@ export function TGFlow() {
function onSelectionChange({ nodes }: { nodes: Node[] }) {
const ids = nodes.map(node => Number(node.id));
- if (ids.length === 0) {
- deselectAll();
- } else {
- setSelected(prev => [...prev.filter(nodeID => !filteredGraph.hasNode(nodeID)), ...ids]);
- }
+
+ setSelected(prev => [...prev.filter(nodeID => !filteredGraph.hasNode(nodeID)), ...ids]);
}
useOnSelectionChange({
onChange: onSelectionChange
@@ -130,11 +113,6 @@ export function TGFlow() {
);
}
- function handleSetSelected(newSelection: number[]) {
- setSelected(newSelection);
- addSelectedNodes(newSelection.map(id => String(id)));
- }
-
function handleKeyDown(event: React.KeyboardEvent) {
if (isProcessing) {
return;
@@ -166,26 +144,7 @@ export function TGFlow() {
return (
-
-
- {focusCst ? (
- setFocus(null)}
- />
- ) : (
- {
- const cst = schema.cstByID.get(cstID);
- return !!cst && isBasicConcept(cst.cst_type);
- }}
- isOwned={schema.inheritance.length > 0 ? cstID => !schema.cstByID.get(cstID)?.is_inherited : undefined}
- value={selected}
- onChange={handleSetSelected}
- />
- )}
-
+
diff --git a/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-term-graph/toolbar-term-graph.tsx b/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-term-graph/toolbar-term-graph.tsx
index 89937d8b..c05e5769 100644
--- a/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-term-graph/toolbar-term-graph.tsx
+++ b/rsconcept/frontend/src/features/rsform/pages/rsform-page/editor-term-graph/toolbar-term-graph.tsx
@@ -1,9 +1,12 @@
-import { useReactFlow } from 'reactflow';
+import { useReactFlow, useStoreApi } from 'reactflow';
import { HelpTopic } from '@/features/help';
import { BadgeHelp } from '@/features/help/components/badge-help';
import { type ILibraryItemReference } from '@/features/library';
import { MiniSelectorOSS } from '@/features/library/components/mini-selector-oss';
+import { ToolbarFocusedCst } from '@/features/rsform/components/term-graph/toolbar-focused-cst';
+import { ToolbarGraphSelection } from '@/features/rsform/components/toolbar-graph-selection';
+import { isBasicConcept } from '@/features/rsform/models/rsform-api';
import { MiniButton } from '@/components/control';
import {
@@ -18,6 +21,7 @@ import {
IconTextOff,
IconTypeGraph
} from '@/components/icons';
+import { cn } from '@/components/utils';
import { useDialogsStore } from '@/stores/dialogs';
import { PARAMETER } from '@/utils/constants';
@@ -28,17 +32,23 @@ import { useRSEdit } from '../rsedit-context';
import { flowOptions } from './tg-flow';
-export function ToolbarTermGraph() {
+interface ToolbarTermGraphProps {
+ className?: string;
+}
+
+export function ToolbarTermGraph({ className }: ToolbarTermGraphProps) {
const isProcessing = useMutatingRSForm();
const {
- schema, //
+ schema,
selected,
+ setSelected,
setFocus,
navigateOss,
isContentEditable,
canDeleteSelected,
createCst,
- promptDeleteCst
+ promptDeleteCst,
+ focusCst
} = useRSEdit();
const showTypeGraph = useDialogsStore(state => state.showShowTypeGraph);
const showParams = useDialogsStore(state => state.showGraphParams);
@@ -47,6 +57,8 @@ export function ToolbarTermGraph() {
const toggleClustering = useTermGraphStore(state => state.toggleClustering);
const { fitView } = useReactFlow();
+ const store = useStoreApi();
+ const { addSelectedNodes } = store.getState();
function handleShowTypeGraph() {
const typeInfo = schema.items.map(item => ({
@@ -86,69 +98,102 @@ export function ToolbarTermGraph() {
navigateOss(newValue.id, event.ctrlKey || event.metaKey);
}
+ function handleSetSelected(newSelection: number[]) {
+ setSelected(newSelection);
+ addSelectedNodes(newSelection.map(id => String(id)));
+ }
+
return (
-
- {schema.oss.length > 0 ?
: null}
-
}
- onClick={showParams}
- />
-
}
- disabled={selected.length !== 1}
- onClick={handleSetFocus}
- />
-
}
- onClick={handleFitView}
- />
-
- ) : (
-
- )
- }
- onClick={toggleText}
- />
-
- ) : (
-
- )
- }
- onClick={toggleClustering}
- />
- {isContentEditable ? (
+
+
+ {schema.oss.length > 0 ? : null}
}
- onClick={handleCreateCst}
- disabled={isProcessing}
+ title='Настройки фильтрации узлов и связей'
+ icon={}
+ onClick={showParams}
/>
- ) : null}
- {isContentEditable ? (
}
- onClick={handleDeleteCst}
- disabled={!canDeleteSelected || isProcessing}
+ title='Задать фокус конституенту'
+ icon={}
+ disabled={selected.length !== 1}
+ onClick={handleSetFocus}
/>
- ) : null}
- }
- title='Граф ступеней'
- onClick={handleShowTypeGraph}
- />
-
+ }
+ onClick={handleFitView}
+ />
+
+ ) : (
+
+ )
+ }
+ onClick={toggleText}
+ />
+
+ ) : (
+
+ )
+ }
+ onClick={toggleClustering}
+ />
+
+ }
+ title='Граф ступеней'
+ onClick={handleShowTypeGraph}
+ />
+
+
+
+ {focusCst ? (
+ setFocus(null)}
+ />
+ ) : (
+ {
+ const cst = schema.cstByID.get(cstID);
+ return !!cst && isBasicConcept(cst.cst_type);
+ }}
+ isOwned={schema.inheritance.length > 0 ? cstID => !schema.cstByID.get(cstID)?.is_inherited : undefined}
+ value={selected}
+ onChange={handleSetSelected}
+ />
+ )}
+ {isContentEditable ? (
+ }
+ onClick={handleCreateCst}
+ disabled={isProcessing}
+ />
+ ) : null}
+ {isContentEditable ? (
+ }
+ onClick={handleDeleteCst}
+ disabled={!canDeleteSelected || isProcessing}
+ />
+ ) : null}
+
);
}