+
+
+
+
+
state.showCreateOperation);
const showCreateBlock = useDialogsStore(state => state.showCreateBlock);
const showDeleteOperation = useDialogsStore(state => state.showDeleteOperation);
+ const showEditBlock = useDialogsStore(state => state.showEditBlock);
function onSelectionChange({ nodes }: { nodes: Node[] }) {
const ids = nodes.map(node => Number(node.id));
@@ -215,13 +219,22 @@ export function OssFlow() {
}
function handleNodeDoubleClick(event: React.MouseEvent, node: OssNode) {
- if (node.type === 'block') {
- return;
- }
event.preventDefault();
event.stopPropagation();
- if (node.data.operation?.result) {
- navigateOperationSchema(Number(node.id));
+
+ if (node.type === 'block') {
+ const block = schema.blockByID.get(-Number(node.id));
+ if (block) {
+ showEditBlock({
+ oss: schema,
+ target: block,
+ layout: getLayout()
+ });
+ }
+ } else {
+ if (node.data.operation?.result) {
+ navigateOperationSchema(Number(node.id));
+ }
}
}
@@ -269,7 +282,7 @@ export function OssFlow() {
function determineDropTarget(event: React.MouseEvent): number | null {
const mousePosition = screenToFlowPosition({ x: event.clientX, y: event.clientY });
- const blocks = getIntersectingNodes({
+ let blocks = getIntersectingNodes({
x: mousePosition.x,
y: mousePosition.y,
width: 1,
@@ -283,6 +296,12 @@ export function OssFlow() {
if (blocks.length === 0) {
return null;
}
+
+ const successors = schema.hierarchy.expandAllOutputs([...selected]).filter(id => id < 0);
+ blocks = blocks.filter(block => !successors.includes(-block.id));
+ if (blocks.length === 0) {
+ return null;
+ }
if (blocks.length === 1) {
return blocks[0].id;
}
@@ -317,13 +336,13 @@ export function OssFlow() {
setIsContextMenuOpen(false);
}
- function handleDrag(event: React.MouseEvent) {
+ const handleDrag = useThrottleCallback((event: React.MouseEvent) => {
if (containMovement) {
return;
}
setIsDragging(true);
setDropTarget(determineDropTarget(event));
- }
+ }, DRAG_THROTTLE_DELAY);
function handleDragStop(event: React.MouseEvent, target: Node) {
if (containMovement) {
@@ -412,6 +431,7 @@ export function OssFlow() {
onClick={() => setIsContextMenuOpen(false)}
onNodeDoubleClick={handleNodeDoubleClick}
onNodeContextMenu={handleContextMenu}
+ onContextMenu={event => event.preventDefault()}
onNodeDragStart={handleDragStart}
onNodeDrag={handleDrag}
onNodeDragStop={handleDragStop}
diff --git a/rsconcept/frontend/src/features/oss/pages/oss-page/editor-oss-graph/toolbar-oss-graph.tsx b/rsconcept/frontend/src/features/oss/pages/oss-page/editor-oss-graph/toolbar-oss-graph.tsx
index 4171d45a..a5de6d46 100644
--- a/rsconcept/frontend/src/features/oss/pages/oss-page/editor-oss-graph/toolbar-oss-graph.tsx
+++ b/rsconcept/frontend/src/features/oss/pages/oss-page/editor-oss-graph/toolbar-oss-graph.tsx
@@ -1,5 +1,6 @@
'use client';
+import { toast } from 'react-toastify';
import { useReactFlow } from 'reactflow';
import { HelpTopic } from '@/features/help';
@@ -9,20 +10,16 @@ import { useUpdateLayout } from '@/features/oss/backend/use-update-layout';
import { MiniButton } from '@/components/control';
import {
- IconAnimation,
- IconAnimationOff,
IconConceptBlock,
- IconCoordinates,
IconDestroy,
IconEdit2,
IconExecute,
IconFitImage,
- IconGrid,
- IconLineStraight,
- IconLineWave,
+ IconFixLayout,
IconNewItem,
IconReset,
- IconSave
+ IconSave,
+ IconSettings
} from '@/components/icons';
import { type Styling } from '@/components/props';
import { cn } from '@/components/utils';
@@ -32,7 +29,6 @@ import { prepareTooltip } from '@/utils/utils';
import { OperationType } from '../../../backend/types';
import { useMutatingOss } from '../../../backend/use-mutating-oss';
-import { useOSSGraphStore } from '../../../stores/oss-graph';
import { useOssEdit } from '../oss-edit-context';
import { VIEW_PADDING } from './oss-flow';
@@ -60,20 +56,12 @@ export function ToolbarOssGraph({
const selectedBlock = selected.length !== 1 ? null : schema.blockByID.get(-selected[0]) ?? null;
const getLayout = useGetLayout();
- const showGrid = useOSSGraphStore(state => state.showGrid);
- const showCoordinates = useOSSGraphStore(state => state.showCoordinates);
- const edgeAnimate = useOSSGraphStore(state => state.edgeAnimate);
- const edgeStraight = useOSSGraphStore(state => state.edgeStraight);
- const toggleShowGrid = useOSSGraphStore(state => state.toggleShowGrid);
- const toggleShowCoordinates = useOSSGraphStore(state => state.toggleShowCoordinates);
- const toggleEdgeAnimate = useOSSGraphStore(state => state.toggleEdgeAnimate);
- const toggleEdgeStraight = useOSSGraphStore(state => state.toggleEdgeStraight);
-
const { updateLayout } = useUpdateLayout();
const { executeOperation } = useExecuteOperation();
const showEditOperation = useDialogsStore(state => state.showEditOperation);
const showEditBlock = useDialogsStore(state => state.showEditBlock);
+ const showOssOptions = useDialogsStore(state => state.showOssOptions);
const readyForSynthesis = (() => {
if (!selectedOperation || selectedOperation.operation_type !== OperationType.SYNTHESIS) {
@@ -100,6 +88,15 @@ export function ToolbarOssGraph({
fitView({ duration: PARAMETER.zoomDuration, padding: VIEW_PADDING });
}
+ function handleFixLayout() {
+ // TODO: implement layout algorithm
+ toast.info('Еще не реализовано');
+ }
+
+ function handleShowOptions() {
+ showOssOptions();
+ }
+
function handleSavePositions() {
void updateLayout({ itemID: schema.id, data: getLayout() });
}
@@ -152,46 +149,15 @@ export function ToolbarOssGraph({
onClick={handleFitView}
/>
- ) : (
-
- )
- }
- onClick={toggleShowGrid}
+ title='Исправить позиции узлов'
+ icon={}
+ onClick={handleFixLayout}
+ disabled={selected.length > 1 || selected[0] > 0}
/>
- ) : (
-
- )
- }
- onClick={toggleEdgeStraight}
- />
-
- ) : (
-
- )
- }
- onClick={toggleEdgeAnimate}
- />
- }
- onClick={toggleShowCoordinates}
+ title='Настройки отображения'
+ icon={}
+ onClick={handleShowOptions}
/>
diff --git a/rsconcept/frontend/src/features/rsform/dialogs/dlg-show-ast/ast-flow.tsx b/rsconcept/frontend/src/features/rsform/dialogs/dlg-show-ast/ast-flow.tsx
index b54f9e0f..f1854526 100644
--- a/rsconcept/frontend/src/features/rsform/dialogs/dlg-show-ast/ast-flow.tsx
+++ b/rsconcept/frontend/src/features/rsform/dialogs/dlg-show-ast/ast-flow.tsx
@@ -69,6 +69,7 @@ export function ASTFlow({ data, onNodeEnter, onNodeLeave, onChangeDragging }: AS
maxZoom={2}
minZoom={0.5}
nodesConnectable={false}
+ onContextMenu={event => event.preventDefault()}
/>
);
}
diff --git a/rsconcept/frontend/src/features/rsform/dialogs/dlg-show-type-graph/mgraph-flow.tsx b/rsconcept/frontend/src/features/rsform/dialogs/dlg-show-type-graph/mgraph-flow.tsx
index cf1b6a50..84d26b09 100644
--- a/rsconcept/frontend/src/features/rsform/dialogs/dlg-show-type-graph/mgraph-flow.tsx
+++ b/rsconcept/frontend/src/features/rsform/dialogs/dlg-show-type-graph/mgraph-flow.tsx
@@ -69,6 +69,7 @@ export function MGraphFlow({ data }: MGraphFlowProps) {
maxZoom={ZOOM_MAX}
minZoom={ZOOM_MIN}
nodesConnectable={false}
+ onContextMenu={event => event.preventDefault()}
/>
);
}
diff --git a/rsconcept/frontend/src/hooks/use-throttle-callback.ts b/rsconcept/frontend/src/hooks/use-throttle-callback.ts
new file mode 100644
index 00000000..c3bf450f
--- /dev/null
+++ b/rsconcept/frontend/src/hooks/use-throttle-callback.ts
@@ -0,0 +1,24 @@
+'use client';
+
+import { useCallback, useRef } from 'react';
+
+/** Throttles a callback to only run once per delay. */
+export function useThrottleCallback void>(
+ callback: Callback,
+ delay: number
+): Callback {
+ const lastCalled = useRef(0);
+
+ const throttled = useCallback(
+ (...args: Parameters) => {
+ const now = Date.now();
+ if (now - lastCalled.current >= delay) {
+ lastCalled.current = now;
+ callback(...args);
+ }
+ },
+ [callback, delay]
+ );
+
+ return throttled as Callback;
+}
diff --git a/rsconcept/frontend/src/stores/dialogs.ts b/rsconcept/frontend/src/stores/dialogs.ts
index 801330a1..87d1ed7e 100644
--- a/rsconcept/frontend/src/stores/dialogs.ts
+++ b/rsconcept/frontend/src/stores/dialogs.ts
@@ -44,6 +44,7 @@ export const DialogType = {
DELETE_OPERATION: 10,
CHANGE_INPUT_SCHEMA: 11,
RELOCATE_CONSTITUENTS: 12,
+ OSS_SETTINGS: 26,
CLONE_LIBRARY_ITEM: 13,
UPLOAD_RSFORM: 14,
@@ -91,6 +92,7 @@ interface DialogsStore {
showCreateVersion: (props: DlgCreateVersionProps) => void;
showDeleteOperation: (props: DlgDeleteOperationProps) => void;
showGraphParams: () => void;
+ showOssOptions: () => void;
showRelocateConstituents: (props: DlgRelocateConstituentsProps) => void;
showRenameCst: (props: DlgRenameCstProps) => void;
showQR: (props: DlgShowQRProps) => void;
@@ -128,6 +130,7 @@ export const useDialogsStore = create()(set => ({
showCreateVersion: props => set({ active: DialogType.CREATE_VERSION, props: props }),
showDeleteOperation: props => set({ active: DialogType.DELETE_OPERATION, props: props }),
showGraphParams: () => set({ active: DialogType.GRAPH_PARAMETERS, props: null }),
+ showOssOptions: () => set({ active: DialogType.OSS_SETTINGS, props: null }),
showRelocateConstituents: props => set({ active: DialogType.RELOCATE_CONSTITUENTS, props: props }),
showRenameCst: props => set({ active: DialogType.RENAME_CONSTITUENTA, props: props }),
showQR: props => set({ active: DialogType.SHOW_QR_CODE, props: props }),
diff --git a/rsconcept/frontend/src/styling/components.css b/rsconcept/frontend/src/styling/components.css
index 44d3c01c..4789c330 100644
--- a/rsconcept/frontend/src/styling/components.css
+++ b/rsconcept/frontend/src/styling/components.css
@@ -325,6 +325,7 @@
.selected & {
color: var(--color-foreground);
border-color: var(--color-graph-selected);
+ border-style: solid;
}
&:hover {
diff --git a/rsconcept/frontend/src/styling/reactflow.css b/rsconcept/frontend/src/styling/reactflow.css
index 7042a773..684857bb 100644
--- a/rsconcept/frontend/src/styling/reactflow.css
+++ b/rsconcept/frontend/src/styling/reactflow.css
@@ -157,5 +157,5 @@
margin: 0;
background-color: transparent;
- pointer-events: none;
+ pointer-events: none !important;
}
diff --git a/rsconcept/frontend/src/utils/labels.ts b/rsconcept/frontend/src/utils/labels.ts
index cbf2a174..41d1cb5b 100644
--- a/rsconcept/frontend/src/utils/labels.ts
+++ b/rsconcept/frontend/src/utils/labels.ts
@@ -37,6 +37,7 @@ export const infoMsg = {
versionDestroyed: 'Версия удалена',
itemDestroyed: 'Схема удалена',
operationDestroyed: 'Операция удалена',
+ blockDestroyed: 'Блок удален',
operationExecuted: 'Операция выполнена',
allOperationExecuted: 'Все операции выполнены',
constituentsDestroyed: (count: number) => `Конституенты удалены: ${count}`