Incremental work on embedded activities
This commit is contained in:
parent
71d0daff12
commit
36c589c062
|
|
@ -264,6 +264,8 @@ export namespace Components {
|
|||
"workflowDefinition"?: WorkflowDefinition;
|
||||
"workflowInstance"?: WorkflowInstance;
|
||||
}
|
||||
interface ElsaWorkflowNavigator {
|
||||
}
|
||||
interface ElsaWorkflowPublishButton {
|
||||
"publishing": boolean;
|
||||
}
|
||||
|
|
@ -549,6 +551,12 @@ declare global {
|
|||
prototype: HTMLElsaWorkflowManagerElement;
|
||||
new (): HTMLElsaWorkflowManagerElement;
|
||||
};
|
||||
interface HTMLElsaWorkflowNavigatorElement extends Components.ElsaWorkflowNavigator, HTMLStencilElement {
|
||||
}
|
||||
var HTMLElsaWorkflowNavigatorElement: {
|
||||
prototype: HTMLElsaWorkflowNavigatorElement;
|
||||
new (): HTMLElsaWorkflowNavigatorElement;
|
||||
};
|
||||
interface HTMLElsaWorkflowPublishButtonElement extends Components.ElsaWorkflowPublishButton, HTMLStencilElement {
|
||||
}
|
||||
var HTMLElsaWorkflowPublishButtonElement: {
|
||||
|
|
@ -614,6 +622,7 @@ declare global {
|
|||
"elsa-workflow-instance-viewer": HTMLElsaWorkflowInstanceViewerElement;
|
||||
"elsa-workflow-journal": HTMLElsaWorkflowJournalElement;
|
||||
"elsa-workflow-manager": HTMLElsaWorkflowManagerElement;
|
||||
"elsa-workflow-navigator": HTMLElsaWorkflowNavigatorElement;
|
||||
"elsa-workflow-publish-button": HTMLElsaWorkflowPublishButtonElement;
|
||||
"elsa-workflow-toolbar": HTMLElsaWorkflowToolbarElement;
|
||||
"elsa-workflow-toolbar-menu": HTMLElsaWorkflowToolbarMenuElement;
|
||||
|
|
@ -840,6 +849,8 @@ declare namespace LocalJSX {
|
|||
"workflowDefinition"?: WorkflowDefinition;
|
||||
"workflowInstance"?: WorkflowInstance;
|
||||
}
|
||||
interface ElsaWorkflowNavigator {
|
||||
}
|
||||
interface ElsaWorkflowPublishButton {
|
||||
"onExportClicked"?: (event: CustomEvent<any>) => void;
|
||||
"onImportClicked"?: (event: CustomEvent<File>) => void;
|
||||
|
|
@ -899,6 +910,7 @@ declare namespace LocalJSX {
|
|||
"elsa-workflow-instance-viewer": ElsaWorkflowInstanceViewer;
|
||||
"elsa-workflow-journal": ElsaWorkflowJournal;
|
||||
"elsa-workflow-manager": ElsaWorkflowManager;
|
||||
"elsa-workflow-navigator": ElsaWorkflowNavigator;
|
||||
"elsa-workflow-publish-button": ElsaWorkflowPublishButton;
|
||||
"elsa-workflow-toolbar": ElsaWorkflowToolbar;
|
||||
"elsa-workflow-toolbar-menu": ElsaWorkflowToolbarMenu;
|
||||
|
|
@ -954,6 +966,7 @@ declare module "@stencil/core" {
|
|||
"elsa-workflow-instance-viewer": LocalJSX.ElsaWorkflowInstanceViewer & JSXBase.HTMLAttributes<HTMLElsaWorkflowInstanceViewerElement>;
|
||||
"elsa-workflow-journal": LocalJSX.ElsaWorkflowJournal & JSXBase.HTMLAttributes<HTMLElsaWorkflowJournalElement>;
|
||||
"elsa-workflow-manager": LocalJSX.ElsaWorkflowManager & JSXBase.HTMLAttributes<HTMLElsaWorkflowManagerElement>;
|
||||
"elsa-workflow-navigator": LocalJSX.ElsaWorkflowNavigator & JSXBase.HTMLAttributes<HTMLElsaWorkflowNavigatorElement>;
|
||||
"elsa-workflow-publish-button": LocalJSX.ElsaWorkflowPublishButton & JSXBase.HTMLAttributes<HTMLElsaWorkflowPublishButtonElement>;
|
||||
"elsa-workflow-toolbar": LocalJSX.ElsaWorkflowToolbar & JSXBase.HTMLAttributes<HTMLElsaWorkflowToolbarElement>;
|
||||
"elsa-workflow-toolbar-menu": LocalJSX.ElsaWorkflowToolbarMenu & JSXBase.HTMLAttributes<HTMLElsaWorkflowToolbarMenuElement>;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import FromJSONData = Model.FromJSONData;
|
|||
import {ContextMenuAnchorPoint, MenuItem, MenuItemGroup} from "../../shared/context-menu/models";
|
||||
import PointLike = Point.PointLike;
|
||||
import descriptorsStore from "../../../data/descriptors-store";
|
||||
import {assignParent} from "./flowchart/assign-parent";
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-flowchart',
|
||||
|
|
@ -350,6 +349,9 @@ export class FlowchartComponent implements ContainerActivityComponent {
|
|||
}
|
||||
|
||||
onNodeMoved = (e: PositionEventArgs<JQuery.ClickEvent>) => {
|
||||
|
||||
console.debug("Node moved...");
|
||||
|
||||
const node = e.node as ActivityNodeShape;
|
||||
const activity = node.data as Activity;
|
||||
const nodePosition = node.position({relative: false});
|
||||
|
|
@ -364,8 +366,6 @@ export class FlowchartComponent implements ContainerActivityComponent {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
assignParent(this.graph, node);
|
||||
}
|
||||
|
||||
onEdgeConnected = async (e: { isNew: boolean, edge: Edge }) => {
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
import {Graph, Rectangle, Node} from "@antv/x6";
|
||||
import {camelCase} from 'lodash';
|
||||
import {Activity} from "../../../../models";
|
||||
import {ActivityNode as ActivityNodeShape} from "../shapes";
|
||||
|
||||
export function assignParent(graph: Graph, node: ActivityNodeShape) {
|
||||
const underlyingNodes = graph.getNodesUnderNode(node);
|
||||
|
||||
if (underlyingNodes.length > 0) {
|
||||
const underlyingNode = underlyingNodes[0] as ActivityNodeShape;
|
||||
const underlyingActivity = underlyingNode.data as Activity;
|
||||
const underlyingView: any = graph.findView(underlyingNode);
|
||||
const portElements: Array<HTMLElement> = underlyingView.selectors.foContent.getElementsByClassName('activity-port');
|
||||
|
||||
if (portElements.length == 0)
|
||||
return;
|
||||
|
||||
const movedNodeView = graph.findView(node);
|
||||
const movedNodeViewRect = movedNodeView.getBBox();
|
||||
|
||||
for (const portElement of portElements) {
|
||||
const portRect = Rectangle.create(portElement.getBoundingClientRect());
|
||||
const localPortRect = Rectangle.create(graph.pageToLocal(portRect));
|
||||
|
||||
if (movedNodeViewRect.intersectsWithRect(localPortRect)) {
|
||||
const portName = camelCase(portElement.dataset.portName);
|
||||
const childActivity = node.data as Activity;
|
||||
underlyingActivity[portName] = childActivity;
|
||||
underlyingNode.activity = {...underlyingActivity};
|
||||
graph.removeCell(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
// export function findParent(args: any): Array<any> {
|
||||
// const childNode = args.node;
|
||||
// const bbox = childNode.getBBox();
|
||||
//
|
||||
// return this.getNodes().filter((node) => {
|
||||
//
|
||||
// const data = node.getData();
|
||||
//
|
||||
// if (data?.typeName == 'Elsa.If') {
|
||||
// debugger;
|
||||
// const targetBBox = node.getBBox();
|
||||
// return bbox.isIntersectWithRect(targetBBox);
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
// })
|
||||
// }
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import {CellView, Graph, Node, Shape} from '@antv/x6';
|
||||
import {v4 as uuid} from 'uuid';
|
||||
import './ports';
|
||||
import {assignParent} from "./flowchart/assign-parent";
|
||||
|
||||
export function createGraph(
|
||||
container: HTMLElement,
|
||||
|
|
|
|||
|
|
@ -68,20 +68,15 @@ export class ActivityNode extends Shape.HTML {
|
|||
// Wait for activity element to be completely rendered.
|
||||
// When using custom elements, they are rendered after they are mounted. Before then, they have a 0 width and height.
|
||||
const tryUpdateSize = () => {
|
||||
// TODO: Figure out if there's a way to select *any* custom element.
|
||||
// Worst case, we need to look for a registered set if tags, or introduce a convention where custom elements need to specify a class name for example.
|
||||
const activityElements: Array<Element> = Array.from(wrapper.getElementsByTagName('elsa-default-activity-template'));
|
||||
const activityElement: Element = wrapper.getElementsByTagName('elsa-default-activity-template')[0];
|
||||
const activityElementRect = activityElement.getBoundingClientRect();
|
||||
|
||||
for (const activityElement of activityElements) {
|
||||
const activityElementRect = activityElement.getBoundingClientRect();
|
||||
// If the custom element has no width or height yet, it means it has not yet rendered.
|
||||
if (activityElementRect.width == 0 || activityElementRect.height == 0) {
|
||||
|
||||
// If the custom element has no width or height yet, it means it has not yet rendered.
|
||||
if (activityElementRect.width == 0 || activityElementRect.height == 0) {
|
||||
|
||||
// Request an animation frame and call ourselves back immediately after.
|
||||
window.requestAnimationFrame(tryUpdateSize);
|
||||
return;
|
||||
}
|
||||
// Request an animation frame and call ourselves back immediately after.
|
||||
window.requestAnimationFrame(tryUpdateSize);
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = wrapper.firstElementChild.getBoundingClientRect();
|
||||
|
|
|
|||
|
|
@ -131,7 +131,11 @@ export class DefaultActivityTemplate {
|
|||
const portName = camelCase(port.name);
|
||||
const activity = this.parsedActivity;
|
||||
const childActivity: Activity = activity ? activity[portName] : null;
|
||||
const isSelected = port.name == this.selectedPortName;
|
||||
const childActivityDescriptor: ActivityDescriptor = childActivity != null ? descriptorsStore.activityDescriptors.find(x => x.activityType == childActivity.typeName) : null;
|
||||
let childActivityDisplayText = childActivity?.metadata?.displayText;
|
||||
|
||||
if (isNullOrWhitespace(childActivityDisplayText))
|
||||
childActivityDisplayText = childActivityDescriptor?.displayName;
|
||||
|
||||
return (
|
||||
<div class="activity-port" data-port-name={port.name} ref={el => this.portElements.push(el)}>
|
||||
|
|
@ -140,20 +144,29 @@ export class DefaultActivityTemplate {
|
|||
</div>
|
||||
<div>
|
||||
{childActivity ? (
|
||||
<div class={`relative block w-full focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500`}
|
||||
onMouseDown={e => this.onPortMouseDown(e, port)}
|
||||
onMouseUp={e => this.onPortMouseUp(e, port)}
|
||||
>
|
||||
<elsa-default-activity-template activityType={childActivity.typeName} displayType="embedded" activity={childActivity} selected={isSelected}/>
|
||||
{/*<span class={textColor}>{childActivity.typeName}</span>*/}
|
||||
<div class="relative block w-full border-2 border-gray-300 border-solid rounded-lg p-5 text-center focus:outline-none">
|
||||
<div class="flex space-x-2">
|
||||
<div class="flex-grow">
|
||||
<span class={textColor}>{childActivityDisplayText}</span>
|
||||
</div>
|
||||
<div class="flex-shrink">
|
||||
<a href="#" class="text-gray-500 hover:text-yellow-700">
|
||||
<svg class="h-6 w-6" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z"/>
|
||||
<path d="M4 20h4l10.5 -10.5a1.5 1.5 0 0 0 -4 -4l-10.5 10.5v4"/>
|
||||
<line x1="13.5" y1="6.5" x2="17.5" y2="10.5"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div onDragOver={this.onDragOverPort}
|
||||
onDrop={this.onDropOnPort}
|
||||
class="relative block w-full border-2 border-gray-300 border-dashed rounded-lg p-6 text-center hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
|
||||
<svg class="h-6 w-6 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
|
||||
</svg>
|
||||
<div class="relative block w-full border-2 border-gray-300 border-dashed rounded-lg p-5 text-center focus:outline-none">
|
||||
<a href="#" class="text-gray-400 hover:text-gray-600">
|
||||
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -178,43 +191,4 @@ export class DefaultActivityTemplate {
|
|||
|
||||
this.selectedPortName = null;
|
||||
}
|
||||
|
||||
private onPortMouseDown = (e: MouseEvent, port: Port) => {
|
||||
e.stopPropagation();
|
||||
};
|
||||
|
||||
private onPortMouseUp = (e: MouseEvent, port: Port) => {
|
||||
e.stopPropagation();
|
||||
|
||||
if (this.selectedPortName != port.name) {
|
||||
this.selectedPortName = port.name;
|
||||
|
||||
const activity = this.parsedActivity;
|
||||
const portName = camelCase(port.name);
|
||||
const childActivity: Activity = activity ? activity[portName] : null;
|
||||
|
||||
const args: ActivitySelectedArgs = {
|
||||
activity: childActivity,
|
||||
applyChanges: a => {
|
||||
activity[portName] = a;
|
||||
},
|
||||
deleteActivity: a => {
|
||||
activity[portName] = null;
|
||||
}
|
||||
};
|
||||
|
||||
this.activitySelected.emit(args);
|
||||
}
|
||||
};
|
||||
|
||||
private onDragOverPort = (e: DragEvent) => {
|
||||
console.debug("Dragging over!");
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
private onDropOnPort = (e: DragEvent) => {
|
||||
//debugger;
|
||||
console.debug("Dropped!");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export class Toolbar {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<div class="flex justify-center absolute border-b border-gray-200 top-0 px-1 pl-4 py-2 text-sm bg-white z-10 elsa-panel-toolbar">
|
||||
<div class="elsa-panel-toolbar flex justify-center absolute border-b border-gray-200 top-0 px-1 pl-4 py-2 text-sm bg-white z-10">
|
||||
<elsa-tooltip tooltipPosition="right" tooltipContent={<p class="text-gray-600 text-sm">Currently in development</p>}>
|
||||
<button class="relative inline-flex items-center px-4 py-2 rounded border border-blue-600 bg-blue-600 text-sm font-medium text-white hover:bg-blue-700 focus:z-10 focus:outline-none focus:ring-blue-600 hover:border-blue-700">
|
||||
Auto-Layout
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
}
|
||||
elsa-canvas {
|
||||
left: var(--activity-picker-width);
|
||||
top: 0;
|
||||
top: 54px;
|
||||
right: var(--activity-editor-width);
|
||||
bottom: 0;
|
||||
}
|
||||
|
|
@ -24,6 +24,13 @@ elsa-panel.panel-state-expanded.elsa-activity-editor-container {
|
|||
left: unset;
|
||||
}
|
||||
|
||||
elsa-workflow-navigator {
|
||||
top: 70px;
|
||||
position: absolute;
|
||||
left: var(--activity-picker-width);
|
||||
right: var(--activity-editor-width);
|
||||
}
|
||||
|
||||
.activity-picker-closed {
|
||||
|
||||
.activity-list {
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ export class WorkflowDefinitionEditor {
|
|||
<WorkflowEditorTunnel.Provider state={tunnelState}>
|
||||
<div class="absolute inset-0" ref={el => this.container = el}>
|
||||
<elsa-workflow-definition-editor-toolbar zoomToFit={this.onZoomToFit} />
|
||||
<elsa-workflow-navigator />
|
||||
<elsa-panel
|
||||
class="elsa-activity-picker-container"
|
||||
position={PanelPosition.Left}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import {Component, h} from "@stencil/core";
|
||||
|
||||
@Component({
|
||||
tag: 'elsa-workflow-navigator',
|
||||
shadow: false
|
||||
})
|
||||
export class WorkflowNavigator {
|
||||
render() {
|
||||
return <div class="text-gray-500">Breadcrumbs</div>
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue