From 9f5461e278aa45da8b9107edd2a2c67e547fae73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20G=C3=BCran?= Date: Fri, 2 Dec 2022 12:34:04 +0100 Subject: [PATCH 1/5] Partially implemented inflexible ports(cases) --- .../src/utils/graph.ts | 401 ++++++++++-------- 1 file changed, 234 insertions(+), 167 deletions(-) diff --git a/src/designer/elsa-workflows-designer/src/utils/graph.ts b/src/designer/elsa-workflows-designer/src/utils/graph.ts index 0eb008c17..dd2f61d3e 100644 --- a/src/designer/elsa-workflows-designer/src/utils/graph.ts +++ b/src/designer/elsa-workflows-designer/src/utils/graph.ts @@ -1,198 +1,265 @@ import { Edge, Graph, Node } from "@antv/x6"; import { PortManager } from "@antv/x6/lib/model/port"; import { Connection } from "../modules/flowchart/models"; -import {v4 as uuid} from 'uuid'; +import { v4 as uuid } from 'uuid'; import { Activity } from "../models"; +import {ActivityNode} from "../services"; export function adjustPortMarkupByNode(node: Node) { - node.getPorts().forEach(port => { - if(port.type == 'out'){ - node.setPortProp(port.id, "attrs", { - circle: { - r: 5, - magnet: true, - stroke: '#fff', - strokeWidth: 2, - fill: '#3c82f6', - }, - text: { - fontSize: 12, - fill: '#888', - }, - }); - } - else { - node.setPortProp(port.id, "attrs", { - circle: { - r: 5, - magnet: true, - stroke: '#3c82f6', - strokeWidth: 2, - fill: '#fff', - }, - text: { - fontSize: 12, - fill: '#888', - }, - }); - } - }); -} - -function createEdge(connection: Connection): Edge.Metadata { - return { - shape: 'elsa-edge', - zIndex: -1, - data: connection, - source: connection.source, - target: connection.target, - sourcePort: connection.sourcePort, - targetPort: connection.targetPort - }; -} - -function hasPortAnEdge(graph: Graph, port: PortManager.PortMetadata) { - return graph.getEdges().some(({data}) => data.sourcePort == port.id || data.targetPort == port.id); -} - -function findMatchingPortForEdge(node: Node, position: string, portType: string, portName: string) { - return node.getPorts().find(p => p.position == position && p.type == portType && (portName == "In" || p.attrs?.text?.text == portName)); -} - -function getPortNameByPortId(portId: string) { - return portId.split('_')[1] == 'null' ? null : portId.split('_')[1]; -} - -function updatePortsAndEdges( - graph: Graph, - selectedNode: Node, - newSelectedNodePosition: "left" | "right" | "top" | "bottom", - neighbourNode: Node, - newNeighbourNodePosition: "left" | "right" | "top" | "bottom" -) -{ - updatePortsAndEdgesOfNodeCouple(graph, selectedNode, neighbourNode, newSelectedNodePosition, newNeighbourNodePosition); - updatePortsAndEdgesOfNodeCouple(graph, neighbourNode, selectedNode, newNeighbourNodePosition, newSelectedNodePosition); - - adjustPortMarkupByNode(selectedNode); - adjustPortMarkupByNode(neighbourNode); -} - -function updatePortsAndEdgesOfNodeCouple(graph: Graph, sourceNode: Node, targetNode: Node, newSourceNodePosition: string, newTargetNodePosition: string) -{ - const edge = graph.model.getEdges().find(({ data }) => data.source == sourceNode.id && data.target == targetNode.id); - - if (edge != null) { - const sourceNodePort = sourceNode.getPort(edge.data.sourcePort) ?? sourceNode.getPorts().find(p => p.type == "out" && getPortNameByPortId(p.id) == getPortNameByPortId(edge.data.sourcePort)); - const targetNodePort = targetNode.getPort(edge.data.targetPort) ?? targetNode.getPorts().find(p => p.type == "in"); - - if(sourceNodePort.position != newSourceNodePosition || targetNodePort.position != newTargetNodePosition) { - graph.removeEdge(edge); - - const newSourceNodePortId = updatePort(graph, sourceNode, sourceNodePort, newSourceNodePosition); - const newTargetNodePortId = updatePort(graph, targetNode, targetNodePort, newTargetNodePosition); - - graph.addEdge(createEdge({ - source: sourceNode.id, - target: targetNode.id, - sourcePort: newSourceNodePortId ?? sourceNodePort.id, - targetPort: newTargetNodePortId ?? targetNodePort.id - })); - } + node.getPorts().forEach(port => { + if (port.type == 'out') { + node.setPortProp(port.id, "attrs", { + circle: { + r: 5, + magnet: true, + stroke: '#fff', + strokeWidth: 2, + fill: '#3c82f6', + }, + text: { + fontSize: 12, + fill: '#888', + }, + }); } -} - -function updatePort(graph: Graph, node: Node, nodePort: PortManager.PortMetadata, newNodePosition: string) -{ - let newNodePortId = null; - if (nodePort.position != newNodePosition) { - - if (!hasPortAnEdge(graph, nodePort)) { - node.removePort(nodePort); - } - - const matchingPort = findMatchingPortForEdge(node, newNodePosition, nodePort.type, getPortNameByPortId(nodePort.id)); - - if(matchingPort == null){ - newNodePortId = createNewPort(nodePort, node, newNodePosition); - } - else{ - newNodePortId = matchingPort.id; - } - } - return newNodePortId; -} - -function createNewPort(nodePort: PortManager.PortMetadata, node: Node, newNodePosition: string) { - const newNodePortId = uuid() + '_' + getPortNameByPortId(nodePort.id); - - node.addPort({ - ...nodePort, - group: newNodePosition, - id: newNodePortId, - position: newNodePosition, - type: nodePort.type - }); - return newNodePortId; -} - -export function autoOrientPortsAndEdges(graph: Graph, selectedNode: Node) -{ - const selectedCenter = selectedNode.getBBox().center; - const neighbors = graph.getNeighbors(selectedNode); - neighbors.forEach((neighbourNode) => { - const neighborCenter = neighbourNode.getBBox().center; - const dx = selectedCenter.x - neighborCenter.x; - const dy = selectedCenter.y - neighborCenter.y; - if (dx >= 0 && dy >= 0) { - if (dx > dy) { - updatePortsAndEdges(graph, selectedNode, "left", neighbourNode as Node, "right"); - } else { - updatePortsAndEdges(graph, selectedNode, "top", neighbourNode as Node, "bottom"); - } - } else if (dx >= 0 && dy <= 0) { - if (dx > -dy) { - updatePortsAndEdges(graph, selectedNode, "left", neighbourNode as Node, "right"); - } else { - updatePortsAndEdges(graph, selectedNode, "bottom", neighbourNode as Node, "top"); - } - } else if (dx <= 0 && dy >= 0) { - if (-dx > dy) { - updatePortsAndEdges(graph, selectedNode, "right", neighbourNode as Node, "left"); - } else { - updatePortsAndEdges(graph, selectedNode, "top", neighbourNode as Node, "bottom"); - } - } else if (dx <= 0 && dy <= 0) { - if (dx > dy) { - updatePortsAndEdges(graph, selectedNode, "right", neighbourNode as Node, "left"); - } else { - updatePortsAndEdges(graph, selectedNode, "bottom", neighbourNode as Node, "left"); - } + else { + node.setPortProp(port.id, "attrs", { + circle: { + r: 5, + magnet: true, + stroke: '#3c82f6', + strokeWidth: 2, + fill: '#fff', + }, + text: { + fontSize: 12, + fill: '#888', + }, + }); } }); } -export function rebuildGraph(graph: Graph) -{ +function createEdge(connection: Connection): Edge.Metadata { + return { + shape: 'elsa-edge', + zIndex: -1, + data: connection, + source: connection.source, + target: connection.target, + sourcePort: connection.sourcePort, + targetPort: connection.targetPort + }; +} + +function hasPortAnEdge(graph: Graph, port: PortManager.PortMetadata) { + return graph.getEdges().some(({ data }) => data.sourcePort == port.id || data.targetPort == port.id); +} + +function findMatchingPortForEdge(node: Node, position: string, portType: string, portName: string) { + return node.getPorts().find(p => p.position == position && p.type == portType && getPortNameByPortId(p.id) == portName); +} + +function getPortNameByPortId(portId: string) { + return portId.includes('_') ? (portId.split('_')[1] == 'null' ? null : portId.split('_')[1]) : portId; +} + +function findOutgoingEdgesByPortId(graph: Graph, portId: string): Edge[] { + return graph.model.getEdges().filter(({ data }) => data.sourcePort == portId); +} + +function updatePortsAndEdges( + graph: Graph, + selectedNode: Node, + newSelectedNodePosition: "left" | "right" | "top" | "bottom", + neighbourNode: Node, + newNeighbourNodePosition: "left" | "right" | "top" | "bottom" +) { + updatePortsAndEdgeOfNodeCouple(graph, selectedNode, neighbourNode, newSelectedNodePosition, newNeighbourNodePosition); + updatePortsAndEdgeOfNodeCouple(graph, neighbourNode, selectedNode, newNeighbourNodePosition, newSelectedNodePosition); + + adjustPortMarkupByNode(selectedNode); + adjustPortMarkupByNode(neighbourNode); +} + +function updatePortsAndEdgeOfNodeCouple(graph: Graph, sourceNode: Node, targetNode: Node, newSourceNodePosition: string, newTargetNodePosition: string) { + const edge = graph.model.getEdges().find(({ data }) => data.source == sourceNode.id && data.target == targetNode.id); + + if (edge != null) { + const sourcePortOfConnection = edge.data.sourcePort; + + if(isNewCalculationNeededForInflexiblePort(graph, sourcePortOfConnection)){ + // const outgoingEdges = findOutgoingEdgesByPortId(graph, sourcePortOfConnection); + // const targetNodes = graph.getNodes().filter(node => outgoingEdges.map(edge => edge.data.target).includes(node.id)); + // const nodeCouplesWithPositions = calculatePositionsForInflexibleNode(sourceNode, targetNodes); + // console.log(nodeCouplesWithPositions); + + // nodeCouplesWithPositions.forEach(couple => { + // update(graph, couple.sourceNode, couple.targetNode, couple.sourceNodePosition, couple.targetNodePosition); + // }); + + return; + }; + + update(graph, sourceNode, targetNode, newSourceNodePosition, newTargetNodePosition); + } +} + +function update(graph: Graph, sourceNode: Node, targetNode: Node, newSourceNodePosition: string, newTargetNodePosition: string) { + const edge = graph.model.getEdges().find(({ data }) => data.source == sourceNode.id && data.target == targetNode.id); + + const sourceNodePort = sourceNode.getPort(edge.data.sourcePort) ?? sourceNode.getPorts().find(p => p.type == "out" && getPortNameByPortId(p.id) == getPortNameByPortId(edge.data.sourcePort)); + const targetNodePort = targetNode.getPort(edge.data.targetPort) ?? targetNode.getPorts().find(p => p.type == "in" && getPortNameByPortId(p.id) == getPortNameByPortId(edge.data.targetPort)); + + if (sourceNodePort?.position != newSourceNodePosition || targetNodePort?.position != newTargetNodePosition) { + graph.removeEdge(edge); + + const newSourceNodePortId = updatePort(graph, sourceNode, sourceNodePort, newSourceNodePosition); + const newTargetNodePortId = updatePort(graph, targetNode, targetNodePort, newTargetNodePosition); + + graph.addEdge(createEdge({ + source: sourceNode.id, + target: targetNode.id, + sourcePort: newSourceNodePortId ?? sourceNodePort.id, + targetPort: newTargetNodePortId ?? targetNodePort.id + })); + } +} + +function calculatePositionsForInflexibleNode(sourceNode: Node, targetNodes: Node[]): +{ sourceNode: Node, sourceNodePosition: "left" | "right" | "top" | "bottom"; targetNode: Node, targetNodePosition: "left" | "right" | "top" | "bottom"; }[] { + const sourceNodeCenter = sourceNode.getBBox().center; + const dxAverageForTargetNodes = targetNodes.map(node => node.getBBox().center.x).reduce((a, b) => a + b, 0); + const dyAverageForTargetNodes = targetNodes.map(node => node.getBBox().center.y).reduce((a, b) => a + b, 0); + + const sourceNodeWithNewPosition = { node: sourceNode, position: calculatePositionsOfNodeCouple(sourceNodeCenter.x, sourceNodeCenter.y, dxAverageForTargetNodes, dyAverageForTargetNodes).selectedNodePosition }; + return targetNodes.map((targetNode) => { + return { + sourceNode: sourceNodeWithNewPosition.node, + sourceNodePosition: sourceNodeWithNewPosition.position, + targetNode: targetNode, + targetNodePosition: calculatePositionsOfNodeCouple(sourceNode.getBBox().center.x, sourceNode.getBBox().center.y, targetNode.getBBox().center.x, targetNode.getBBox().center.y).neighbourNodePosition + } + }); +} + +function isNewCalculationNeededForInflexiblePort(graph: Graph, inflexiblePort: any) { + const portName = getPortNameByPortId(inflexiblePort); + if (portName != null && portName != "Done") { + const outgoingEdges = findOutgoingEdgesByPortId(graph, inflexiblePort); + if (outgoingEdges.length > 1) { + return true; + } + } + return false; +} + +function updatePort(graph: Graph, node: Node, nodePort: PortManager.PortMetadata, newNodePosition: string) { + let newNodePortId = null; + if (nodePort?.position != newNodePosition) { + + if (!hasPortAnEdge(graph, nodePort)) { + node.removePort(nodePort); + } + + const matchingPort = findMatchingPortForEdge(node, newNodePosition, nodePort.type, getPortNameByPortId(nodePort.id)); + + if (matchingPort == null) { + newNodePortId = createNewPort(nodePort, node, newNodePosition); + } + else { + newNodePortId = matchingPort.id; + } + } + return newNodePortId; +} + +function createNewPort(nodePort: PortManager.PortMetadata, node: Node, newNodePosition: string) { + const newNodePortId = uuid() + '_' + getPortNameByPortId(nodePort.id); + + node.addPort({ + ...nodePort, + group: newNodePosition, + id: newNodePortId, + position: newNodePosition, + type: nodePort.type + }); + return newNodePortId; +} + +export function autoOrientPortsAndEdges(graph: Graph, selectedNode: Node) { + const neighbors = graph.getNeighbors(selectedNode); + const selectedCenter = selectedNode.getBBox().center; + const nodeGroupWithPositions = neighbors.map((neighbourNode) => { + return { + ...calculatePositionsOfNodeCouple(selectedCenter.x, selectedCenter.y, neighbourNode.getBBox().center.x, neighbourNode.getBBox().center.y), + selectedNode: selectedNode, + neighbourNode: neighbourNode + } + }); + nodeGroupWithPositions.forEach(group => updatePortsAndEdges(graph, group.selectedNode, group.selectedNodePosition, group.neighbourNode as Node, group.neighbourNodePosition)); +} + +export function rebuildGraph(graph: Graph) { graph.getNodes().forEach((node: Node) => { autoOrientPortsAndEdges(graph, node); + adjustPortMarkupByNode(node); }); } export function adjustConnectionsInRequestModel(root: Activity) { - if(root.connections.length > 0){ + if (root.connections?.length > 0) { root.connections.forEach((connection: { sourcePort: string; targetPort: string; }) => { connection.sourcePort = getPortNameByPortId(connection.sourcePort); connection.targetPort = getPortNameByPortId(connection.targetPort); }); } + let activitiesWithConnections = root.activities?.filter(act => act.body?.connections?.length > 0); + activitiesWithConnections.forEach(activity => { + adjustConnectionsInRequestModel(activity.body); + }); } export function adjustConnectionsInResponseModel(root: Activity) { - if(root.connections.length > 0){ + if (root.connections.length > 0) { root.connections.forEach((connection: { sourcePort: string; targetPort: string; }) => { connection.sourcePort = uuid() + '_' + connection.sourcePort; connection.targetPort = uuid() + '_' + connection.targetPort; }); } + let activitiesWithConnections = root.activities?.filter(act => act.body?.connections?.length > 0); + activitiesWithConnections.forEach(activity => { + adjustConnectionsInResponseModel(activity.body); + }); } + +function calculatePositionsOfNodeCouple(selectedNodeX: number, selectedNodeY: number, neighbourNodeX: number, neighbourNodeY: number): { selectedNodePosition: "left" | "right" | "top" | "bottom"; neighbourNodePosition: "left" | "right" | "top" | "bottom"; } { + const dx = selectedNodeX - neighbourNodeX; + const dy = selectedNodeY - neighbourNodeY; + if (dx >= 0 && dy >= 0) { + if (dx > dy) { + return {selectedNodePosition: "left", neighbourNodePosition: "right"}; + } else { + return {selectedNodePosition: "top", neighbourNodePosition: "bottom"}; + } + } else if (dx >= 0 && dy <= 0) { + if (dx > -dy) { + return {selectedNodePosition: "left", neighbourNodePosition: "right"}; + } else { + return {selectedNodePosition: "bottom", neighbourNodePosition: "top"}; + } + } else if (dx <= 0 && dy >= 0) { + if (-dx > dy) { + return {selectedNodePosition: "right", neighbourNodePosition: "left"}; + } else { + return {selectedNodePosition: "top", neighbourNodePosition: "bottom"}; + } + } else if (dx <= 0 && dy <= 0) { + if (dx > dy) { + return {selectedNodePosition: "right", neighbourNodePosition: "left"}; + } else { + return {selectedNodePosition: "bottom", neighbourNodePosition: "left"}; + } + } +} + From 0450b822c3e971040abb2e7eaf6f21bcd4f42d1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20G=C3=BCran?= Date: Fri, 2 Dec 2022 12:34:46 +0100 Subject: [PATCH 2/5] Fixed issue with rebuilding graph on inner flowcharts --- .../src/modules/flowchart/flowchart.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/designer/elsa-workflows-designer/src/modules/flowchart/flowchart.tsx b/src/designer/elsa-workflows-designer/src/modules/flowchart/flowchart.tsx index c61bad211..0ba570fd5 100644 --- a/src/designer/elsa-workflows-designer/src/modules/flowchart/flowchart.tsx +++ b/src/designer/elsa-workflows-designer/src/modules/flowchart/flowchart.tsx @@ -259,10 +259,6 @@ export class FlowchartComponent implements ContainerActivityComponent { @Method() async import(root: Activity): Promise { await this.importInternal(root); - - rebuildGraph(this.graph); - - await this.scrollToStart(); } @Method() @@ -280,6 +276,7 @@ export class FlowchartComponent implements ContainerActivityComponent { const currentActivityId = this.currentPath[this.currentPath.length - 1].activityId; const currentActivity = this.activityLookup[currentActivityId]; const parentActivity = this.activityLookup[parentActivityId] as Flowchart; + const parentActivityDescriptor = descriptorsStore.activityDescriptors.find(x => x.typeName == parentActivity.type); const indexInParent = currentActivity.activities?.findIndex(x => x == parentActivity); const portName = e.detail.port.name; @@ -390,6 +387,7 @@ export class FlowchartComponent implements ContainerActivityComponent { const currentNavigationItem = currentPath[currentPath.length - 1]; const currentPortName = currentNavigationItem?.portName; const currentScope = this.activityLookup[currentNavigationItem.activityId] as Activity; + const currentScopeDescriptor = this.getActivityDescriptor(currentScope.type); if (!!currentPortName) { @@ -450,6 +448,9 @@ export class FlowchartComponent implements ContainerActivityComponent { this.graph.freeze(); this.graph.fromJSON(model, {silent: false}); this.graph.unfreeze(); + + rebuildGraph(this.graph); + await this.scrollToStart(); }; private getFlowchartModel = (): FlowchartModel => { From 57face151da647b08125f9254e44ef29756422f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20G=C3=BCran?= Date: Fri, 2 Dec 2022 12:35:58 +0100 Subject: [PATCH 3/5] Fixed the issue with server-client port model difference --- .../modules/workflow-definitions/services/api.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/designer/elsa-workflows-designer/src/modules/workflow-definitions/services/api.ts b/src/designer/elsa-workflows-designer/src/modules/workflow-definitions/services/api.ts index e50c51cf3..addef2b8c 100644 --- a/src/designer/elsa-workflows-designer/src/modules/workflow-definitions/services/api.ts +++ b/src/designer/elsa-workflows-designer/src/modules/workflow-definitions/services/api.ts @@ -5,6 +5,7 @@ import {Service} from "typedi"; import {ElsaApiClientProvider} from "../../../services"; import {AxiosResponse} from "axios"; import { adjustConnectionsInRequestModel, adjustConnectionsInResponseModel } from '../../../utils/graph'; +import { cloneDeep } from '@antv/x6/lib/util/object/object'; @Service() export class WorkflowDefinitionsApi { @@ -45,12 +46,15 @@ export class WorkflowDefinitionsApi { } async post(request: SaveWorkflowDefinitionRequest): Promise { - //TODO: Written as a workaround for different server and client models. + //TODO: Written as a workaround for different server and client models. //To be deleted after the port model on backend is updated. - adjustConnectionsInRequestModel(request.root); + const requestClone = cloneDeep(request); + adjustConnectionsInRequestModel(requestClone.root); const httpClient = await this.getHttpClient(); - const response = await httpClient.post('workflow-definitions', request); + const response = await httpClient.post('workflow-definitions', requestClone); + + adjustConnectionsInResponseModel(response.data.root); return response.data; } @@ -142,6 +146,11 @@ export class WorkflowDefinitionsApi { } const workflowDefinition = response.data; + + //TODO: Written as a workaround for different server and client models. + //To be deleted after the connection model on backend is updated. + adjustConnectionsInResponseModel(workflowDefinition.root); + return {workflowDefinition: workflowDefinition}; } From b941ca89cc399194dd690af21aec56f9eaa5c5b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCrkan=20G=C3=BCran?= Date: Fri, 2 Dec 2022 19:46:48 +0100 Subject: [PATCH 4/5] Finished inflexible port implementation for cases --- .../src/modules/flowchart/graph-factory.ts | 4 +- .../src/utils/graph.ts | 372 +++++++++--------- 2 files changed, 186 insertions(+), 190 deletions(-) diff --git a/src/designer/elsa-workflows-designer/src/modules/flowchart/graph-factory.ts b/src/designer/elsa-workflows-designer/src/modules/flowchart/graph-factory.ts index 622f5fc0f..7d9b6f884 100644 --- a/src/designer/elsa-workflows-designer/src/modules/flowchart/graph-factory.ts +++ b/src/designer/elsa-workflows-designer/src/modules/flowchart/graph-factory.ts @@ -1,5 +1,5 @@ import {CellView, Graph, Node, Shape} from '@antv/x6'; -import { autoOrientPortsAndEdges } from '../../utils/graph'; +import { autoOrientConnections } from '../../utils/graph'; import './ports'; import {Activity} from "../../models"; import {Connection} from "./models"; @@ -287,7 +287,7 @@ export function createGraph( }); graph.on("node:moving", ({ node }) => { - autoOrientPortsAndEdges(graph, node); + autoOrientConnections(graph, node); }); return graph; diff --git a/src/designer/elsa-workflows-designer/src/utils/graph.ts b/src/designer/elsa-workflows-designer/src/utils/graph.ts index dd2f61d3e..b8b8745c3 100644 --- a/src/designer/elsa-workflows-designer/src/utils/graph.ts +++ b/src/designer/elsa-workflows-designer/src/utils/graph.ts @@ -3,7 +3,189 @@ import { PortManager } from "@antv/x6/lib/model/port"; import { Connection } from "../modules/flowchart/models"; import { v4 as uuid } from 'uuid'; import { Activity } from "../models"; -import {ActivityNode} from "../services"; + +export function rebuildGraph(graph: Graph) { + graph.getNodes().forEach((node: Node) => { + autoOrientConnections(graph, node); + adjustPortMarkupByNode(node); + }); +} + +export function autoOrientConnections(graph: Graph, selectedNode: Node) { + const neighbors = graph.getNeighbors(selectedNode); + const selectedCenter = selectedNode.getBBox().center; + const nodeCouplesWithPositions = neighbors.map((neighbourNode) => { + return { + ...calculatePortPositionsOfNodeCouple(selectedCenter.x, selectedCenter.y, neighbourNode.getBBox().center.x, neighbourNode.getBBox().center.y), + selectedNode: selectedNode, + neighbourNode: neighbourNode + } + }); + nodeCouplesWithPositions.forEach(couple => updatePortsWithNewPositions(graph, couple.selectedNode, couple.portPositionOfSelectedNode, couple.neighbourNode as Node, couple.portPositionOfNeighbourNode)); +} + +function updatePortsWithNewPositions( + graph: Graph, + selectedNode: Node, + portPositionOfSelectedNode: "left" | "right" | "top" | "bottom", + neighbourNode: Node, + portPositionOfNeighbourNode: "left" | "right" | "top" | "bottom" +) { + updatePortsAndEdgeOfNodeCouple(graph, selectedNode, neighbourNode, portPositionOfSelectedNode, portPositionOfNeighbourNode); + updatePortsAndEdgeOfNodeCouple(graph, neighbourNode, selectedNode, portPositionOfNeighbourNode, portPositionOfSelectedNode); + + adjustPortMarkupByNode(selectedNode); + adjustPortMarkupByNode(neighbourNode); +} + +function calculatePositionsForInflexibleNode(sourceNode: Node, targetNodes: Node[]): +{ sourceNode: Node, sourceNodePosition: "left" | "right" | "top" | "bottom"; targetNode: Node, targetNodePosition: "left" | "right" | "top" | "bottom"; }[] { + const sourceNodeCenter = sourceNode.getBBox().center; + const dxAverageForTargetNodes = targetNodes.map(node => node.getBBox().center.x).reduce((a, b) => a + b, 0) / targetNodes.length; + const dyAverageForTargetNodes = targetNodes.map(node => node.getBBox().center.y).reduce((a, b) => a + b, 0) / targetNodes.length; + + const sourcePortWithNewPosition = { node: sourceNode, position: calculatePortPositionsOfNodeCouple(sourceNodeCenter.x, sourceNodeCenter.y, dxAverageForTargetNodes, dyAverageForTargetNodes).portPositionOfSelectedNode }; + return targetNodes.map((targetNode) => { + return { + sourceNode: sourcePortWithNewPosition.node, + sourceNodePosition: sourcePortWithNewPosition.position, + targetNode: targetNode, + targetNodePosition: calculatePortPositionsOfNodeCouple(sourceNode.getBBox().center.x, sourceNode.getBBox().center.y, targetNode.getBBox().center.x, targetNode.getBBox().center.y).portPositionOfNeighbourNode + } + }); +} + +function calculatePortPositionsOfNodeCouple(selectedNodeX: number, selectedNodeY: number, neighbourNodeX: number, neighbourNodeY: number): { portPositionOfSelectedNode: "left" | "right" | "top" | "bottom"; portPositionOfNeighbourNode: "left" | "right" | "top" | "bottom"; } { + const dx = selectedNodeX - neighbourNodeX; + const dy = selectedNodeY - neighbourNodeY; + if (dx >= 0 && dy >= 0) { + if (dx > dy) { + return {portPositionOfSelectedNode: "left", portPositionOfNeighbourNode: "right"}; + } else { + return {portPositionOfSelectedNode: "top", portPositionOfNeighbourNode: "bottom"}; + } + } else if (dx >= 0 && dy <= 0) { + if (dx > -dy) { + return {portPositionOfSelectedNode: "left", portPositionOfNeighbourNode: "right"}; + } else { + return {portPositionOfSelectedNode: "bottom", portPositionOfNeighbourNode: "top"}; + } + } else if (dx <= 0 && dy >= 0) { + if (-dx > dy) { + return {portPositionOfSelectedNode: "right", portPositionOfNeighbourNode: "left"}; + } else { + return {portPositionOfSelectedNode: "top", portPositionOfNeighbourNode: "bottom"}; + } + } else if (dx <= 0 && dy <= 0) { + if (dx > dy) { + return {portPositionOfSelectedNode: "right", portPositionOfNeighbourNode: "left"}; + } else { + return {portPositionOfSelectedNode: "bottom", portPositionOfNeighbourNode: "left"}; + } + } +} + +function updatePortsAndEdgeOfNodeCouple(graph: Graph, sourceNode: Node, targetNode: Node, portPositionOfSourceNode: string, portPositionOfTargetNode: string) { + const edge = graph.model.getEdges().find(({ data }) => data.source == sourceNode.id && data.target == targetNode.id); + + if (edge != null) { + const sourcePortOfConnection = edge.data.sourcePort; + if(isNewCalculationNeededForInflexiblePort(graph, sourceNode, sourcePortOfConnection)){ + const outgoingEdges = findOutgoingEdges(graph, sourceNode, sourcePortOfConnection); + const targetNodes = graph.getNodes().filter(node => outgoingEdges.map(edge => edge.data.target).includes(node.id)); + const nodeCouplesWithPositions = calculatePositionsForInflexibleNode(sourceNode, targetNodes); + + nodeCouplesWithPositions.forEach(couple => { + updatePortsAndEdge(graph, couple.sourceNode, couple.targetNode, couple.sourceNodePosition, couple.targetNodePosition); + }); + + return; + }; + + updatePortsAndEdge(graph, sourceNode, targetNode, portPositionOfSourceNode, portPositionOfTargetNode); + } +} + +function isNewCalculationNeededForInflexiblePort(graph: Graph, sourceNode: Node, inflexiblePort: any) { + const portName = getPortNameByPortId(inflexiblePort); + if (portName != null && portName != "Done") { + const outgoingEdges = findOutgoingEdges(graph, sourceNode, inflexiblePort); + if (outgoingEdges.length > 1) { + return true; + } + } + return false; +} + +function updatePortsAndEdge(graph: Graph, sourceNode: Node, targetNode: Node, newSourceNodePosition: string, newTargetNodePosition: string) { + const edge = graph.model.getEdges().find(({ data }) => data.source == sourceNode.id && data.target == targetNode.id); + + const sourceNodePort = sourceNode.getPort(edge.data.sourcePort) ?? sourceNode.getPorts().find(p => p.type == "out" && getPortNameByPortId(p.id) == getPortNameByPortId(edge.data.sourcePort)); + const targetNodePort = targetNode.getPort(edge.data.targetPort) ?? targetNode.getPorts().find(p => p.type == "in" && getPortNameByPortId(p.id) == getPortNameByPortId(edge.data.targetPort)); + + if (sourceNode.getPort(edge.data.sourcePort) == null || targetNode.getPort(edge.data.targetPort) == null || sourceNodePort?.position != newSourceNodePosition || targetNodePort?.position != newTargetNodePosition) { + graph.removeEdge(edge); + + const newSourceNodePortId = updatePort(graph, sourceNode, sourceNodePort, newSourceNodePosition); + const newTargetNodePortId = updatePort(graph, targetNode, targetNodePort, newTargetNodePosition); + + graph.addEdge(createEdge({ + source: sourceNode.id, + target: targetNode.id, + sourcePort: newSourceNodePortId ?? sourceNodePort.id, + targetPort: newTargetNodePortId ?? targetNodePort.id + })); + } +} + +function hasPortAnEdge(graph: Graph, port: PortManager.PortMetadata) { + return graph.getEdges().some(({ data }) => data.sourcePort == port.id || data.targetPort == port.id); +} + +function findMatchingPortForEdge(node: Node, position: string, portType: string, portName: string) { + return node.getPorts().find(p => p.position == position && p.type == portType && getPortNameByPortId(p.id) == portName); +} + +function getPortNameByPortId(portId: string) { + return portId.includes('_') ? (portId.split('_')[1] == 'null' ? null : portId.split('_')[1]) : portId; +} + +function findOutgoingEdges(graph: Graph, node: Node, portId: string): Edge[] { + return graph.model.getEdges().filter(({ data }) => data.source == node.id && getPortNameByPortId(data.sourcePort) == getPortNameByPortId(portId)); +} + +function updatePort(graph: Graph, node: Node, nodePort: PortManager.PortMetadata, newPortPosition: string) { + let newNodePortId = null; + if (nodePort?.position != newPortPosition) { + + if (!hasPortAnEdge(graph, nodePort)) { + node.removePort(nodePort); + } + + const matchingPort = findMatchingPortForEdge(node, newPortPosition, nodePort.type, getPortNameByPortId(nodePort.id)); + + if (matchingPort == null) { + newNodePortId = createNewPort(nodePort, node, newPortPosition); + } + else { + newNodePortId = matchingPort.id; + } + } + return newNodePortId; +} + +function createNewPort(nodePort: PortManager.PortMetadata, node: Node, newPortPosition: string) { + const newNodePortId = uuid() + '_' + getPortNameByPortId(nodePort.id); + + node.addPort({ + ...nodePort, + group: newPortPosition, + id: newNodePortId, + position: newPortPosition, + type: nodePort.type + }); + return newNodePortId; +} export function adjustPortMarkupByNode(node: Node) { node.getPorts().forEach(port => { @@ -52,161 +234,6 @@ function createEdge(connection: Connection): Edge.Metadata { }; } -function hasPortAnEdge(graph: Graph, port: PortManager.PortMetadata) { - return graph.getEdges().some(({ data }) => data.sourcePort == port.id || data.targetPort == port.id); -} - -function findMatchingPortForEdge(node: Node, position: string, portType: string, portName: string) { - return node.getPorts().find(p => p.position == position && p.type == portType && getPortNameByPortId(p.id) == portName); -} - -function getPortNameByPortId(portId: string) { - return portId.includes('_') ? (portId.split('_')[1] == 'null' ? null : portId.split('_')[1]) : portId; -} - -function findOutgoingEdgesByPortId(graph: Graph, portId: string): Edge[] { - return graph.model.getEdges().filter(({ data }) => data.sourcePort == portId); -} - -function updatePortsAndEdges( - graph: Graph, - selectedNode: Node, - newSelectedNodePosition: "left" | "right" | "top" | "bottom", - neighbourNode: Node, - newNeighbourNodePosition: "left" | "right" | "top" | "bottom" -) { - updatePortsAndEdgeOfNodeCouple(graph, selectedNode, neighbourNode, newSelectedNodePosition, newNeighbourNodePosition); - updatePortsAndEdgeOfNodeCouple(graph, neighbourNode, selectedNode, newNeighbourNodePosition, newSelectedNodePosition); - - adjustPortMarkupByNode(selectedNode); - adjustPortMarkupByNode(neighbourNode); -} - -function updatePortsAndEdgeOfNodeCouple(graph: Graph, sourceNode: Node, targetNode: Node, newSourceNodePosition: string, newTargetNodePosition: string) { - const edge = graph.model.getEdges().find(({ data }) => data.source == sourceNode.id && data.target == targetNode.id); - - if (edge != null) { - const sourcePortOfConnection = edge.data.sourcePort; - - if(isNewCalculationNeededForInflexiblePort(graph, sourcePortOfConnection)){ - // const outgoingEdges = findOutgoingEdgesByPortId(graph, sourcePortOfConnection); - // const targetNodes = graph.getNodes().filter(node => outgoingEdges.map(edge => edge.data.target).includes(node.id)); - // const nodeCouplesWithPositions = calculatePositionsForInflexibleNode(sourceNode, targetNodes); - // console.log(nodeCouplesWithPositions); - - // nodeCouplesWithPositions.forEach(couple => { - // update(graph, couple.sourceNode, couple.targetNode, couple.sourceNodePosition, couple.targetNodePosition); - // }); - - return; - }; - - update(graph, sourceNode, targetNode, newSourceNodePosition, newTargetNodePosition); - } -} - -function update(graph: Graph, sourceNode: Node, targetNode: Node, newSourceNodePosition: string, newTargetNodePosition: string) { - const edge = graph.model.getEdges().find(({ data }) => data.source == sourceNode.id && data.target == targetNode.id); - - const sourceNodePort = sourceNode.getPort(edge.data.sourcePort) ?? sourceNode.getPorts().find(p => p.type == "out" && getPortNameByPortId(p.id) == getPortNameByPortId(edge.data.sourcePort)); - const targetNodePort = targetNode.getPort(edge.data.targetPort) ?? targetNode.getPorts().find(p => p.type == "in" && getPortNameByPortId(p.id) == getPortNameByPortId(edge.data.targetPort)); - - if (sourceNodePort?.position != newSourceNodePosition || targetNodePort?.position != newTargetNodePosition) { - graph.removeEdge(edge); - - const newSourceNodePortId = updatePort(graph, sourceNode, sourceNodePort, newSourceNodePosition); - const newTargetNodePortId = updatePort(graph, targetNode, targetNodePort, newTargetNodePosition); - - graph.addEdge(createEdge({ - source: sourceNode.id, - target: targetNode.id, - sourcePort: newSourceNodePortId ?? sourceNodePort.id, - targetPort: newTargetNodePortId ?? targetNodePort.id - })); - } -} - -function calculatePositionsForInflexibleNode(sourceNode: Node, targetNodes: Node[]): -{ sourceNode: Node, sourceNodePosition: "left" | "right" | "top" | "bottom"; targetNode: Node, targetNodePosition: "left" | "right" | "top" | "bottom"; }[] { - const sourceNodeCenter = sourceNode.getBBox().center; - const dxAverageForTargetNodes = targetNodes.map(node => node.getBBox().center.x).reduce((a, b) => a + b, 0); - const dyAverageForTargetNodes = targetNodes.map(node => node.getBBox().center.y).reduce((a, b) => a + b, 0); - - const sourceNodeWithNewPosition = { node: sourceNode, position: calculatePositionsOfNodeCouple(sourceNodeCenter.x, sourceNodeCenter.y, dxAverageForTargetNodes, dyAverageForTargetNodes).selectedNodePosition }; - return targetNodes.map((targetNode) => { - return { - sourceNode: sourceNodeWithNewPosition.node, - sourceNodePosition: sourceNodeWithNewPosition.position, - targetNode: targetNode, - targetNodePosition: calculatePositionsOfNodeCouple(sourceNode.getBBox().center.x, sourceNode.getBBox().center.y, targetNode.getBBox().center.x, targetNode.getBBox().center.y).neighbourNodePosition - } - }); -} - -function isNewCalculationNeededForInflexiblePort(graph: Graph, inflexiblePort: any) { - const portName = getPortNameByPortId(inflexiblePort); - if (portName != null && portName != "Done") { - const outgoingEdges = findOutgoingEdgesByPortId(graph, inflexiblePort); - if (outgoingEdges.length > 1) { - return true; - } - } - return false; -} - -function updatePort(graph: Graph, node: Node, nodePort: PortManager.PortMetadata, newNodePosition: string) { - let newNodePortId = null; - if (nodePort?.position != newNodePosition) { - - if (!hasPortAnEdge(graph, nodePort)) { - node.removePort(nodePort); - } - - const matchingPort = findMatchingPortForEdge(node, newNodePosition, nodePort.type, getPortNameByPortId(nodePort.id)); - - if (matchingPort == null) { - newNodePortId = createNewPort(nodePort, node, newNodePosition); - } - else { - newNodePortId = matchingPort.id; - } - } - return newNodePortId; -} - -function createNewPort(nodePort: PortManager.PortMetadata, node: Node, newNodePosition: string) { - const newNodePortId = uuid() + '_' + getPortNameByPortId(nodePort.id); - - node.addPort({ - ...nodePort, - group: newNodePosition, - id: newNodePortId, - position: newNodePosition, - type: nodePort.type - }); - return newNodePortId; -} - -export function autoOrientPortsAndEdges(graph: Graph, selectedNode: Node) { - const neighbors = graph.getNeighbors(selectedNode); - const selectedCenter = selectedNode.getBBox().center; - const nodeGroupWithPositions = neighbors.map((neighbourNode) => { - return { - ...calculatePositionsOfNodeCouple(selectedCenter.x, selectedCenter.y, neighbourNode.getBBox().center.x, neighbourNode.getBBox().center.y), - selectedNode: selectedNode, - neighbourNode: neighbourNode - } - }); - nodeGroupWithPositions.forEach(group => updatePortsAndEdges(graph, group.selectedNode, group.selectedNodePosition, group.neighbourNode as Node, group.neighbourNodePosition)); -} - -export function rebuildGraph(graph: Graph) { - graph.getNodes().forEach((node: Node) => { - autoOrientPortsAndEdges(graph, node); - adjustPortMarkupByNode(node); - }); -} - export function adjustConnectionsInRequestModel(root: Activity) { if (root.connections?.length > 0) { root.connections.forEach((connection: { sourcePort: string; targetPort: string; }) => { @@ -231,35 +258,4 @@ export function adjustConnectionsInResponseModel(root: Activity) { activitiesWithConnections.forEach(activity => { adjustConnectionsInResponseModel(activity.body); }); -} - -function calculatePositionsOfNodeCouple(selectedNodeX: number, selectedNodeY: number, neighbourNodeX: number, neighbourNodeY: number): { selectedNodePosition: "left" | "right" | "top" | "bottom"; neighbourNodePosition: "left" | "right" | "top" | "bottom"; } { - const dx = selectedNodeX - neighbourNodeX; - const dy = selectedNodeY - neighbourNodeY; - if (dx >= 0 && dy >= 0) { - if (dx > dy) { - return {selectedNodePosition: "left", neighbourNodePosition: "right"}; - } else { - return {selectedNodePosition: "top", neighbourNodePosition: "bottom"}; - } - } else if (dx >= 0 && dy <= 0) { - if (dx > -dy) { - return {selectedNodePosition: "left", neighbourNodePosition: "right"}; - } else { - return {selectedNodePosition: "bottom", neighbourNodePosition: "top"}; - } - } else if (dx <= 0 && dy >= 0) { - if (-dx > dy) { - return {selectedNodePosition: "right", neighbourNodePosition: "left"}; - } else { - return {selectedNodePosition: "top", neighbourNodePosition: "bottom"}; - } - } else if (dx <= 0 && dy <= 0) { - if (dx > dy) { - return {selectedNodePosition: "right", neighbourNodePosition: "left"}; - } else { - return {selectedNodePosition: "bottom", neighbourNodePosition: "left"}; - } - } -} - +} \ No newline at end of file From 06aee918c353b498ec51f8a8add2b53bed3902c2 Mon Sep 17 00:00:00 2001 From: LevVadim Date: Mon, 5 Dec 2022 12:17:15 +0200 Subject: [PATCH 5/5] fix monaco editor script view resize 24437 (#3503) --- .../shared/monaco-editor/monaco-editor.scss | 12 ++++++++++-- .../shared/monaco-editor/monaco-editor.tsx | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/designer/elsa-workflows-designer/src/components/shared/monaco-editor/monaco-editor.scss b/src/designer/elsa-workflows-designer/src/components/shared/monaco-editor/monaco-editor.scss index 16c349dd9..442da9aaf 100644 --- a/src/designer/elsa-workflows-designer/src/components/shared/monaco-editor/monaco-editor.scss +++ b/src/designer/elsa-workflows-designer/src/components/shared/monaco-editor/monaco-editor.scss @@ -9,10 +9,18 @@ left: 0; top: 0; width: 100%; - height: 100%; - max-height: 100% !important; + max-width: 100%; + min-height: 130px; + max-height: 200px !important; margin: 0; padding: 0; + --tw-border-opacity: 1; + border: 1px solid rgb(209 213 219 / var(--tw-border-opacity)); + -moz-appearance: textfield-multiline; + -webkit-appearance: textarea; + overflow: auto; + resize: both; + min-width: 100%; } .monaco-editor { diff --git a/src/designer/elsa-workflows-designer/src/components/shared/monaco-editor/monaco-editor.tsx b/src/designer/elsa-workflows-designer/src/components/shared/monaco-editor/monaco-editor.tsx index a2e9867d6..a057e0331 100644 --- a/src/designer/elsa-workflows-designer/src/components/shared/monaco-editor/monaco-editor.tsx +++ b/src/designer/elsa-workflows-designer/src/components/shared/monaco-editor/monaco-editor.tsx @@ -213,9 +213,9 @@ export class MonacoEditor { const padding = this.padding || 'pt-1.5 pl-1'; return ( -
this.container = el} class={`monaco-editor-container ${padding}`}/> +
this.container = el} class={`rounded-md monaco-editor-container ${padding}`}/> ) }