Add activity selection

This commit is contained in:
Sipke Schoorstra 2021-04-28 13:02:03 +02:00
parent 6b411bb518
commit 4a5df3c816
4 changed files with 61 additions and 67 deletions

View file

@ -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
// {
// /// <summary>
// /// Schedules new and existing instances matching the specified trigger.
// /// </summary>
// Task TriggerWorkflowsAsync<TTrigger>(Func<TTrigger, bool> predicate, object? input = default, string? correlationId = default, string? contextId = default, CancellationToken cancellationToken = default) where TTrigger : ITrigger;
//
// /// <summary>
// /// Schedules the specified workflow instance for execution.
// /// </summary>
// Task ScheduleWorkflowInstanceAsync(string instanceId, string? activityId = default, object? input = default, CancellationToken cancellationToken = default);
//
// /// <summary>
// /// Creates new workflow instances of the specified workflow definition for each start activity and schedules it for execution.
// /// </summary>
// Task<IEnumerable<WorkflowInstance>> ScheduleWorkflowDefinitionAsync(
// string definitionId,
// string? tenantId,
// object? input = default,
// string? correlationId = default,
// string? contextId = default,
// CancellationToken cancellationToken = default);
//
// /// <summary>
// /// Creates a new workflow instances of the specified workflow definition for the specified start activity and schedules it for execution.
// /// </summary>
// Task<IEnumerable<WorkflowInstance>> ScheduleWorkflowDefinitionAsync(
// string definitionId,
// string activityId,
// string? tenantId,
// object? input = default,
// string? correlationId = default,
// string? contextId = default,
// CancellationToken cancellationToken = default);
//
// /// <summary>
// /// Schedules new workflows that start with the specified activity type or are blocked on the specified activity type.
// /// </summary>
// Task TriggerWorkflowsAsync(string activityType, object? input = default, string? correlationId = default, string? contextId = default, CancellationToken cancellationToken = default);
// }
// }

View file

@ -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<string>;
}
interface ElsaDropdownButton {
"icon"?: any;
@ -487,7 +488,10 @@ declare namespace LocalJSX {
}
interface ElsaDesignerTree {
"model"?: WorkflowModel;
"onActivityDeselected"?: (event: CustomEvent<ActivityModel>) => void;
"onActivitySelected"?: (event: CustomEvent<ActivityModel>) => void;
"onWorkflow-changed"?: (event: CustomEvent<WorkflowModel>) => void;
"selectedActivityIds"?: Array<string>;
}
interface ElsaDropdownButton {
"icon"?: any;

View file

@ -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<string> = [];
@Event({eventName: 'workflow-changed', bubbles: true, composed: true, cancelable: true}) workflowChanged: EventEmitter<WorkflowModel>;
@Event() activitySelected: EventEmitter<ActivityModel>;
@Event() activityDeselected: EventEmitter<ActivityModel>;
@State() workflowModel: WorkflowModel;
el: HTMLElement;
@ -43,6 +48,7 @@ export class ElsaWorkflowDesigner {
parentActivityId?: string;
parentActivityOutcome?: string;
activityDisplayContexts: Map<ActivityDesignDisplayContext> = {};
selectedActivities: Map<ActivityModel> = {};
@State() contextMenu: {
shown: boolean;
@ -70,6 +76,20 @@ export class ElsaWorkflowDesigner {
this.workflowModel = newValue;
}
@Watch('selectedActivityIds')
handleSelectedActivityIdsChanged(newValue: Array<string>) {
debugger;
const ids = newValue || [];
const selectedActivities = this.workflowModel.activities.filter(x => ids.includes(x.activityId));
const map: Map<ActivityModel> = {};
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 `<div id=${`activity-${activity.activityId}`}
class="activity border-2 border-solid border-white rounded bg-white text-left text-black text-lg hover:border-blue-600 select-none max-w-md shadow-sm relative">
class="activity border-2 border-solid rounded bg-white text-left text-black text-lg select-none max-w-md shadow-sm relative ${cssClass}">
<div class="p-5">
<div class="flex justify-between space-x-8">
<div class="flex-shrink-0">
@ -429,9 +468,11 @@ export class ElsaWorkflowDesigner {
renderActivityBody(description: string | null) {
if (!description) return '';
return `<div class="p-6 text-gray-400 text-sm border-t border-t-solid">
${description}
</div>`;
return (
`<div class="p-6 text-gray-400 text-sm border-t border-t-solid">
${description}
</div>`
);
}
// private onActivitySelected(e: CustomEvent<ActivityModel>) {
@ -445,8 +486,8 @@ export class ElsaWorkflowDesigner {
render() {
return (
<Host class="workflow-canvas flex-1 flex" ref={el => (this.el = el)}>
<svg ref={el => (this.svg = el)} id="svg" style={{ height: 'calc(100vh - 64px)', width: '100%', pointerEvents: this.contextMenu.shown ? 'none' : '' }}>
<g ref={el => (this.inner = el)} />
<svg ref={el => (this.svg = el)} id="svg" style={{height: 'calc(100vh - 64px)', width: '100%', pointerEvents: this.contextMenu.shown ? 'none' : ''}}>
<g ref={el => (this.inner = el)}/>
</svg>
<div
data-transition-enter="transition ease-out duration-100"
@ -456,7 +497,7 @@ export class ElsaWorkflowDesigner {
data-transition-leave-start="transform opacity-100 scale-100"
data-transition-leave-end="transform opacity-0 scale-95"
class={`${this.contextMenu.shown ? '' : 'hidden'} context-menu z-10 mx-3 w-48 mt-1 rounded-md shadow-lg`}
style={{ position: 'absolute', left: `${this.contextMenu.x}px`, top: `${this.contextMenu.y - 64}px` }}
style={{position: 'absolute', left: `${this.contextMenu.x}px`, top: `${this.contextMenu.y - 64}px`}}
ref={el =>
registerClickOutside(this, el, () => {
this.handleContextMenuChange(0, 0, false, null);
@ -487,7 +528,6 @@ export class ElsaWorkflowDesigner {
</div>
</div>
</div>
{/* <div innerHTML={<ActivityIcon />} /> */}
</Host>
);
}

View file

@ -169,7 +169,7 @@ export class ElsaWorkflowInstanceViewerScreen {
}
onActivityDeselected(e: CustomEvent<ActivityModel>) {
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 (
<Host class="flex flex-col w-full relative" ref={el => this.el = el}>
{this.renderCanvas()}
<elsa-workflow-instance-journal ref={el => this.journal = el}
<elsa-workflow-instance-journal ref={el => this.journal = el}
workflowInstanceId={this.workflowInstanceId}
serverUrl={this.serverUrl}
activityDescriptors={descriptors}
@ -196,9 +196,9 @@ export class ElsaWorkflowInstanceViewerScreen {
<div class="flex-1 flex">
<elsa-designer-tree model={this.workflowModel}
class="flex-1" ref={el => 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)}
/>
</div>
);