Skip to content

Commit

Permalink
[2572] Use dynamic handles
Browse files Browse the repository at this point in the history
Bug: #2572
Signed-off-by: Michaël Charfadi <michael.charfadi@obeosoft.com>
  • Loading branch information
mcharfadi committed Nov 14, 2023
1 parent 4f6fd5d commit ad23956
Show file tree
Hide file tree
Showing 21 changed files with 550 additions and 176 deletions.
Expand Up @@ -11,6 +11,7 @@
* Obeo - initial API and implementation
*******************************************************************************/
import { Node } from 'reactflow';
import { GQLEdge } from '../graphql/subscription/edgeFragment.types';
import { GQLNode, GQLNodeStyle } from '../graphql/subscription/nodeFragment.types';

export interface IConvertEngine {
Expand All @@ -25,6 +26,7 @@ export interface INodeConverterHandler {
gqlNode: GQLNode<GQLNodeStyle>,
parentNode: GQLNode<GQLNodeStyle> | null,
isBorderNode: boolean,
nodes: Node[]
nodes: Node[],
gqlEdges: GQLEdge[]
): void;
}
Expand Up @@ -18,9 +18,10 @@ import {
GQLViewModifier,
} from '../graphql/subscription/nodeFragment.types';
import { BorderNodePositon } from '../renderer/DiagramRenderer.types';
import { ConnectionHandle } from '../renderer/handles/ConnectionHandles.types';
import { IconLabelNodeData } from '../renderer/node/IconsLabelNode.types';
import { convertLabelStyle } from './convertDiagram';
import { IConvertEngine, INodeConverterHandler } from './ConvertEngine.types';
import { convertLabelStyle } from './convertDiagram';

const defaultPosition: XYPosition = { x: 0, y: 0 };

Expand All @@ -41,6 +42,8 @@ const toIconLabelNode = (
labelEditable,
} = gqlNode;

const connectionHandles: ConnectionHandle[] = [];

const data: IconLabelNodeData = {
targetObjectId,
targetObjectLabel,
Expand All @@ -55,6 +58,7 @@ const toIconLabelNode = (
borderNodePosition: isBorderNode ? BorderNodePositon.WEST : null,
faded: state === GQLViewModifier.Faded,
labelEditable: labelEditable,
connectionHandles,
};

if (insideLabel) {
Expand Down
Expand Up @@ -11,19 +11,23 @@
* Obeo - initial API and implementation
*******************************************************************************/
import { Node, XYPosition } from 'reactflow';
import { GQLEdge } from '../graphql/subscription/edgeFragment.types';
import { GQLImageNodeStyle, GQLNode, GQLNodeStyle, GQLViewModifier } from '../graphql/subscription/nodeFragment.types';
import { BorderNodePositon } from '../renderer/DiagramRenderer.types';
import { ConnectionHandle } from '../renderer/handles/ConnectionHandles.types';
import { ImageNodeData } from '../renderer/node/ImageNode.types';
import { IConvertEngine, INodeConverterHandler } from './ConvertEngine.types';
import { convertLabelStyle } from './convertDiagram';
import { AlignmentMap } from './convertDiagram.types';
import { IConvertEngine, INodeConverterHandler } from './ConvertEngine.types';
import { convertHandles } from './convertHandles';

const defaultPosition: XYPosition = { x: 0, y: 0 };

const toImageNode = (
gqlNode: GQLNode<GQLImageNodeStyle>,
gqlParentNode: GQLNode<GQLNodeStyle> | null,
isBorderNode: boolean
isBorderNode: boolean,
gqlEdges: GQLEdge[]
): Node<ImageNodeData> => {
const {
targetObjectId,
Expand All @@ -37,6 +41,8 @@ const toImageNode = (
labelEditable,
} = gqlNode;

const connectionHandles: ConnectionHandle[] = convertHandles(gqlNode, gqlEdges);

const data: ImageNodeData = {
targetObjectId,
targetObjectLabel,
Expand All @@ -50,6 +56,7 @@ const toImageNode = (
borderNodePosition: isBorderNode ? BorderNodePositon.WEST : null,
labelEditable,
positionDependentRotation: style.positionDependentRotation,
connectionHandles,
};

if (insideLabel) {
Expand Down Expand Up @@ -106,9 +113,10 @@ export class ImageNodeConverterHandler implements INodeConverterHandler {
gqlNode: GQLNode<GQLImageNodeStyle>,
parentNode: GQLNode<GQLNodeStyle> | null,
isBorderNode: boolean,
nodes: Node[]
nodes: Node[],
gqlEdges: GQLEdge[]
) {
nodes.push(toImageNode(gqlNode, parentNode, isBorderNode));
nodes.push(toImageNode(gqlNode, parentNode, isBorderNode, gqlEdges));

convertEngine.convertNodes(gqlNode.borderNodes ?? [], gqlNode, nodes);
convertEngine.convertNodes(gqlNode.childNodes ?? [], gqlNode, nodes);
Expand Down
Expand Up @@ -11,24 +11,28 @@
* Obeo - initial API and implementation
*******************************************************************************/
import { Node, XYPosition } from 'reactflow';
import { GQLEdge } from '../graphql/subscription/edgeFragment.types';
import {
GQLNode,
GQLNodeStyle,
GQLRectangularNodeStyle,
GQLViewModifier,
} from '../graphql/subscription/nodeFragment.types';
import { BorderNodePositon } from '../renderer/DiagramRenderer.types';
import { ConnectionHandle } from '../renderer/handles/ConnectionHandles.types';
import { ListNodeData } from '../renderer/node/ListNode.types';
import { IConvertEngine, INodeConverterHandler } from './ConvertEngine.types';
import { convertLabelStyle } from './convertDiagram';
import { AlignmentMap } from './convertDiagram.types';
import { convertHandles } from './convertHandles';

const defaultPosition: XYPosition = { x: 0, y: 0 };

const toListNode = (
gqlNode: GQLNode<GQLRectangularNodeStyle>,
gqlParentNode: GQLNode<GQLNodeStyle> | null,
isBorderNode: boolean
isBorderNode: boolean,
gqlEdges: GQLEdge[]
): Node<ListNodeData> => {
const {
targetObjectId,
Expand All @@ -42,6 +46,8 @@ const toListNode = (
labelEditable,
} = gqlNode;

const connectionHandles: ConnectionHandle[] = convertHandles(gqlNode, gqlEdges);

const data: ListNodeData = {
targetObjectId,
targetObjectLabel,
Expand All @@ -59,6 +65,7 @@ const toListNode = (
borderNodePosition: isBorderNode ? BorderNodePositon.WEST : null,
faded: state === GQLViewModifier.Faded,
labelEditable,
connectionHandles,
};

if (insideLabel) {
Expand Down Expand Up @@ -120,9 +127,10 @@ export class ListNodeConverterHandler implements INodeConverterHandler {
gqlNode: GQLNode<GQLRectangularNodeStyle>,
parentNode: GQLNode<GQLNodeStyle> | null,
isBorderNode: boolean,
nodes: Node[]
nodes: Node[],
gqlEdges: GQLEdge[]
) {
nodes.push(toListNode(gqlNode, parentNode, isBorderNode));
nodes.push(toListNode(gqlNode, parentNode, isBorderNode, gqlEdges));
convertEngine.convertNodes(gqlNode.borderNodes ?? [], gqlNode, nodes);
convertEngine.convertNodes(gqlNode.childNodes ?? [], gqlNode, nodes);
}
Expand Down
Expand Up @@ -11,24 +11,28 @@
* Obeo - initial API and implementation
*******************************************************************************/
import { Node, XYPosition } from 'reactflow';
import { GQLEdge } from '../graphql/subscription/edgeFragment.types';
import {
GQLNode,
GQLNodeStyle,
GQLRectangularNodeStyle,
GQLViewModifier,
} from '../graphql/subscription/nodeFragment.types';
import { BorderNodePositon } from '../renderer/DiagramRenderer.types';
import { ConnectionHandle } from '../renderer/handles/ConnectionHandles.types';
import { RectangularNodeData } from '../renderer/node/RectangularNode.types';
import { IConvertEngine, INodeConverterHandler } from './ConvertEngine.types';
import { convertLabelStyle } from './convertDiagram';
import { AlignmentMap } from './convertDiagram.types';
import { convertHandles } from './convertHandles';

const defaultPosition: XYPosition = { x: 0, y: 0 };

const toRectangularNode = (
gqlNode: GQLNode<GQLRectangularNodeStyle>,
gqlParentNode: GQLNode<GQLNodeStyle> | null,
isBorderNode: boolean
isBorderNode: boolean,
gqlEdges: GQLEdge[]
): Node<RectangularNodeData> => {
const {
targetObjectId,
Expand All @@ -42,6 +46,8 @@ const toRectangularNode = (
labelEditable,
} = gqlNode;

const connectionHandles: ConnectionHandle[] = convertHandles(gqlNode, gqlEdges);

const data: RectangularNodeData = {
targetObjectId,
targetObjectLabel,
Expand All @@ -60,6 +66,7 @@ const toRectangularNode = (
isBorderNode: isBorderNode,
borderNodePosition: isBorderNode ? BorderNodePositon.EAST : null,
labelEditable,
connectionHandles,
};

if (insideLabel) {
Expand Down Expand Up @@ -121,9 +128,10 @@ export class RectangleNodeConverterHandler implements INodeConverterHandler {
gqlNode: GQLNode<GQLRectangularNodeStyle>,
parentNode: GQLNode<GQLNodeStyle> | null,
isBorderNode: boolean,
nodes: Node[]
nodes: Node[],
gqlEdges: GQLEdge[]
) {
nodes.push(toRectangularNode(gqlNode, parentNode, isBorderNode));
nodes.push(toRectangularNode(gqlNode, parentNode, isBorderNode, gqlEdges));
convertEngine.convertNodes(gqlNode.borderNodes ?? [], gqlNode, nodes);
convertEngine.convertNodes(gqlNode.childNodes ?? [], gqlNode, nodes);
}
Expand Down
Expand Up @@ -105,7 +105,7 @@ export const convertDiagram = (
if (nodeConverterHandler) {
const isBorderNode: boolean = !!parentNode?.borderNodes?.map((borderNode) => borderNode.id).includes(node.id);

nodeConverterHandler.handle(this, node, parentNode, isBorderNode, nodes);
nodeConverterHandler.handle(this, node, parentNode, isBorderNode, nodes, gqlDiagram.edges);
}
});
},
Expand All @@ -118,8 +118,10 @@ export const convertDiagram = (

const nodeId2Depth = new Map<string, number>();
nodes.forEach((node) => nodeId2Depth.set(node.id, nodeDepth(nodeId2node, node.id)));

let usedHandles: string[] = [];
const edges: Edge[] = gqlDiagram.edges.map((gqlEdge) => {
const sourceNode: Node<NodeData> | undefined = nodeId2node.get(gqlEdge.sourceId);
const targetNode: Node<NodeData> | undefined = nodeId2node.get(gqlEdge.targetId);
const data: MultiLabelEdgeData = {
targetObjectId: gqlEdge.targetObjectId,
targetObjectKind: gqlEdge.targetObjectKind,
Expand All @@ -138,6 +140,17 @@ export const convertDiagram = (
data.endLabel = convertEdgeLabel(gqlEdge.endLabel);
}

const sourceHandle = sourceNode?.data.connectionHandles
.filter((handle) => handle.type === 'source')
.find((handle) => !usedHandles.find((usedHandles) => usedHandles === handle.id));

const targetHandle = targetNode?.data.connectionHandles
.filter((handle) => handle.type === 'target')
.find((handle) => !usedHandles.find((usedHandles) => usedHandles === handle.id));
if (sourceHandle?.id && targetHandle?.id) {
usedHandles.push(sourceHandle?.id, targetHandle.id);
}

return {
id: gqlEdge.id,
type: 'multiLabelEdge',
Expand All @@ -152,6 +165,10 @@ export const convertDiagram = (
},
data,
hidden: gqlEdge.state === GQLViewModifier.Hidden,
sourceHandle: sourceHandle?.id,
targetHandle: targetHandle?.id,
sourceNode: sourceNode,
targetNode: targetNode,
};
});

Expand Down
@@ -0,0 +1,62 @@
/*******************************************************************************
* 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 { Position } from 'reactflow';
import { ConnectionHandle } from '../renderer/handles/ConnectionHandles.types';

const numberSourceHandles = (handles: ConnectionHandle[]): string => {
return handles.filter((handle) => handle.type === 'source').length.toString();
};
const numberTargetHandles = (handles: ConnectionHandle[]): string => {
return handles.filter((handle) => handle.type === 'target').length.toString();
};
export const convertHandles = (gqlNode, gqlEdges) => {
const connectionHandles: ConnectionHandle[] = [];
gqlEdges.forEach((edge) => {
if (edge.sourceId === gqlNode.id) {
connectionHandles.push({
id: `handle--source--${gqlNode.id}--${numberSourceHandles(connectionHandles)}`,
edgeId: edge.id,
nodeId: gqlNode.id,
position: Position.Right,
type: 'source',
});
}

if (edge.targetId === gqlNode.id) {
connectionHandles.push({
id: `handle--target--${gqlNode.id}--${numberTargetHandles(connectionHandles)}`,
edgeId: edge.id,
nodeId: gqlNode.id,
position: Position.Left,
type: 'target',
});
}
});
connectionHandles.push({
id: `handle--source--${gqlNode.id}--${numberSourceHandles(connectionHandles)}`,
edgeId: '',
nodeId: gqlNode.id,
position: Position.Right,
type: 'source',
});

connectionHandles.push({
id: `handle--target--${gqlNode.id}--${numberTargetHandles(connectionHandles)}`,
edgeId: '',
nodeId: gqlNode.id,
position: Position.Left,
type: 'target',
});

return connectionHandles;
};
Expand Up @@ -45,6 +45,7 @@ import { useDrop } from './drop/useDrop';
import { useDropNode } from './dropNode/useDropNode';
import { edgeTypes } from './edge/EdgeTypes';
import { MultiLabelEdgeData } from './edge/MultiLabelEdge.types';
import { useHandleChange } from './handles/useHandleChange';
import { useLayout } from './layout/useLayout';
import { DiagramNodeType } from './node/NodeTypes.types';
import { useNodeType } from './node/useNodeType';
Expand Down Expand Up @@ -81,6 +82,7 @@ export const DiagramRenderer = ({ diagramRefreshedEventPayload, selection, setSe
const { reconnectEdge } = useReconnectEdge();
const { onDrop, onDragOver } = useDrop();
const { onBorderChange } = useBorderChange();
const { onHandleChange } = useHandleChange();
const { getNodeTypes } = useNodeType();

const [nodes, setNodes, onNodesChange] = useNodesState<NodeData>([]);
Expand Down Expand Up @@ -187,7 +189,7 @@ export const DiagramRenderer = ({ diagramRefreshedEventPayload, selection, setSe

const handleNodesChange: OnNodesChange = (changes: NodeChange[]) => {
onNodesChange(onBorderChange(changes));

onNodesChange(onHandleChange(changes));
const selectionEntries: SelectionEntry[] = changes
.filter(isNodeSelectChange)
.filter((change) => change.selected)
Expand Down
Expand Up @@ -15,6 +15,7 @@ import { Selection } from '@eclipse-sirius/sirius-components-core';
import { Edge, Node } from 'reactflow';
import { GQLDiagramRefreshedEventPayload } from '../graphql/subscription/diagramEventSubscription.types';
import { MultiLabelEdgeData } from './edge/MultiLabelEdge.types';
import { ConnectionHandle } from './handles/ConnectionHandles.types';
import { DiagramNodeType } from './node/NodeTypes.types';

export type FitViewLifecycle = 'neverRendered' | 'shouldFitview' | 'viewfit';
Expand Down Expand Up @@ -60,6 +61,7 @@ export interface NodeData {
borderNodePosition: BorderNodePositon | null;
labelEditable: boolean;
style: React.CSSProperties;
connectionHandles: ConnectionHandle[];
}

export enum BorderNodePositon {
Expand Down

0 comments on commit ad23956

Please sign in to comment.