Add FlowNode activity

This commit is contained in:
Sipke Schoorstra 2022-11-24 16:03:35 +01:00
parent cf6c80ab85
commit 2876bb65bc
11 changed files with 86 additions and 46 deletions

View file

@ -55,23 +55,11 @@ services
.AddElsa(elsa => elsa
.UseWorkflowManagement(management => management
.UseEntityFrameworkCore(ef => ef.UseSqlite(sqliteConnectionString))
.AddActivity<WriteLine>()
.AddActivity<ReadLine>()
.AddActivity<If>()
.AddActivity<HttpEndpoint>()
.AddActivity<WriteHttpResponse>()
.AddActivity<Flowchart>()
.AddActivity<FlowDecision>()
.AddActivity<FlowSwitch>()
.AddActivity<FlowJoin>()
.AddActivity<Elsa.Scheduling.Activities.Delay>()
.AddActivity<Elsa.Scheduling.Activities.Timer>()
.AddActivity<ForEach>()
.AddActivity<Switch>()
.AddActivity<RunJavaScript>()
.AddActivity<Event>()
.AddActivity<SendHttpRequest>()
.AddActivity<ProcessVideo>()
.AddActivitiesFrom<WriteLine>()
.AddActivitiesFrom<HttpEndpoint>()
.AddActivitiesFrom<Elsa.Scheduling.Activities.Delay>()
.AddActivitiesFrom<RunJavaScript>()
.AddActivitiesFrom<Program>()
)
.Use<IdentityFeature>(identity =>
{

View file

@ -0,0 +1,10 @@
import {FunctionalComponent, h} from '@stencil/core';
import {ActivityIconSettings, getActivityIconCssClass} from "./models";
export const DefaultIcon: FunctionalComponent<ActivityIconSettings> = (settings) => (
<svg class={getActivityIconCssClass(settings)} width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
);

View file

@ -0,0 +1,8 @@
import {FunctionalComponent, h} from '@stencil/core';
import {ActivityIconSettings, getActivityIconCssClass} from "./models";
export const FlowNodeIcon: FunctionalComponent<ActivityIconSettings> = (settings) => (
<svg class={getActivityIconCssClass(settings)} width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
</svg>
);

View file

@ -1,7 +1,7 @@
import {FunctionalComponent, h} from '@stencil/core';
import {ActivityIconSettings, getActivityIconCssClass} from "./models";
export const SendHttpRequestIcon: FunctionalComponent<ActivityIconSettings> = (settings) => (
export const HttpRequestIcon: FunctionalComponent<ActivityIconSettings> = (settings) => (
<svg class={getActivityIconCssClass(settings)} 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"/>

View file

@ -1,7 +1,7 @@
import {FunctionalComponent, h} from '@stencil/core';
import {ActivityIconSettings, getActivityIconCssClass} from "./models";
export const WriteHttpResponseIcon: FunctionalComponent<ActivityIconSettings> = (settings) => (
export const HttpResponseIcon: FunctionalComponent<ActivityIconSettings> = (settings) => (
<svg class={getActivityIconCssClass(settings)} width="24" height="24" viewBox="0 0 24 24"
stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">

View file

@ -1,12 +1,17 @@
export * from "./default";
export * from "./delay";
export * from "./event";
export * from "./http-endpoint";
export * from "./write-line";
export * from "./read-line";
export * from "./if";
export * from "./flow-decision";
export * from "./flow-join";
export * from "./flow-node";
export * from "./flowchart";
export * from "./foreach";
export * from "./delay";
export * from "./timer";
export * from "./run-javascript";
export * from "./http-endpoint";
export * from "./http-request";
export * from "./http-response";
export * from "./if";
export * from "./models";
export * from "./read-line";
export * from "./run-javascript";
export * from "./timer";
export * from "./write-line";

View file

@ -1,5 +1,5 @@
import {Component, h, Prop, State, Event, EventEmitter, Listen, Element} from "@stencil/core";
import {ActivityIcon, ActivityIconRegistry, PortProviderRegistry} from "../../services";
import {ActivityIconProducer, ActivityIconRegistry, PortProviderRegistry} from "../../services";
import {Container} from "typedi";
import {Activity, ActivityDescriptor, ActivityKind, ActivitySelectedArgs, ChildActivitySelectedArgs, EditChildActivityArgs, Port, PortMode} from "../../models";
import descriptorsStore from "../../data/descriptors-store";
@ -14,7 +14,7 @@ export class DefaultActivityTemplate {
private readonly iconRegistry: ActivityIconRegistry;
private readonly portProviderRegistry: PortProviderRegistry;
private activityDescriptor: ActivityDescriptor;
private icon: ActivityIcon;
private icon: ActivityIconProducer;
private portElements: Array<HTMLElement> = [];
constructor() {
@ -90,7 +90,7 @@ export class DefaultActivityTemplate {
)
}
private renderIcon = (icon?: ActivityIcon): string => {
private renderIcon = (icon?: ActivityIconProducer): string => {
const iconCssClass = this.displayTypeIsPicker ? 'px-2' : 'px-4';
if (!icon)

View file

@ -406,7 +406,9 @@ export class FlowchartComponent implements ContainerActivityComponent {
this.activity = flowchart;
this.activityLookup = createActivityLookup(activityNodes);
this.currentPath = [{activityId: flowchart.id, portName: null, index: 0}];
if(!this.currentPath || this.currentPath.length == 0)
this.currentPath = [{activityId: flowchart.id, portName: null, index: 0}];
await this.setupGraph(flowchart);
};
@ -704,6 +706,7 @@ export class FlowchartComponent implements ContainerActivityComponent {
const path = this.currentPath;
const index = path.indexOf(item);
debugger;
this.currentPath = path.slice(0, index + 1);
if (!!item.portName) {

View file

@ -20,6 +20,7 @@ export class WorkflowNavigator {
@Prop() items: Array<FlowchartNavigationItem> = [];
@Prop() flowchart: Flowchart;
@Event() navigate: EventEmitter<FlowchartNavigationItem>;
render() {
@ -47,7 +48,7 @@ export class WorkflowNavigator {
const activityId = item.activityId;
const activity = nodes.find(x => x.activity.id == activityId).activity;
const activityDescriptor = descriptorsStore.activityDescriptors.find(x => x.type == activity.type);
const icon = this.iconRegistry.get(activity.type)();
const icon = this.iconRegistry.getOrDefault(activity.type)();
const listElements = [];
const isLastItem = index == this.items.length - 1;

View file

@ -3,36 +3,33 @@ import 'reflect-metadata';
import {Service} from "typedi";
import {
DelayIcon,
DefaultIcon,
DelayIcon, TimerIcon,
EventIcon,
FlowchartIcon, FlowDecisionIcon,
FlowchartIcon, FlowDecisionIcon, FlowJoinIcon, FlowNodeIcon,
ForEachIcon,
HttpEndpointIcon,
IfIcon,
ReadLineIcon,
ReadLineIcon, WriteLineIcon,
RunJavaScriptIcon,
TimerIcon,
WriteLineIcon
HttpEndpointIcon, HttpResponseIcon, HttpRequestIcon
} from "../components/icons/activities";
import {WriteHttpResponseIcon} from "../components/icons/activities/write-http-response";
import {FlowJoinIcon} from "../components/icons/activities/flow-join";
import {SendHttpRequestIcon} from "../components/icons/activities/send-http-request"
export type ActivityType = string;
export type ActivityIcon = (ActivityIconSettings?) => any;
export type ActivityIconProducer = (ActivityIconSettings?) => any;
// A registry of activity icons.
@Service()
export class ActivityIconRegistry {
private iconMap: Map<ActivityType, ActivityIcon> = new Map<ActivityType, ActivityIcon>();
private iconMap: Map<ActivityType, ActivityIconProducer> = new Map<ActivityType, ActivityIconProducer>();
constructor() {
this.add('Elsa.WriteLine', settings => <WriteLineIcon size={settings?.size}/>);
this.add('Elsa.WriteLine', settings => <WriteLineIcon size={settings?.size}/>);
this.add('Elsa.ReadLine', settings => <ReadLineIcon size={settings?.size}/>);
this.add('Elsa.If', settings => <IfIcon size={settings?.size}/>);
this.add('Elsa.Flowchart', settings => <FlowchartIcon size={settings?.size}/>);
this.add('Elsa.HttpEndpoint', settings => <HttpEndpointIcon size={settings?.size}/>);
this.add('Elsa.WriteHttpResponse', settings => <WriteHttpResponseIcon size={settings?.size}/>);
this.add('Elsa.WriteHttpResponse', settings => <HttpResponseIcon size={settings?.size}/>);
this.add('Elsa.ForEach', settings => <ForEachIcon size={settings?.size}/>);
this.add('Elsa.Delay', settings => <DelayIcon size={settings?.size}/>);
this.add('Elsa.Timer', settings => <TimerIcon size={settings?.size}/>);
@ -40,17 +37,22 @@ export class ActivityIconRegistry {
this.add('Elsa.Event', settings => <EventIcon size={settings?.size}/>);
this.add('Elsa.RunJavaScript', settings => <RunJavaScriptIcon size={settings?.size}/>);
this.add('Elsa.FlowJoin', settings => <FlowJoinIcon size={settings?.size}/>);
this.add('Elsa.SendHttpRequest', settings => <SendHttpRequestIcon size={settings?.size}/>);
this.add('Elsa.FlowNode', settings => <FlowNodeIcon size={settings?.size}/>);
this.add('Elsa.SendHttpRequest', settings => <HttpRequestIcon size={settings?.size}/>);
}
public add(activityType: ActivityType, icon: ActivityIcon) {
public add(activityType: ActivityType, icon: ActivityIconProducer) {
this.iconMap.set(activityType, icon);
}
public get(activityType: ActivityType): ActivityIcon {
public get(activityType: ActivityType): ActivityIconProducer {
return this.iconMap.get(activityType);
}
public getOrDefault(activityType: ActivityType): ActivityIconProducer {
return this.iconMap.get(activityType) ?? ((settings) => <DefaultIcon size={settings?.size}/>);
}
has(activityType: string): boolean {
return this.iconMap.has(activityType);
}

View file

@ -0,0 +1,23 @@
using Elsa.Workflows.Core.Attributes;
using Elsa.Workflows.Core.Models;
using Elsa.Workflows.Core.Services;
namespace Elsa.Workflows.Core.Activities.Flowchart.Activities;
/// <summary>
/// A simple container that executes the specified activity.
/// </summary>
[Activity("Elsa", "Flow", "A simple container that executes the specified activity.")]
public class FlowNode : ActivityBase
{
/// <summary>
/// The activity to execute.
/// </summary>
[Port]
public IActivity? Body { get; set; }
/// <inheritdoc />
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) => await context.ScheduleActivityAsync(Body, OnBodyCompletedAsync);
private async ValueTask OnBodyCompletedAsync(ActivityExecutionContext context, ActivityExecutionContext childContext) => await context.CompleteActivityAsync();
}