From 7c913601c0cecff39d66bdab4ca5365c8d11a2d7 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 15 Nov 2020 15:52:24 +0100 Subject: [PATCH] Incremental work on activity + connection rendering --- .../ClientApp/public/tailwind.css | 6 +- .../ClientApp/public/workflow-designer.html | 4 +- .../ClientApp/src/tailwind.css | 6 +- .../ClientApp/src/workflow-designer.html | 4 +- .../Extensions/WorkflowModelExtensions.cs | 27 +++ .../Models/ActivityModel.cs | 13 ++ .../Models/ConnectionModel.cs | 4 + .../Models/WorkflowModel.cs | 46 ++++ .../Pages/Designer.razor | 16 ++ .../Pages/Designer.razor.cs | 6 + .../Pages/Designer/Designer.razor | 197 ------------------ .../{Pages/Designer => Shared}/Activity.razor | 0 .../Designer => Shared}/Activity.razor.cs | 2 +- .../Designer => Shared}/ActivityEditor.razor | 0 .../ActivityEditor.razor.cs | 2 +- .../Designer => Shared}/ActivityPicker.razor | 0 .../ActivityPicker.razor.cs | 2 +- .../Shared/WorkflowDesigner.razor | 83 ++++++++ .../WorkflowDesigner.razor.cs} | 57 ++++- 19 files changed, 267 insertions(+), 208 deletions(-) create mode 100644 src/dashboards/blazor/ElsaDashboard.Application/Extensions/WorkflowModelExtensions.cs create mode 100644 src/dashboards/blazor/ElsaDashboard.Application/Models/ActivityModel.cs create mode 100644 src/dashboards/blazor/ElsaDashboard.Application/Models/ConnectionModel.cs create mode 100644 src/dashboards/blazor/ElsaDashboard.Application/Models/WorkflowModel.cs create mode 100644 src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer.razor create mode 100644 src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer.razor.cs delete mode 100644 src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/Designer.razor rename src/dashboards/blazor/ElsaDashboard.Application/{Pages/Designer => Shared}/Activity.razor (100%) rename src/dashboards/blazor/ElsaDashboard.Application/{Pages/Designer => Shared}/Activity.razor.cs (80%) rename src/dashboards/blazor/ElsaDashboard.Application/{Pages/Designer => Shared}/ActivityEditor.razor (100%) rename src/dashboards/blazor/ElsaDashboard.Application/{Pages/Designer => Shared}/ActivityEditor.razor.cs (80%) rename src/dashboards/blazor/ElsaDashboard.Application/{Pages/Designer => Shared}/ActivityPicker.razor (100%) rename src/dashboards/blazor/ElsaDashboard.Application/{Pages/Designer => Shared}/ActivityPicker.razor.cs (98%) create mode 100644 src/dashboards/blazor/ElsaDashboard.Application/Shared/WorkflowDesigner.razor rename src/dashboards/blazor/ElsaDashboard.Application/{Pages/Designer/Designer.razor.cs => Shared/WorkflowDesigner.razor.cs} (57%) diff --git a/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/public/tailwind.css b/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/public/tailwind.css index 44d2e916e..64ac1da6d 100644 --- a/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/public/tailwind.css +++ b/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/public/tailwind.css @@ -41491,13 +41491,17 @@ div.tree { } .tree ul { - padding-top: 60px; + padding-top: 1.5rem; position: relative; transition: all 0.5s; -webkit-transition: all 0.5s; -moz-transition: all 0.5s; } +.tree ul.root { + padding-top: 0; +} + .tree li { display: inline-block; vertical-align: top; diff --git a/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/public/workflow-designer.html b/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/public/workflow-designer.html index 86afa354d..52d6260d2 100644 --- a/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/public/workflow-designer.html +++ b/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/public/workflow-designer.html @@ -289,13 +289,13 @@
-
    +
    • -
      +
      diff --git a/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/src/tailwind.css b/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/src/tailwind.css index ae70c601d..b0e2f8b15 100644 --- a/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/src/tailwind.css +++ b/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/src/tailwind.css @@ -24,13 +24,17 @@ div.tree { } .tree ul { - padding-top: 60px; + @apply pt-6; position: relative; transition: all 0.5s; -webkit-transition: all 0.5s; -moz-transition: all 0.5s; } +.tree ul.root { + @apply pt-0; +} + .tree li { display: inline-block; vertical-align: top; diff --git a/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/src/workflow-designer.html b/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/src/workflow-designer.html index 97bdaf24a..296beb45f 100644 --- a/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/src/workflow-designer.html +++ b/src/dashboards/blazor/ElsaDashboard.Application/ClientApp/src/workflow-designer.html @@ -25,14 +25,14 @@
      -
        +
        • -
          +
          GetChildActivities(this WorkflowModel workflowModel, string? parentId) + { + if (parentId == null) + { + var targetIds = workflowModel.Connections.Select(x => x.TargetId).Distinct().ToLookup(x => x); + var children = workflowModel.Activities.Where(x => !targetIds.Contains(x.ActivityId)).ToList(); + + return children; + } + else + { + var targetIds = workflowModel.Connections.Where(x => x.SourceId == parentId).Select(x => x.TargetId).Distinct().ToLookup(x => x); + var children = workflowModel.Activities.Where(x => targetIds.Contains(x.ActivityId)).ToList(); + + return children; + } + } + } +} \ No newline at end of file diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Models/ActivityModel.cs b/src/dashboards/blazor/ElsaDashboard.Application/Models/ActivityModel.cs new file mode 100644 index 000000000..5c71197d7 --- /dev/null +++ b/src/dashboards/blazor/ElsaDashboard.Application/Models/ActivityModel.cs @@ -0,0 +1,13 @@ +namespace ElsaDashboard.Application.Models +{ + public record ActivityModel + { + public ActivityModel() + { + } + + public ActivityModel(string activityId, string type) => (ActivityId, Type) = (activityId, type); + public string ActivityId { get; init; } + public string Type { get; init; } + } +} \ No newline at end of file diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Models/ConnectionModel.cs b/src/dashboards/blazor/ElsaDashboard.Application/Models/ConnectionModel.cs new file mode 100644 index 000000000..22ecf7e7d --- /dev/null +++ b/src/dashboards/blazor/ElsaDashboard.Application/Models/ConnectionModel.cs @@ -0,0 +1,4 @@ +namespace ElsaDashboard.Application.Models +{ + public sealed record ConnectionModel(string SourceId, string? TargetId, string Outcome); +} \ No newline at end of file diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Models/WorkflowModel.cs b/src/dashboards/blazor/ElsaDashboard.Application/Models/WorkflowModel.cs new file mode 100644 index 000000000..ecdf0af19 --- /dev/null +++ b/src/dashboards/blazor/ElsaDashboard.Application/Models/WorkflowModel.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Elsa.Client.Models; + +namespace ElsaDashboard.Application.Models +{ + public sealed record WorkflowModel + { + public WorkflowModel() + { + } + + public WorkflowModel(string? name, IEnumerable activities, IEnumerable connections) => (Name, Activities, Connections) = (name, activities.ToImmutableList(), connections.ToImmutableList()); + public WorkflowModel(string? name) => Name = (name); + public string? Name { get; init; } + public IImmutableList Activities { get; init; } = Array.Empty().ToImmutableList(); + public IImmutableList Connections { get; init; } = Array.Empty().ToImmutableList(); + + public static WorkflowModel Blank() => new("New Workflow"); + + public static WorkflowModel Demo() => new WorkflowModel + { + Activities = new [] + { + new ActivityModel("activity-1", "WriteLine"), + new ActivityModel("activity-2", "WriteLine"), + new ActivityModel("activity-3", "WriteLine"), + new ActivityModel("activity-4", "WriteLine"), + new ActivityModel("activity-5", "WriteLine"), + }.ToImmutableList(), + + Connections = new[] + { + new ConnectionModel("activity-1", "activity-2", "Done"), + new ConnectionModel("activity-2", "activity-3", "Done"), + new ConnectionModel("activity-2", "activity-4", "Done"), + new ConnectionModel("activity-4", "activity-5", "Done") + }.ToImmutableList() + }; + + public WorkflowModel AddActivity(ActivityModel activity) => this with { Activities = Activities.Add(activity)}; + public WorkflowModel RemoveActivity(ActivityModel activity) => this with { Activities = Activities.Where(x => x != activity).ToImmutableList()}; + } +} \ No newline at end of file diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer.razor b/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer.razor new file mode 100644 index 000000000..7a168ace5 --- /dev/null +++ b/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer.razor @@ -0,0 +1,16 @@ +@page "/designer" +
          +
          +

          + Workflow Editor +

          +
          +
          + + + +
          +
          + \ No newline at end of file diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer.razor.cs b/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer.razor.cs new file mode 100644 index 000000000..d27d81f02 --- /dev/null +++ b/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer.razor.cs @@ -0,0 +1,6 @@ +namespace ElsaDashboard.Application.Pages +{ + partial class Designer + { + } +} \ No newline at end of file diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/Designer.razor b/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/Designer.razor deleted file mode 100644 index f697081ae..000000000 --- a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/Designer.razor +++ /dev/null @@ -1,197 +0,0 @@ -@implements IAsyncDisposable -@page "/designer" -
          -
          -

          - Workflow Editor -

          -
          -
          - - - -
          -
          -
          -
          -
          -
          - -
          -
          -
            -
          • -
            - -
            - - - -
            -
            -
              -
            • - -
                -
              • - -
                  -
                • -
                  - Yes -
                  -
                    -
                  • - -
                      -
                    • - -
                        -
                      • -
                        - No -
                        -
                          -
                        • - -
                        • -
                        -
                      • -
                      • -
                        - Yes -
                        -
                          -
                        • - -
                            -
                          • -
                            - Yes -
                            -
                              -
                            • - -
                            • -
                            -
                          • -
                          -
                        • -
                        -
                      • -
                      -
                    • -
                    -
                  • -
                  -
                • -
                • -
                  - No -
                  -
                    -
                  • - -
                      -
                    • - -
                        -
                      • -
                        - No -
                        -
                          -
                        • - -
                            -
                          • -
                            - Yes -
                            -
                              -
                            • - -
                                -
                              • - -
                              • -
                              -
                            • -
                            -
                          • -
                          -
                        • -
                        -
                      • -
                      • -
                        - Yes -
                        -
                          -
                        • - -
                            -
                          • -
                            - Yes -
                            -
                              -
                            • - -
                                -
                              • -
                                - + -
                                -
                                  -
                                • - -
                                    -
                                  • - -
                                      -
                                    • - -
                                    • -
                                    -
                                  • -
                                  -
                                • -
                                -
                              • -
                              -
                            • -
                            -
                          • -
                          -
                        • -
                        -
                      • -
                      -
                    • -
                    -
                  • -
                  -
                • -
                -
              • -
              -
            • -
            -
          • -
          -
          -
          -
          -
          -
          - -
          \ No newline at end of file diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/Activity.razor b/src/dashboards/blazor/ElsaDashboard.Application/Shared/Activity.razor similarity index 100% rename from src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/Activity.razor rename to src/dashboards/blazor/ElsaDashboard.Application/Shared/Activity.razor diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/Activity.razor.cs b/src/dashboards/blazor/ElsaDashboard.Application/Shared/Activity.razor.cs similarity index 80% rename from src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/Activity.razor.cs rename to src/dashboards/blazor/ElsaDashboard.Application/Shared/Activity.razor.cs index 314bfaa8d..5ca6d62b0 100644 --- a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/Activity.razor.cs +++ b/src/dashboards/blazor/ElsaDashboard.Application/Shared/Activity.razor.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; -namespace ElsaDashboard.Application.Pages.Designer +namespace ElsaDashboard.Application.Shared { partial class Activity { diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/ActivityEditor.razor b/src/dashboards/blazor/ElsaDashboard.Application/Shared/ActivityEditor.razor similarity index 100% rename from src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/ActivityEditor.razor rename to src/dashboards/blazor/ElsaDashboard.Application/Shared/ActivityEditor.razor diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/ActivityEditor.razor.cs b/src/dashboards/blazor/ElsaDashboard.Application/Shared/ActivityEditor.razor.cs similarity index 80% rename from src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/ActivityEditor.razor.cs rename to src/dashboards/blazor/ElsaDashboard.Application/Shared/ActivityEditor.razor.cs index 5c4675405..b5e9eee26 100644 --- a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/ActivityEditor.razor.cs +++ b/src/dashboards/blazor/ElsaDashboard.Application/Shared/ActivityEditor.razor.cs @@ -1,7 +1,7 @@ using Elsa.Client.Models; using Microsoft.AspNetCore.Components; -namespace ElsaDashboard.Application.Pages.Designer +namespace ElsaDashboard.Application.Shared { partial class ActivityEditor { diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/ActivityPicker.razor b/src/dashboards/blazor/ElsaDashboard.Application/Shared/ActivityPicker.razor similarity index 100% rename from src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/ActivityPicker.razor rename to src/dashboards/blazor/ElsaDashboard.Application/Shared/ActivityPicker.razor diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/ActivityPicker.razor.cs b/src/dashboards/blazor/ElsaDashboard.Application/Shared/ActivityPicker.razor.cs similarity index 98% rename from src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/ActivityPicker.razor.cs rename to src/dashboards/blazor/ElsaDashboard.Application/Shared/ActivityPicker.razor.cs index 3afdacc00..e656e6f96 100644 --- a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/ActivityPicker.razor.cs +++ b/src/dashboards/blazor/ElsaDashboard.Application/Shared/ActivityPicker.razor.cs @@ -7,7 +7,7 @@ using ElsaDashboard.Application.Models; using ElsaDashboard.Shared.Rpc; using Microsoft.AspNetCore.Components; -namespace ElsaDashboard.Application.Pages.Designer +namespace ElsaDashboard.Application.Shared { partial class ActivityPicker { diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Shared/WorkflowDesigner.razor b/src/dashboards/blazor/ElsaDashboard.Application/Shared/WorkflowDesigner.razor new file mode 100644 index 000000000..dd6b26d52 --- /dev/null +++ b/src/dashboards/blazor/ElsaDashboard.Application/Shared/WorkflowDesigner.razor @@ -0,0 +1,83 @@ +@using ElsaDashboard.Application.Extensions + +
          +
          +
          +
          + +
          +
          +
            +
          • +
            + +
            + @RenderTree(GetRootActivities(), true) +
          • +
          +
          +
          +
          +
          +
          + +
          + +@code { + + private RenderFragment RenderTree(IEnumerable activities, bool isRoot) + { + var list = activities.ToList(); + var cssClass = isRoot ? "root" : default; + + return + @ + @if (list.Any()) + { +
            + @foreach (var activity in list) + { + var activityId = activity.ActivityId; + var plusId = $"{activityId}-button-plus"; + var children = Model.GetChildActivities(activity.ActivityId).ToList(); + +
          • +
            + + @if (isRoot) + { + var beforePlusId = $"start-button-plus-{activity.ActivityId}"; +
            + + + +
            + } + + + @if (children.Any()) + { +
            + + + +
            + } +
            + @if (children.Any()) + { + @RenderTree(children, false) + } +
          • + } +
          + } +
          ; + } + +} \ No newline at end of file diff --git a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/Designer.razor.cs b/src/dashboards/blazor/ElsaDashboard.Application/Shared/WorkflowDesigner.razor.cs similarity index 57% rename from src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/Designer.razor.cs rename to src/dashboards/blazor/ElsaDashboard.Application/Shared/WorkflowDesigner.razor.cs index 16a0b8324..559be983d 100644 --- a/src/dashboards/blazor/ElsaDashboard.Application/Pages/Designer/Designer.razor.cs +++ b/src/dashboards/blazor/ElsaDashboard.Application/Shared/WorkflowDesigner.razor.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Elsa.Client.Models; using ElsaDashboard.Application.Extensions; @@ -8,10 +10,11 @@ using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using Microsoft.JSInterop; -namespace ElsaDashboard.Application.Pages.Designer +namespace ElsaDashboard.Application.Shared { - partial class Designer + partial class WorkflowDesigner : IAsyncDisposable { + [Parameter] public WorkflowModel Model { private get; set; } = WorkflowModel.Demo(); [Inject] private IJSRuntime JS { get; set; } = default!; [Inject] private IFlyoutPanelService FlyoutPanelService { get; set; } = default!; private IJSObjectReference _designerModule = default!; @@ -21,7 +24,10 @@ namespace ElsaDashboard.Application.Pages.Designer if (firstRender) { _designerModule = await JS.InvokeAsync("import", "./_content/ElsaDashboard.Application/workflowDesigner.js"); + } + + await RepaintConnections(); } public async ValueTask DisposeAsync() @@ -30,6 +36,50 @@ namespace ElsaDashboard.Application.Pages.Designer await _designerModule.DisposeAsync(); } + private IEnumerable GetRootActivities() => Model.GetChildActivities(null); + + private async ValueTask RepaintConnections() + { + var rootActivities = Model.GetChildActivities(null).ToList(); + + var rootConnections = rootActivities.SelectMany(x => + { + return new[] + { + new + { + sourceId = "start-button", + targetId = $"start-button-plus-{x.ActivityId}" + }, + new + { + sourceId = $"start-button-plus-{x.ActivityId}", + targetId = x.ActivityId + }, + }; + }).ToArray(); + + var connections = Model.Connections.SelectMany(x => + { + return new[] + { + new + { + sourceId = x.SourceId, + targetId = $"{x.SourceId}-button-plus" + }, + new + { + sourceId = $"{x.SourceId}-button-plus", + targetId = x.TargetId + }, + }; + }).ToArray(); + + var allConnections = rootConnections.Concat(connections); + await _designerModule.InvokeVoidAsync("updateConnections", (object) allConnections); + } + private async ValueTask ClosePanelAsync() => await FlyoutPanelService.HideAsync(); private async ValueTask ShowActivityPickerAsync() @@ -42,7 +92,10 @@ namespace ElsaDashboard.Application.Pages.Designer private async ValueTask AddActivityAsync(ActivityDescriptor activityDescriptor) { + var activity = new ActivityModel(Guid.NewGuid().ToString("N"), activityDescriptor.Type); + Model = Model.AddActivity(activity); await FlyoutPanelService.HideAsync(); + await RepaintConnections(); } private async ValueTask OnStartClick() => await ShowActivityPickerAsync();