Fix merge of auto rotating ports
This commit is contained in:
parent
6ada301b1f
commit
c9f1021a4b
|
|
@ -68,7 +68,7 @@ export namespace Components {
|
|||
}
|
||||
interface ElsaCanvas {
|
||||
"addActivity": (args: AddActivityArgs) => Promise<Activity>;
|
||||
"autoLayout": () => Promise<void>;
|
||||
"autoLayout": (direction: "TB" | "BT" | "LR" | "RL") => Promise<void>;
|
||||
"exportGraph": () => Promise<Activity>;
|
||||
"getRootComponent": () => Promise<ContainerActivityComponent>;
|
||||
"importGraph": (root: Activity) => Promise<void>;
|
||||
|
|
@ -121,7 +121,7 @@ export namespace Components {
|
|||
}
|
||||
interface ElsaFlowchart {
|
||||
"addActivity": (args: AddActivityArgs) => Promise<Activity>;
|
||||
"autoLayout": () => Promise<void>;
|
||||
"autoLayout": (direction: "TB" | "BT" | "LR" | "RL") => Promise<void>;
|
||||
"export": () => Promise<Activity>;
|
||||
"getCurrentLevel": () => Promise<Activity>;
|
||||
"getGraph": () => Promise<Graph>;
|
||||
|
|
@ -267,7 +267,7 @@ export namespace Components {
|
|||
"workflowDefinition"?: WorkflowDefinition;
|
||||
}
|
||||
interface ElsaWorkflowDefinitionEditorToolbar {
|
||||
"autoLayout": () => Promise<void>;
|
||||
"autoLayout": (direction: "TB" | "BT" | "LR" | "RL") => Promise<void>;
|
||||
"zoomToFit": () => Promise<void>;
|
||||
}
|
||||
interface ElsaWorkflowDefinitionEditorToolbox {
|
||||
|
|
@ -955,7 +955,7 @@ declare namespace LocalJSX {
|
|||
"workflowDefinition"?: WorkflowDefinition;
|
||||
}
|
||||
interface ElsaWorkflowDefinitionEditorToolbar {
|
||||
"autoLayout"?: () => Promise<void>;
|
||||
"autoLayout"?: (direction: "TB" | "BT" | "LR" | "RL") => Promise<void>;
|
||||
"zoomToFit"?: () => Promise<void>;
|
||||
}
|
||||
interface ElsaWorkflowDefinitionEditorToolbox {
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@ export class Canvas {
|
|||
}
|
||||
|
||||
@Method()
|
||||
public async autoLayout(): Promise<void> {
|
||||
return await this.root.autoLayout();
|
||||
public async autoLayout(direction: "TB" | "BT" | "LR" | "RL"): Promise<void> {
|
||||
return await this.root.autoLayout(direction);
|
||||
}
|
||||
|
||||
@Method()
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ export class DefaultActivityTemplate {
|
|||
const iconCssClass = this.displayTypeIsPicker ? 'px-2' : 'px-4';
|
||||
|
||||
if (!icon)
|
||||
return <div class={iconCssClass}></div>;
|
||||
return undefined;
|
||||
|
||||
return (
|
||||
<div class={`${iconCssClass} py-1`}>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {Container, Service} from "typedi"
|
|||
import {ActivityNodeHandler, CreateUINodeContext} from "./activity-node-handler";
|
||||
import {PortProviderContext, PortProviderRegistry} from "../../services";
|
||||
import {PortMode} from "../../models";
|
||||
import {v4 as uuid} from 'uuid';
|
||||
|
||||
@Service()
|
||||
export class DefaultNodeHandler implements ActivityNodeHandler {
|
||||
|
|
@ -27,27 +28,31 @@ export class DefaultNodeHandler implements ActivityNodeHandler {
|
|||
if (outPorts.length == 1)
|
||||
outPorts[0].displayName = null;
|
||||
|
||||
const inPortModels = inPorts.map(x => ({
|
||||
id: x.name,
|
||||
group: 'in',
|
||||
const leftPortModels = inPorts.map((x) => ({
|
||||
id: uuid() + '_' + x.name,
|
||||
group: 'left',
|
||||
attrs: !!x.displayName ? {
|
||||
text: {
|
||||
text: x.displayName
|
||||
}
|
||||
} : null
|
||||
},
|
||||
} : null,
|
||||
type:'in',
|
||||
position:'left'
|
||||
}));
|
||||
|
||||
const outPortModels = outPorts.map(x => ({
|
||||
id: x.name,
|
||||
group: 'out',
|
||||
const rightPortModels = outPorts.map((x) => ({
|
||||
id: uuid() + '_' + x.name,
|
||||
group: 'right',
|
||||
attrs: {
|
||||
text: {
|
||||
text: x.displayName
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
type: 'out',
|
||||
position: 'right'
|
||||
}));
|
||||
|
||||
const portModels = [...inPortModels, ...outPortModels];
|
||||
const portModels = [...leftPortModels, ...rightPortModels];
|
||||
|
||||
return {
|
||||
id: activity.id,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ import PositionEventArgs = NodeView.PositionEventArgs;
|
|||
import FromJSONData = Model.FromJSONData;
|
||||
import PointLike = Point.PointLike;
|
||||
import {generateUniqueActivityName} from "../../utils/generate-activity-name";
|
||||
import {DagreLayout, OutNode} from '@antv/layout';
|
||||
import { DagreLayout, OutNode} from '@antv/layout';
|
||||
import { adjustPortMarkupByNode, rebuildGraph } from '../../utils/graph';
|
||||
import {WorkflowDefinition} from "../workflow-definitions/models/entities";
|
||||
import FlowchartTunnel, {FlowchartState} from "./state";
|
||||
import WorkflowDefinitionTunnel, {WorkflowDefinitionState} from "../../state/workflow-definition-state";
|
||||
|
|
@ -125,11 +126,10 @@ export class FlowchartComponent implements ContainerActivityComponent {
|
|||
}
|
||||
|
||||
@Method()
|
||||
async autoLayout() {
|
||||
|
||||
async autoLayout(direction: "TB" | "BT" | "LR" | "RL") {
|
||||
const dagreLayout = new DagreLayout({
|
||||
type: 'dagre',
|
||||
rankdir: 'TB',
|
||||
rankdir: direction,
|
||||
align: 'UL',
|
||||
ranksep: 30,
|
||||
nodesep: 15,
|
||||
|
|
@ -161,8 +161,7 @@ export class FlowchartComponent implements ContainerActivityComponent {
|
|||
this.updateActivity({id: activity.id, originalId: activity.id, activity: activity});
|
||||
});
|
||||
|
||||
await this.import(this.activity);
|
||||
await this.scrollToStart();
|
||||
this.import(this.activity);
|
||||
}
|
||||
|
||||
@Method()
|
||||
|
|
@ -191,6 +190,8 @@ export class FlowchartComponent implements ContainerActivityComponent {
|
|||
|
||||
const node = this.nodeFactory.createNode(descriptor, activity, sx, sy);
|
||||
graph.addNode(node, {merge: true});
|
||||
|
||||
adjustPortMarkupByNode(graph.getNodes().find(n => n.id == node.id));
|
||||
await this.updateModel();
|
||||
return activity;
|
||||
}
|
||||
|
|
@ -511,13 +512,6 @@ export class FlowchartComponent implements ContainerActivityComponent {
|
|||
return this.activity;
|
||||
|
||||
const activity = this.activityLookup[currentItem.activityId] as Flowchart;
|
||||
|
||||
if (activity == null) {
|
||||
alert("Critical");
|
||||
location.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
const activityDescriptor = descriptorsStore.activityDescriptors.find(x => x.typeName == activity.type);
|
||||
|
||||
if (activityDescriptor.isContainer)
|
||||
|
|
@ -718,9 +712,6 @@ export class FlowchartComponent implements ContainerActivityComponent {
|
|||
};
|
||||
|
||||
private onNavigateHierarchy = async (e: CustomEvent<FlowchartNavigationItem>) => {
|
||||
|
||||
debugger;
|
||||
|
||||
const item = e.detail;
|
||||
const activityId = item.activityId;
|
||||
let activity = this.activityLookup[activityId];
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ import {Activity} from "../../models";
|
|||
import {Connection} from "./models";
|
||||
import descriptorsStore from "../../data/descriptors-store";
|
||||
import {generateUniqueActivityName} from "../../utils/generate-activity-name";
|
||||
import {Hash} from "../../utils";
|
||||
import {createActivityLookup} from "../../services";
|
||||
|
||||
export function createGraph(
|
||||
container: HTMLElement,
|
||||
|
|
@ -70,8 +68,8 @@ export function createGraph(
|
|||
router: {
|
||||
name: 'manhattan',
|
||||
args: {
|
||||
startDirections: ['right'],
|
||||
endDirections: ['left'],
|
||||
startDirections: ['top','right','left','bottom'],
|
||||
endDirections: ['top','right','left','bottom'],
|
||||
},
|
||||
},
|
||||
// router: {
|
||||
|
|
@ -91,23 +89,34 @@ export function createGraph(
|
|||
snap: {
|
||||
radius: 20,
|
||||
},
|
||||
validateMagnet({magnet}) {
|
||||
return magnet.getAttribute('port-group') !== 'in'
|
||||
validateMagnet({view, magnet}) {
|
||||
const node = view.cell as Node;
|
||||
const sourcePort = node.getPort(magnet.getAttribute('port'));
|
||||
return sourcePort.type !== 'in'
|
||||
},
|
||||
validateConnection({sourceView, targetView, sourceMagnet, targetMagnet}) {
|
||||
if (!sourceMagnet || sourceMagnet.getAttribute('port-group') === 'in') {
|
||||
if(!sourceMagnet || !targetMagnet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sourceNode = sourceView.cell as Node;
|
||||
const sourcePort = sourceNode.getPort(sourceMagnet.getAttribute('port'));
|
||||
|
||||
const targetNode = targetView.cell as Node;
|
||||
const targetPort = targetNode.getPort(targetMagnet.getAttribute('port'));
|
||||
|
||||
if (sourcePort.type === 'in') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (!targetMagnet || targetMagnet.getAttribute('port-group') !== 'in') {
|
||||
if (targetPort.type !== 'in') {
|
||||
return false
|
||||
}
|
||||
|
||||
const portId = targetMagnet.getAttribute('port')!
|
||||
const node = targetView.cell as Node
|
||||
const port = node.getPort(portId)
|
||||
return !(port && port.connected);
|
||||
|
||||
return !(targetPort && targetPort.connected);
|
||||
},
|
||||
createEdge() {
|
||||
return graph.createEdge({
|
||||
|
|
@ -282,4 +291,4 @@ export function createGraph(
|
|||
});
|
||||
|
||||
return graph;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,35 +1,4 @@
|
|||
import {Graph, Line, Path} from "@antv/x6";
|
||||
import {toResult} from "@antv/x6/lib/registry/port-layout/util";
|
||||
|
||||
Graph.registerPortLayout('dynamicOut', (portsPositionArgs, elemBBox) => {
|
||||
return portsPositionArgs.map((_, index) => {
|
||||
|
||||
const portCount = portsPositionArgs.length;
|
||||
const ratio = (index + 0.5) / portCount;
|
||||
const p1 = portCount <= 3 ? elemBBox.getTopRight() : elemBBox.getBottomLeft();
|
||||
const p2 = portCount <= 3 ? elemBBox.getBottomRight() : elemBBox.getBottomRight();
|
||||
const line = new Line(p1, p2)
|
||||
const p = line.pointAt(ratio);
|
||||
|
||||
return toResult(p.round(), 0, {});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
Graph.registerPortLayout('dynamicIn', (portsPositionArgs, elemBBox) => {
|
||||
return portsPositionArgs.map((_, index) => {
|
||||
|
||||
const portCount = portsPositionArgs.length;
|
||||
const ratio = (index + 0.5) / portCount;
|
||||
const p1 = portCount <= 3 ? elemBBox.getTopLeft() : elemBBox.getTopLeft();
|
||||
const p2 = portCount <= 3 ? elemBBox.getBottomLeft() : elemBBox.getTopRight();
|
||||
const line = new Line(p1, p2)
|
||||
const p = line.pointAt(ratio);
|
||||
|
||||
return toResult(p.round(), 0, {});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
Graph.registerConnector(
|
||||
'elsa-connector',
|
||||
|
|
|
|||
|
|
@ -114,8 +114,8 @@ export class ActivityNodeShape extends Shape.HTML {
|
|||
ActivityNodeShape.config({
|
||||
ports: {
|
||||
groups: {
|
||||
in: {
|
||||
position: 'dynamicIn',
|
||||
left: {
|
||||
position: 'left',
|
||||
attrs: {
|
||||
circle: {
|
||||
r: 5,
|
||||
|
|
@ -135,15 +135,57 @@ ActivityNodeShape.config({
|
|||
},
|
||||
},
|
||||
},
|
||||
out: {
|
||||
position: 'dynamicOut',
|
||||
right: {
|
||||
position: 'right',
|
||||
attrs: {
|
||||
circle: {
|
||||
r: 5,
|
||||
magnet: true,
|
||||
stroke: '#fff',
|
||||
stroke: '#3c82f6',
|
||||
strokeWidth: 2,
|
||||
fill: '#3c82f6',
|
||||
fill: '#fff',
|
||||
},
|
||||
text: {
|
||||
fontSize: 12,
|
||||
fill: '#888',
|
||||
},
|
||||
},
|
||||
label: {
|
||||
position: {
|
||||
name: 'outside',
|
||||
},
|
||||
},
|
||||
},
|
||||
top: {
|
||||
position: 'top',
|
||||
attrs: {
|
||||
circle: {
|
||||
r: 5,
|
||||
magnet: true,
|
||||
stroke: '#3c82f6',
|
||||
strokeWidth: 2,
|
||||
fill: '#fff',
|
||||
},
|
||||
text: {
|
||||
fontSize: 12,
|
||||
fill: '#888',
|
||||
},
|
||||
},
|
||||
label: {
|
||||
position: {
|
||||
name: 'outside',
|
||||
},
|
||||
},
|
||||
},
|
||||
bottom: {
|
||||
position: 'bottom',
|
||||
attrs: {
|
||||
circle: {
|
||||
r: 5,
|
||||
magnet: true,
|
||||
stroke: '#3c82f6',
|
||||
strokeWidth: 2,
|
||||
fill: '#fff',
|
||||
},
|
||||
text: {
|
||||
fontSize: 12,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ export class WorkflowDefinitionEditor {
|
|||
private canvas: HTMLElsaCanvasElement;
|
||||
private container: HTMLDivElement;
|
||||
private toolbox: HTMLElsaWorkflowDefinitionEditorToolboxElement;
|
||||
private readonly emitActivityChangedDebounced: (e: ActivityPropertyChangedEventArgs) => void;
|
||||
private readonly updateModelDebounced: () => void;
|
||||
private readonly saveChangesDebounced: () => void;
|
||||
private readonly workflowDefinitionApi: WorkflowDefinitionsApi;
|
||||
|
||||
|
|
@ -40,6 +42,8 @@ export class WorkflowDefinitionEditor {
|
|||
this.pluginRegistry = Container.get(PluginRegistry);
|
||||
this.activityNameFormatter = Container.get(ActivityNameFormatter);
|
||||
this.portProviderRegistry = Container.get(PortProviderRegistry);
|
||||
this.emitActivityChangedDebounced = debounce(this.emitActivityChanged, 100);
|
||||
this.updateModelDebounced = debounce(this.updateModel, 10);
|
||||
this.saveChangesDebounced = debounce(this.saveChanges, 1000);
|
||||
this.workflowDefinitionApi = Container.get(WorkflowDefinitionsApi);
|
||||
}
|
||||
|
|
@ -110,7 +114,6 @@ export class WorkflowDefinitionEditor {
|
|||
await this.updateWorkflowDefinition(workflowDefinition);
|
||||
await this.canvas.importGraph(workflowDefinition.root);
|
||||
await this.eventBus.emit(WorkflowEditorEventTypes.WorkflowDefinition.Imported, this, {workflowDefinition});
|
||||
await this.canvas.scrollToStart();
|
||||
}
|
||||
|
||||
// Updates the workflow definition without importing it into the designer.
|
||||
|
|
@ -225,9 +228,9 @@ export class WorkflowDefinitionEditor {
|
|||
});
|
||||
};
|
||||
|
||||
private onZoomToFit = async () => await this.canvas.zoomToFit()
|
||||
private onZoomToFit = async () => await this.canvas.zoomToFit();
|
||||
|
||||
private onAutoLayout = async () => await this.canvas.autoLayout()
|
||||
private onAutoLayout = async (direction: "TB" | "BT" | "LR" | "RL") => await this.canvas.autoLayout(direction);
|
||||
|
||||
private onActivityUpdated = async (e: CustomEvent<ActivityUpdatedArgs>) => {
|
||||
await this.canvas.updateActivity({
|
||||
|
|
@ -237,12 +240,12 @@ export class WorkflowDefinitionEditor {
|
|||
});
|
||||
|
||||
await this.updateModel();
|
||||
await this.emitActivityChanged(e.detail.activity, e.detail.propertyName);
|
||||
this.emitActivityChangedDebounced({...e.detail, workflowEditor: this.el});
|
||||
this.saveChangesDebounced();
|
||||
}
|
||||
|
||||
private onWorkflowPropsUpdated = async (e: CustomEvent<WorkflowDefinitionPropsUpdatedArgs>) => {
|
||||
await this.updateModel();
|
||||
private onWorkflowPropsUpdated = (e: CustomEvent<WorkflowDefinitionPropsUpdatedArgs>) => {
|
||||
this.updateModelDebounced();
|
||||
this.saveChangesDebounced();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,16 @@ export class Toolbar {
|
|||
public zoomToFit: () => Promise<void>;
|
||||
|
||||
@Prop()
|
||||
public autoLayout: () => Promise<void>;
|
||||
public autoLayout: (direction: "TB" | "BT" | "LR" | "RL") => Promise<void>;
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div class="elsa-panel-toolbar flex justify-center absolute border-b border-gray-200 top-0 px-1 pl-4 pb-2 text-sm bg-white z-10 space-x-2">
|
||||
<button class="btn btn-primary" onClick={this.autoLayout}>
|
||||
Auto-Layout
|
||||
<button class="btn btn-primary" onClick={() => this.autoLayout("LR")}>
|
||||
Auto-Layout (LR)
|
||||
</button>
|
||||
<button class="btn btn-primary" onClick={() => this.autoLayout("TB")}>
|
||||
Auto-Layout (TB)
|
||||
</button>
|
||||
<button onClick={this.zoomToFit}class="btn btn-primary">
|
||||
Zoom to fit
|
||||
|
|
|
|||
|
|
@ -101,7 +101,6 @@ export class WorkflowDefinitionsPlugin implements Plugin {
|
|||
private generateUniqueActivityName = async (activityDescriptor: ActivityDescriptor): Promise<string> => await generateUniqueActivityName([], activityDescriptor);
|
||||
|
||||
private saveWorkflowDefinition = async (definition: WorkflowDefinition, publish: boolean): Promise<WorkflowDefinition> => {
|
||||
debugger;
|
||||
const updatedWorkflow = await this.workflowDefinitionManager.saveWorkflow(definition, publish);
|
||||
let reload = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export interface ContainerActivityComponent {
|
|||
export(): Promise<Activity>
|
||||
import(root: Activity): Promise<void>;
|
||||
zoomToFit(): Promise<void>;
|
||||
autoLayout(): Promise<void>;
|
||||
autoLayout(direction: "TB" | "BT" | "LR" | "RL"): Promise<void>;
|
||||
scrollToStart(): Promise<void>;
|
||||
reset(): Promise<void>;
|
||||
getCurrentLevel(): Promise<Activity>;
|
||||
|
|
|
|||
Loading…
Reference in a new issue