Fix copy-paste issues

This commit is contained in:
Sipke Schoorstra 2023-03-12 20:07:56 +01:00
parent 08069c40d2
commit 6797c3c3e1
4 changed files with 88 additions and 57 deletions

View file

@ -19,7 +19,6 @@ export class DefaultNodeHandler implements ActivityNodeHandler {
const portModels = this.createPorts(context);
return {
id: activity.id,
shape: 'activity',
activity: activity,
activityDescriptor: activityDescriptor,

View file

@ -5,9 +5,9 @@ import {Edge, Graph, Model, Node, NodeView, Point} from '@antv/x6';
import './shapes';
import './ports';
import {ActivityNodeShape} from './shapes';
import {Activity, ActivityDeletedArgs, ActivityDescriptor, ActivitySelectedArgs, ChildActivitySelectedArgs, ContainerSelectedArgs, EditChildActivityArgs, GraphUpdatedArgs, Port, WorkflowUpdatedArgs} from '../../models';
import {Activity, ActivityDeletedArgs, ActivityDescriptor, ActivitySelectedArgs, ChildActivitySelectedArgs, ContainerSelectedArgs, EditChildActivityArgs, GraphUpdatedArgs, WorkflowUpdatedArgs} from '../../models';
import {createGraph} from './graph-factory';
import {AddActivityArgs, Connection, Flowchart, FlowchartModel, FlowchartNavigationItem, FlowchartPathItem, LayoutDirection, RenameActivityArgs, UpdateActivityArgs} from './models';
import {AddActivityArgs, Connection, Flowchart, FlowchartModel, FlowchartPathItem, LayoutDirection, RenameActivityArgs, UpdateActivityArgs} from './models';
import {NodeFactory} from "./node-factory";
import {Container} from "typedi";
import {createActivityLookup, EventBus, flatten, PortProviderRegistry, walkActivities} from "../../services";
@ -182,10 +182,10 @@ export class FlowchartComponent {
},
};
const node = this.nodeFactory.createNode(descriptor, activity, sx, sy);
graph.addNode(node, {merge: true});
adjustPortMarkupByNode(graph.getNodes().find(n => n.id == node.id));
const nodeMetadata = this.nodeFactory.createNode(descriptor, activity, sx, sy);
graph.addNode(nodeMetadata, {merge: true});
const node = graph.getNodes().find(n => n.data.id == nodeMetadata.activity.id);
adjustPortMarkupByNode(node);
await this.updateModel();
return activity;
}
@ -194,9 +194,8 @@ export class FlowchartComponent {
async updateActivity(args: UpdateActivityArgs) {
const activityId = args.id;
const originalId = args.originalId;
const nodeId = originalId;
const activity = args.activity;
const node = this.graph.getNodes().find(x => x.id == nodeId);
const node = this.graph.getNodes().find(x => x.data.id == originalId);
const nodeShape = node as ActivityNodeShape;
if (!!node) {
@ -209,7 +208,7 @@ export class FlowchartComponent {
// If the ID of the activity changed, we need to update connection references (X6 stores deep copies of data).
if (activityId !== originalId)
this.syncEdgeData(nodeId, activity);
this.syncEdgeData(originalId, activity);
// Update ports.
if (args.updatePorts) {
@ -227,9 +226,9 @@ export class FlowchartComponent {
@Method()
public async renameActivity(args: RenameActivityArgs) {
const nodeId = args.originalId;
const originalId = args.originalId;
const activity = args.activity;
const node = this.graph.getNodes().find(x => x.id == nodeId) as ActivityNodeShape;
const node = this.graph.getNodes().find(x => x.data.id == originalId) as ActivityNodeShape;
if (!node)
return;
@ -237,12 +236,12 @@ export class FlowchartComponent {
// Update the node's data with the activity.
node.setData(activity, {overwrite: true});
// Updating the node's activity property to trigger a rerender.
// Update the node's activity property to trigger a rerender.
node.activity = activity;
// If the ID of the activity changed, we need to update connection references (X6 stores deep copies of data).
if (activity.id !== nodeId)
this.syncEdgeData(nodeId, activity);
if (activity.id !== originalId)
this.syncEdgeData(originalId, activity);
}
@Method()
@ -337,7 +336,7 @@ export class FlowchartComponent {
this.getAllActivities);
graph.on('blank:click', this.onGraphClick);
graph.on('node:click', this.onNodeClick);
graph.on('node:selected', this.onNodeSelected);
graph.on('node:contextmenu', this.onNodeContextMenu);
graph.on('edge:connected', this.onEdgeConnected);
graph.on('node:moved', this.onNodeMoved);
@ -346,7 +345,6 @@ export class FlowchartComponent {
//graph.on('node:added', this.onGraphChanged);
graph.on('node:added', this.onNodeAdded);
graph.on('node:removed', this.onNodeRemoved);
graph.on('node:removed', this.onGraphChanged);
graph.on('edge:added', this.onGraphChanged);
graph.on('edge:removed', this.onGraphChanged);
graph.on('edge:connected', this.onGraphChanged);
@ -395,7 +393,6 @@ export class FlowchartComponent {
private updateModel = async () => {
const model = this.getFlowchartModel();
const currentFlowchart = await this.getCurrentFlowchartActivity();
currentFlowchart.activities = model.activities;
currentFlowchart.connections = model.connections;
currentFlowchart.start = model.start;
@ -495,7 +492,8 @@ export class FlowchartComponent {
};
private updateLookups = () => {
const activityNodes = flatten(walkActivities(this.rootActivity));
const graph = walkActivities(this.rootActivity);
const activityNodes = flatten(graph);
this.activities = activityNodes.map(x => x.activity);
this.activityLookup = createActivityLookup(activityNodes);
}
@ -516,7 +514,7 @@ export class FlowchartComponent {
return this.containerSelected.emit(args);
};
private onNodeClick = async (e: PositionEventArgs<JQuery.ClickEvent>) => {
private onNodeSelected = async (e: PositionEventArgs<JQuery.ClickEvent>) => {
const node = e.node as ActivityNodeShape;
const activityCopy = node.activity;
@ -614,22 +612,32 @@ export class FlowchartComponent {
await this.updateModel();
this.graphUpdated.emit();
}
private onNodeRemoved = (e: any) => {
private onNodeRemoved = async (e: any) => {
const activity = e.node.data as Activity;
this.activityDeleted.emit({activity});
};
private onNodeAdded = async (e: any) => {
const activity = e.node.data as Activity;
const activityDescriptor = this.getActivityDescriptor(activity.type);
activity.id = await this.generateUniqueActivityName(activityDescriptor)
e.node.activity = {...activity};
await this.onGraphChanged(e);
};
private onNodeAdded = async (e: any) => {
const node = e.node as any;
if (!node.isClone) {
const activity = {...node.getData()} as Activity;
const activityDescriptor = this.getActivityDescriptor(activity.type);
activity.id = await this.generateUniqueActivityName(activityDescriptor)
node.activity = {...activity};
}
await this.onGraphChanged(e);
};
private onToggleCanStartWorkflowClicked = (node: ActivityNodeShape) => {
const activity = node.data as Activity;
activity.canStartWorkflow = !activity.canStartWorkflow;
node.activity = {...activity};
};
private onDeleteActivityClicked = (node: ActivityNodeShape) => {
let cells = this.graph.getSelectedCells();
@ -685,10 +693,10 @@ export class FlowchartComponent {
const addedPorts = desiredPorts.filter(x => !actualPorts.some(y => getPortNameByPortId(y.id) == getPortNameByPortId(x.id)));
const removedPorts = actualPorts.filter(x => !desiredPorts.some(y => getPortNameByPortId(y.id) == getPortNameByPortId(x.id)));
if(addedPorts.length > 0 )
if (addedPorts.length > 0)
node.addPorts(addedPorts);
if(removedPorts.length > 0)
if (removedPorts.length > 0)
node.removePorts(removedPorts);
};

View file

@ -1,5 +1,5 @@
import {CellView, Graph, Node, Shape} from '@antv/x6';
import { autoOrientConnections } from '../../utils/graph';
import {autoOrientConnections} from '../../utils/graph';
import './ports';
import {Activity} from "../../models";
import {Connection} from "./models";
@ -66,8 +66,8 @@ export function createGraph(
router: {
name: 'manhattan',
args: {
startDirections: ['top','right','left','bottom'],
endDirections: ['top','right','left','bottom'],
startDirections: ['top', 'right', 'left', 'bottom'],
endDirections: ['top', 'right', 'left', 'bottom'],
},
},
// router: {
@ -93,7 +93,7 @@ export function createGraph(
return sourcePort.type !== 'in'
},
validateConnection({sourceView, targetView, sourceMagnet, targetMagnet}) {
if(!sourceMagnet || !targetMagnet) {
if (!sourceMagnet || !targetMagnet) {
return false;
}
@ -113,7 +113,6 @@ export function createGraph(
const portId = targetMagnet.getAttribute('port')!
const node = targetView.cell as Node
const port = node.getPort(portId)
return !(targetPort && targetPort.connected);
},
createEdge() {
@ -199,40 +198,62 @@ export function createGraph(
graph.bindKey(['meta+v', 'ctrl+v'], async () => {
if (!graph.isClipboardEmpty()) {
const cells = graph.paste({offset: 32});
const allActivities = [...getAllActivities()];
const cells = graph.getCellsInClipboard();
const activityCells = cells.filter(x => x.shape == 'activity');
const connectionCells = cells.filter(x => x.shape == 'elsa-edge');
const allActivities = [...getAllActivities()];
const idMap = {};
const newCells = [];
for (const cell of activityCells) {
const activity = {...cell.data} as Activity;
const clonedCell = cell.clone();
clonedCell.isClone = true;
const activity = {...clonedCell.getData()} as Activity;
const activityTypeName = activity.type;
const activityDescriptor = descriptorsStore.activityDescriptors.find(x => x.typeName == activityTypeName);
const currentId = activity.id;
const idExists = allActivities.find(x => x.id == currentId);
const idExists = !!allActivities.find(x => x.id == currentId);
if(idExists) {
if (idExists) {
const newId = await generateUniqueActivityName(allActivities, activityDescriptor);
idMap[currentId] = newId;
activity.id = newId;
}
cell.setData(activity)
clonedCell.replaceData(activity, {});
clonedCell.activity = activity;
const clonedNode = clonedCell as Node;
const position = clonedNode.getPosition();
position.x += 64;
position.y += 64;
clonedNode.setPosition(position);
allActivities.push(activity);
newCells.push(clonedCell);
}
for (const cell of connectionCells) {
const connection = {...cell.data} as Connection;
const clonedCell = cell.clone();
const connection = {...clonedCell.getData()} as Connection;
connection.source = idMap[connection.source];
connection.target = idMap[connection.target];
cell.setData(connection);
clonedCell.replaceData(connection);
newCells.push(clonedCell);
}
graph.addCell(newCells, {});
// Wait for the new cells to be rendered.
requestAnimationFrame(() => {
graph.cleanSelection();
graph.select(newCells);
});
}
return false
});
//undo redo
// undo
graph.bindKey(['meta+z', 'ctrl+z'], () => {
if (graph.history.canUndo()) {
graph.history.undo()
@ -240,6 +261,7 @@ export function createGraph(
return false
});
// redo
graph.bindKey(['meta+y', 'ctrl+y'], () => {
if (graph.history.canRedo()) {
graph.history.redo()
@ -247,7 +269,7 @@ export function createGraph(
return false
});
// select all;
// select all
graph.bindKey(['meta+a', 'ctrl+a'], () => {
const nodes = graph.getNodes()
if (nodes) {
@ -255,7 +277,7 @@ export function createGraph(
}
});
//delete
// delete
graph.bindKey('del', () => {
const cells = graph.getSelectedCells()
if (cells.length) {
@ -278,7 +300,7 @@ export function createGraph(
}
});
graph.on("node:moving", ({ node }) => {
graph.on("node:moving", ({node}) => {
autoOrientConnections(graph, node);
});

View file

@ -62,12 +62,6 @@ export function flattenList(activities: Array<ActivityNode>): Array<ActivityNode
return list;
}
export function findActivity(root: Activity, predicate: (activity: Activity) => boolean): Activity {
const graph = walkActivities(root);
const nodes = this.nodes;
return nodes.find(node => predicate(node.activity))?.activity;
}
function walkRecursive(node: ActivityNode, activity: Activity, collectedActivities: Set<Activity>, collectedNodes: Set<ActivityNode>, descriptors: Array<ActivityDescriptor>) {
const ports = getPorts(node, activity, descriptors);
@ -77,14 +71,22 @@ function walkRecursive(node: ActivityNode, activity: Activity, collectedActiviti
if (!childNode) {
childNode = {activity: port.activity, children: [], parents: [], port: port.port};
collectedNodes.add(childNode);
if (!collectedNodes.has(childNode))
collectedNodes.add(childNode);
}
if (childNode !== node) {
if(!!childNode.activity) {
childNode.parents.push(node);
node.children.push(childNode);
collectedActivities.add(port.activity)
if (!!childNode.activity) {
if (!childNode.parents.includes(node))
childNode.parents.push(node);
if (!node.children.includes(childNode))
node.children.push(childNode);
if (!collectedActivities.has(port.activity))
collectedActivities.add(port.activity);
walkRecursive(childNode, port.activity, collectedActivities, collectedNodes, descriptors);
}
}