From 4a5df3c816b7950bbdbf3f63af4370b5961353a4 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Wed, 28 Apr 2021 13:02:03 +0200 Subject: [PATCH] Add activity selection --- .../Services/IWorkflowScheduler.cs | 50 --------------- .../elsa-workflows-studio/src/components.d.ts | 6 +- .../elsa-designer-tree/elsa-designer-tree.tsx | 62 +++++++++++++++---- .../elsa-workflow-instance-viewer-screen.tsx | 10 +-- 4 files changed, 61 insertions(+), 67 deletions(-) delete mode 100644 src/core/Elsa.Abstractions/Services/IWorkflowScheduler.cs diff --git a/src/core/Elsa.Abstractions/Services/IWorkflowScheduler.cs b/src/core/Elsa.Abstractions/Services/IWorkflowScheduler.cs deleted file mode 100644 index 96298bf9f..000000000 --- a/src/core/Elsa.Abstractions/Services/IWorkflowScheduler.cs +++ /dev/null @@ -1,50 +0,0 @@ -// using System; -// using System.Collections.Generic; -// using System.Threading; -// using System.Threading.Tasks; -// using Elsa.Models; -// using Elsa.Triggers; -// -// namespace Elsa.Services -// { -// public interface IWorkflowScheduler -// { -// /// -// /// Schedules new and existing instances matching the specified trigger. -// /// -// Task TriggerWorkflowsAsync(Func predicate, object? input = default, string? correlationId = default, string? contextId = default, CancellationToken cancellationToken = default) where TTrigger : ITrigger; -// -// /// -// /// Schedules the specified workflow instance for execution. -// /// -// Task ScheduleWorkflowInstanceAsync(string instanceId, string? activityId = default, object? input = default, CancellationToken cancellationToken = default); -// -// /// -// /// Creates new workflow instances of the specified workflow definition for each start activity and schedules it for execution. -// /// -// Task> ScheduleWorkflowDefinitionAsync( -// string definitionId, -// string? tenantId, -// object? input = default, -// string? correlationId = default, -// string? contextId = default, -// CancellationToken cancellationToken = default); -// -// /// -// /// Creates a new workflow instances of the specified workflow definition for the specified start activity and schedules it for execution. -// /// -// Task> ScheduleWorkflowDefinitionAsync( -// string definitionId, -// string activityId, -// string? tenantId, -// object? input = default, -// string? correlationId = default, -// string? contextId = default, -// CancellationToken cancellationToken = default); -// -// /// -// /// Schedules new workflows that start with the specified activity type or are blocked on the specified activity type. -// /// -// Task TriggerWorkflowsAsync(string activityType, object? input = default, string? correlationId = default, string? contextId = default, CancellationToken cancellationToken = default); -// } -// } \ No newline at end of file diff --git a/src/designer/elsa-workflows-studio/src/components.d.ts b/src/designer/elsa-workflows-studio/src/components.d.ts index ab73ede4a..cf2f48803 100644 --- a/src/designer/elsa-workflows-studio/src/components.d.ts +++ b/src/designer/elsa-workflows-studio/src/components.d.ts @@ -5,7 +5,7 @@ * It contains typing information for all components that exist in this project. */ import { HTMLStencilElement, JSXBase } from "@stencil/core/internal"; -import { ActivityDefinitionProperty, ActivityDescriptor, ActivityPropertyDescriptor, VersionOptions, WorkflowBlueprint, WorkflowDefinition, WorkflowExecutionLogRecord, WorkflowModel } from "./models"; +import { ActivityDefinitionProperty, ActivityDescriptor, ActivityModel, ActivityPropertyDescriptor, VersionOptions, WorkflowBlueprint, WorkflowDefinition, WorkflowExecutionLogRecord, WorkflowModel } from "./models"; import { LocationSegments, MatchResults, RouterHistory } from "@stencil/router"; import { MenuItem } from "./components/controls/elsa-context-menu/models"; import { DropdownButtonItem, DropdownButtonOrigin } from "./components/controls/elsa-dropdown-button/models"; @@ -35,6 +35,7 @@ export namespace Components { } interface ElsaDesignerTree { "model": WorkflowModel; + "selectedActivityIds": Array; } interface ElsaDropdownButton { "icon"?: any; @@ -487,7 +488,10 @@ declare namespace LocalJSX { } interface ElsaDesignerTree { "model"?: WorkflowModel; + "onActivityDeselected"?: (event: CustomEvent) => void; + "onActivitySelected"?: (event: CustomEvent) => void; "onWorkflow-changed"?: (event: CustomEvent) => void; + "selectedActivityIds"?: Array; } interface ElsaDropdownButton { "icon"?: any; diff --git a/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree/elsa-designer-tree.tsx b/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree/elsa-designer-tree.tsx index 8f0864043..c8d730dec 100644 --- a/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree/elsa-designer-tree.tsx +++ b/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree/elsa-designer-tree.tsx @@ -18,6 +18,8 @@ import {registerClickOutside} from 'stencil-click-outside'; import state from '../../../../utils/store'; import uuid = jsPlumb.jsPlumbUtil.uuid; import {ActivityIcon} from '../../../icons/activity-icon'; +import {languages} from "monaco-editor"; +import css = languages.css; @Component({ tag: 'elsa-designer-tree', @@ -27,7 +29,10 @@ import {ActivityIcon} from '../../../icons/activity-icon'; }) export class ElsaWorkflowDesigner { @Prop() model: WorkflowModel = {activities: [], connections: [], persistenceBehavior: WorkflowPersistenceBehavior.WorkflowBurst}; + @Prop() selectedActivityIds: Array = []; @Event({eventName: 'workflow-changed', bubbles: true, composed: true, cancelable: true}) workflowChanged: EventEmitter; + @Event() activitySelected: EventEmitter; + @Event() activityDeselected: EventEmitter; @State() workflowModel: WorkflowModel; el: HTMLElement; @@ -43,6 +48,7 @@ export class ElsaWorkflowDesigner { parentActivityId?: string; parentActivityOutcome?: string; activityDisplayContexts: Map = {}; + selectedActivities: Map = {}; @State() contextMenu: { shown: boolean; @@ -70,6 +76,20 @@ export class ElsaWorkflowDesigner { this.workflowModel = newValue; } + @Watch('selectedActivityIds') + handleSelectedActivityIdsChanged(newValue: Array) { + debugger; + const ids = newValue || []; + const selectedActivities = this.workflowModel.activities.filter(x => ids.includes(x.activityId)); + const map: Map = {}; + + for (const activity of selectedActivities) + map[activity.activityId] = activity; + + this.selectedActivities = map; + this.rerenderTree(); + } + handleEditActivity(activity: ActivityModel) { this.showActivityEditor(activity, true); } @@ -197,7 +217,7 @@ export class ElsaWorkflowDesigner { this.innerD3Selected = d3.select(this.inner); setTimeout(() => { this.rerenderTree() - }, 400) + }, 600) } componentWillRender() { @@ -222,7 +242,7 @@ export class ElsaWorkflowDesigner { eventBus.emit(EventTypes.ActivityDesignDisplaying, this, displayContext); displayContexts[model.activityId] = displayContext; } - + this.activityDisplayContexts = displayContexts; } @@ -355,9 +375,27 @@ export class ElsaWorkflowDesigner { d3.selectAll('.node.activity').each((n: any) => { const node = this.graph.node(n) as any; + const activity = node.activity; + const activityId = activity.activityId; + d3.select(node.elem).on('click', () => { + // If a parent activity was selected to connect to: if (this.parentActivityId && this.parentActivityOutcome) { - this.addConnection(this.parentActivityId, node.activity.activityId, this.parentActivityOutcome); + this.addConnection(this.parentActivityId, activityId, this.parentActivityOutcome); + } else { + // When clicking an activity: + if (!!this.selectedActivities[activityId]) + delete this.selectedActivities[activityId]; + else { + for (const key in this.selectedActivities) { + this.activityDeselected.emit(this.selectedActivities[key]); + } + this.selectedActivities = {}; + this.selectedActivities[activityId] = activity; + this.activitySelected.emit(activity); + } + + this.rerenderTree(); } }); d3.select(node.elem) @@ -399,8 +437,9 @@ export class ElsaWorkflowDesigner { renderActivity(activity: ActivityModel) { const displayContext = this.activityDisplayContexts[activity.activityId] || undefined; + const cssClass = !!this.selectedActivities[activity.activityId] ? 'border-blue-600' : 'border-white hover:border-blue-600' return `
+ class="activity border-2 border-solid rounded bg-white text-left text-black text-lg select-none max-w-md shadow-sm relative ${cssClass}">
@@ -429,9 +468,11 @@ export class ElsaWorkflowDesigner { renderActivityBody(description: string | null) { if (!description) return ''; - return `
- ${description} -
`; + return ( + `
+ ${description} +
` + ); } // private onActivitySelected(e: CustomEvent) { @@ -445,8 +486,8 @@ export class ElsaWorkflowDesigner { render() { return ( (this.el = el)}> - (this.svg = el)} id="svg" style={{ height: 'calc(100vh - 64px)', width: '100%', pointerEvents: this.contextMenu.shown ? 'none' : '' }}> - (this.inner = el)} /> + (this.svg = el)} id="svg" style={{height: 'calc(100vh - 64px)', width: '100%', pointerEvents: this.contextMenu.shown ? 'none' : ''}}> + (this.inner = el)}/>
registerClickOutside(this, el, () => { this.handleContextMenuChange(0, 0, false, null); @@ -487,7 +528,6 @@ export class ElsaWorkflowDesigner {
- {/*
} /> */} ); } diff --git a/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-viewer-screen.tsx b/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-viewer-screen.tsx index 0de858eb2..759137a33 100644 --- a/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-viewer-screen.tsx +++ b/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-viewer-screen.tsx @@ -169,7 +169,7 @@ export class ElsaWorkflowInstanceViewerScreen { } onActivityDeselected(e: CustomEvent) { - if(this.selectedActivityId == e.detail.activityId) + if (this.selectedActivityId == e.detail.activityId) this.selectedActivityId = null; this.journal.selectActivityRecord(this.selectedActivityId); @@ -180,7 +180,7 @@ export class ElsaWorkflowInstanceViewerScreen { return ( this.el = el}> {this.renderCanvas()} - this.journal = el} + this.journal = el} workflowInstanceId={this.workflowInstanceId} serverUrl={this.serverUrl} activityDescriptors={descriptors} @@ -196,9 +196,9 @@ export class ElsaWorkflowInstanceViewerScreen {
this.designer = el} - // selectedActivityId={this.selectedActivityId} - // onActivitySelected={e => this.onActivitySelected(e)} - // onActivityDeselected={e => this.onActivityDeselected(e)} + selectedActivityIds={[this.selectedActivityId]} + onActivitySelected={e => this.onActivitySelected(e)} + onActivityDeselected={e => this.onActivityDeselected(e)} />
);