Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Elsa.Api.Client.Resources.ActivityDescriptors.Models;
using Microsoft.AspNetCore.Components;

namespace Elsa.Studio.Workflows.Components.WorkflowDefinitionEditor.Components.ActivityProperties;

public record ActivityInputDisplayModel(RenderFragment Editor);
public record ActivityInputDisplayModel(RenderFragment Editor, InputDescriptor InputDescriptor);
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
<MudStack Spacing="5">
@foreach (var inputModel in InputDisplayModels)
{
@inputModel.Editor
if (IsVisibleInput(inputModel))
{
@inputModel.Editor
}
}
</MudStack>
</MudForm>
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ public InputsTab()
private ICollection<OutputDescriptor> OutputDescriptors { get; set; } = new List<OutputDescriptor>();
private ICollection<ActivityInputDisplayModel> InputDisplayModels { get; set; } = new List<ActivityInputDisplayModel>();

private List<string> _selectedStates = [];
private static Dictionary<string, JsonDocument> _InputDescriptors = new();
private static Dictionary<string, string> _previousStates = new();

/// <inheritdoc />
protected override async Task OnParametersSetAsync()
{
Expand All @@ -73,20 +77,63 @@ protected override async Task OnParametersSetAsync()
InputDescriptors = ActivityDescriptor.Inputs.ToList();
OutputDescriptors = ActivityDescriptor.Outputs.ToList();
InputDisplayModels = (await BuildInputEditorModels(Activity, ActivityDescriptor, InputDescriptors)).ToList();
StateHasChanged();
}

private async Task<IEnumerable<ActivityInputDisplayModel>> BuildInputEditorModels(JsonObject activity, ActivityDescriptor activityDescriptor, ICollection<InputDescriptor> inputDescriptors)
{
var models = new List<ActivityInputDisplayModel>();
var browsableInputDescriptors = inputDescriptors.Where(x => x.IsBrowsable == true).ToList();

foreach (var inputDescriptor in browsableInputDescriptors)
foreach (var inputDescriptor in inputDescriptors)
{
var inputName = inputDescriptor.Name.Camelize();
var value = activity.GetProperty(inputName);
var wrappedInput = inputDescriptor.IsWrapped ? ToWrappedInput(value) : default;
var syntaxProvider = wrappedInput != null ? ExpressionDescriptorProvider.GetByType(wrappedInput.Expression.Type) : default;

// Check if we have a custom input
JsonDocument? InputDescriptor = null;
if (inputDescriptor.Description is not null)
{
SetInputDescriptor(inputDescriptor);
InputDescriptor = GetInputDescriptor(inputDescriptor);

if (InputDescriptor is not null)
{
string? inputType = InputDescriptor.RootElement.GetProperty("InputType").GetString();
// Check if we have conditional inputs
if (inputType == "StateDropdown")
{
if (wrappedInput is not null)
{
// Add the current value to the selected states
AddSelectedState(inputDescriptor, wrappedInput);
UpdateDescription(inputDescriptor, wrappedInput);
}
else if (wrappedInput is null && inputDescriptor.DefaultValue is not null)
{
// Add the default value to the selected states
AddSelectedState(inputDescriptor, inputDescriptor.DefaultValue);
UpdateDescription(inputDescriptor, inputDescriptor.DefaultValue);
}
else if (wrappedInput is null || (wrappedInput is not null && string.IsNullOrEmpty(wrappedInput.Expression.ToString())))
{
// Empty state && InputDescriptor exists, therefore we hide the description
inputDescriptor.Description = "";
}

}
else if (inputType == "ConditionalInput")
{
inputDescriptor.Description = InputDescriptor.RootElement.GetProperty("Description").EnumerateArray().First().GetString();
}
else
{
inputDescriptor.Description = InputDescriptor.RootElement.GetProperty("Description").GetString();
}
}
}

// Check if refresh is needed.
if (inputDescriptor.UISpecifications != null
&& inputDescriptor.UISpecifications.TryGetValue("Refresh", out var refreshInput)
Expand All @@ -109,17 +156,179 @@ private async Task<IEnumerable<ActivityInputDisplayModel>> BuildInputEditorModel
Value = input,
SelectedExpressionDescriptor = syntaxProvider,
UIHintHandler = uiHintHandler,
IsReadOnly = (Workspace?.IsReadOnly ?? false) || (inputDescriptor.IsReadOnly ?? false),
IsReadOnly = Workspace?.IsReadOnly ?? false
};

context.OnValueChanged = async v => await HandleValueChangedAsync(context, v);
context.OnValueChanged = HandleValueChangedAsync(context, inputDescriptor);
var editor = uiHintHandler.DisplayInputEditor(context);
models.Add(new ActivityInputDisplayModel(editor));
models.Add(new ActivityInputDisplayModel(editor, inputDescriptor));
}

return models;
}

private string GetInputDescriptorKey(InputDescriptor inputDescriptor)
{
return ActivityDescriptor?.Name + inputDescriptor.Name;
}

private JsonDocument? GetInputDescriptor(InputDescriptor inputDescriptor)
{
return _InputDescriptors.GetValueOrDefault(GetInputDescriptorKey(inputDescriptor));
}

private void SetInputDescriptor(InputDescriptor inputDescriptor)
{
try
{
var InputDescriptor = JsonDocument.Parse(inputDescriptor.Description!);
_InputDescriptors[GetInputDescriptorKey(inputDescriptor)] = InputDescriptor;
}
catch
{
// Ignore --> Standard Elsa 3 descriptor
}
}

private void AddSelectedState(InputDescriptor inputDescriptor, WrappedInput? v)
{
var InputDescriptor = GetInputDescriptor(inputDescriptor);
if (v is null || InputDescriptor is null) return;

var valueAsString = v!.Expression.ToString();
AddSelectedState(inputDescriptor, valueAsString);
UpdateDescription(inputDescriptor, v);
}

private void AddSelectedState(InputDescriptor inputDescriptor, object? value)
{
if (value is null) return;

var InputDescriptor = GetInputDescriptor(inputDescriptor);
if (InputDescriptor is null) return;

var inputDescriptorKey = GetInputDescriptorKey(inputDescriptor);
// Remove the previous state
var previousState = _previousStates.GetValueOrDefault(inputDescriptorKey, string.Empty);
if (!string.IsNullOrEmpty(previousState))
{
RemoveSelectedState(previousState);
}

var valueAsString = value as string;
if (value is JsonElement)
{
valueAsString = ((JsonElement)value).GetString();
}

var stateNames = InputDescriptor.RootElement.GetProperty("Options").EnumerateArray();
var stateIds = InputDescriptor.RootElement.GetProperty("Ids").EnumerateArray();

for (var i = 0; i < stateNames.Count(); ++i)
{
var current = stateNames.ElementAt(i);
if (current.GetString() == valueAsString)
{
var id = stateIds.ElementAt(i).GetString()!;

// Ensure that we have no duplicates
RemoveSelectedState(id);

_selectedStates.Add(id);
_previousStates[inputDescriptorKey] = id;
break;
}
}

StateHasChanged();
}

private void RemoveSelectedState(string name)
{
_selectedStates.Remove(name);
}

private Func<object?, Task> HandleValueChangedAsync(DisplayInputEditorContext context, InputDescriptor inputDescriptor)
{
return async v =>
{
await HandleValueChangedAsync(context, v);

var InputDescriptor = GetInputDescriptor(inputDescriptor);
// Check if we have a custom input descriptor
if (InputDescriptor is null)
{
return;
}

// Update the selected states
if (InputDescriptor.RootElement.GetProperty("InputType").GetString() == "StateDropdown")
{
AddSelectedState(inputDescriptor, v as WrappedInput);
}
};
}

public void UpdateDescription(InputDescriptor inputDescriptor, object? value)
{
if (value is null)
{
return;
}

var valueAsString = "";
if (value is WrappedInput)
{
valueAsString = ((WrappedInput)value).Expression.ToString();
}
else if (value is JsonElement)
{
valueAsString = ((JsonElement)value).GetString();
}

var InputDescriptor = GetInputDescriptor(inputDescriptor)!;
// Update with the correct description
var options = InputDescriptor.RootElement.GetProperty("Options").EnumerateArray();
var descriptions = InputDescriptor.RootElement.GetProperty("Description").EnumerateArray();
var foundDescription = false;
for (var i = 0; i < options.Count(); ++i)
{
if (options.ElementAt(i).GetString() == valueAsString)
{
inputDescriptor.Description = descriptions.ElementAt(i).GetString();
foundDescription = true;
break;
}
}

if (!foundDescription)
{
inputDescriptor.Description = "";
}
}

public bool IsVisibleInput(ActivityInputDisplayModel model)
{

var InputDescriptor = GetInputDescriptor(model.InputDescriptor);

if (InputDescriptor == null) return true;

// Check we have a match for the selected state
if (InputDescriptor.RootElement.GetProperty("InputType").GetString() == "ConditionalInput")
{
var validStates = InputDescriptor.RootElement.GetProperty("ShowForStates");
foreach (var validState in validStates.EnumerateArray())
{
var value = validState.GetString();
if (value is not null && _selectedStates.Contains(value)) return true;
}
return false;
}

return true;
}

private async Task RefreshDescriptor(JsonObject activity, ActivityDescriptor activityDescriptor, IEnumerable<InputDescriptor> inputDescriptors, InputDescriptor currentInputDescriptor)
{
var activityTypeName = activityDescriptor.TypeName;
Expand Down