diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 7aacf93286..d4e74a2e46 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -58,6 +58,7 @@ Note that double-clicking no longer triggers a direct edit. - https://github.com/eclipse-sirius/sirius-web/issues/2509[#2509] [diagram] Fix an issue that prevents using graphical DnD in deep node in some cases. - https://github.com/eclipse-sirius/sirius-web/issues/2453[#2453] [diagram] Add support for feedback messages on following actions: DropOnDiagram, DeleteFromDiagram, ReconnectEdge and EditLabel - https://github.com/eclipse-sirius/sirius-web/issues/2513[#2513] [diagram] Fix the double node border displayed when node list was containing compartments. +- https://github.com/eclipse-sirius/sirius-web/issues/2532[#2532] [diagram] Fix the limitation on borders that prevent movement on all sides of their node parent. === New Features diff --git a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/DiagramRenderer.tsx b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/DiagramRenderer.tsx index f5c4320ea9..4f43aa3047 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/DiagramRenderer.tsx +++ b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/DiagramRenderer.tsx @@ -31,11 +31,14 @@ import { useReactFlow, useStoreApi, } from 'reactflow'; + +import 'reactflow/dist/style.css'; import { convertDiagram } from '../converter/convertDiagram'; -import { Diagram, DiagramRendererProps, DiagramRendererState, EdgeData, NodeData } from './DiagramRenderer.types'; +import { useBorderChange } from './border/useBorderChange'; import { ConnectorContextualMenu } from './connector/ConnectorContextualMenu'; import { useConnector } from './connector/useConnector'; import { useDiagramDelete } from './delete/useDiagramDelete'; +import { Diagram, DiagramRendererProps, DiagramRendererState, EdgeData, NodeData } from './DiagramRenderer.types'; import { useDiagramDirectEdit } from './direct-edit/useDiagramDirectEdit'; import { useDrop } from './drop/useDrop'; import { useDropNode } from './dropNode/useDropNode'; @@ -50,8 +53,6 @@ import { useDiagramPalette } from './palette/useDiagramPalette'; import { DiagramPanel } from './panel/DiagramPanel'; import { useReconnectEdge } from './reconnect-edge/useReconnectEdge'; -import 'reactflow/dist/style.css'; - const GRID_STEP: number = 10; const isNodeSelectChange = (change: NodeChange): change is NodeSelectionChange => change.type === 'select'; @@ -76,6 +77,7 @@ export const DiagramRenderer = ({ diagramRefreshedEventPayload, selection, setSe const { onConnect, onConnectStart, onConnectEnd } = useConnector(); const { reconnectEdge } = useReconnectEdge(); const { onDrop, onDragOver } = useDrop(); + const { onBorderChange } = useBorderChange(); const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); @@ -145,7 +147,7 @@ export const DiagramRenderer = ({ diagramRefreshedEventPayload, selection, setSe }, [selection]); const handleNodesChange: OnNodesChange = (changes: NodeChange[]) => { - onNodesChange(changes); + onNodesChange(onBorderChange(changes)); const selectionEntries: SelectionEntry[] = changes .filter(isNodeSelectChange) diff --git a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/border/useBorderChange.tsx b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/border/useBorderChange.tsx new file mode 100644 index 0000000000..0159a5fa93 --- /dev/null +++ b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/border/useBorderChange.tsx @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2023 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +import { useCallback } from 'react'; +import { Node, NodeChange, useReactFlow, XYPosition } from 'reactflow'; +import { UseBorderChangeValue } from './useBorderChange.types'; + +const isNewPositionInsideIsParent = (newNodePosition: XYPosition, movedNode: Node, parentNode: Node): boolean => { + if (movedNode.width && movedNode.height && parentNode?.positionAbsolute && parentNode.width && parentNode.height) { + return ( + parentNode.positionAbsolute.x < newNodePosition.x + movedNode.width && + parentNode.positionAbsolute.x + parentNode.width > newNodePosition.x && + parentNode.positionAbsolute.y < newNodePosition.y + movedNode.height && + parentNode.positionAbsolute.y + parentNode.height > newNodePosition.y + ); + } + return false; +}; + +export const useBorderChange = (): UseBorderChangeValue => { + const { getNodes } = useReactFlow(); + + const onBorderChange = useCallback((changes: NodeChange[]): NodeChange[] => { + return changes.map((change) => { + if (change.type === 'position' && change.positionAbsolute) { + const movedNode = getNodes().find((node) => change.id === node.id); + if (movedNode?.data.isBorderNode) { + const parentNode = getNodes().find((node) => movedNode?.parentNode === node.id); + if (parentNode && isNewPositionInsideIsParent(change.positionAbsolute, movedNode, parentNode)) { + //Invalid position, reset to the initial one + change.position = movedNode.position; + change.positionAbsolute = movedNode.positionAbsolute; + } + } + } + return change; + }); + }, []); + + return { onBorderChange }; +}; diff --git a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/border/useBorderChange.types.ts b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/border/useBorderChange.types.ts new file mode 100644 index 0000000000..2c7c684986 --- /dev/null +++ b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/border/useBorderChange.types.ts @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2023 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +import { NodeChange } from 'reactflow'; + +export interface UseBorderChangeValue { + onBorderChange: (changes: NodeChange[]) => NodeChange[]; +} diff --git a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/layoutBorderNodes.ts b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/layoutBorderNodes.ts index ba32ab82c8..0b71630662 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/layoutBorderNodes.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/layout/layoutBorderNodes.ts @@ -32,34 +32,10 @@ export const getBorderNodeExtent = ( ): CoordinateExtent | 'parent' => { let coordinateExtent: CoordinateExtent | 'parent' = 'parent'; if (nodeParent.width && nodeParent.height && borderNorde.height && borderNorde.width) { - switch (borderNorde.data.borderNodePosition) { - case BorderNodePositon.EAST: - coordinateExtent = [ - [nodeParent.width, 0], - [nodeParent.width, nodeParent.height - borderNorde.height], - ]; - break; - case BorderNodePositon.NORTH: - coordinateExtent = [ - [0 - borderNorde.width * 0.8, 0 - borderNorde.height], - [nodeParent.width - borderNorde.width * 0.3, 0 - borderNorde.height], - ]; - break; - case BorderNodePositon.SOUTH: - coordinateExtent = [ - [0 - borderNorde.width * 0.8, nodeParent.height], - [nodeParent.width - borderNorde.width * 0.3, nodeParent.height], - ]; - break; - case BorderNodePositon.WEST: - coordinateExtent = [ - [0 - borderNorde.width, 0], - [0 - borderNorde.width, nodeParent.height - borderNorde.height], - ]; - break; - default: - coordinateExtent = 'parent'; - } + coordinateExtent = [ + [0 - borderNorde.width, 0 - borderNorde.height], + [nodeParent.width, nodeParent.height], + ]; } return coordinateExtent; };