Blazor-Diagrams / Blazor.Diagrams

A fully customizable and extensible all-purpose diagrams library for Blazor
https://blazor-diagrams.zhaytam.com
MIT License
921 stars 177 forks source link

Review performance #217

Open zHaytam opened 1 year ago

zHaytam commented 1 year ago

Other improvements are to be found on the way.

mRmEEeZ commented 1 year ago

@zHaytam hello, can we somehow turn it off untill it gets better performance wise ? when i try to move a node it lags so hard

thanks and great work !

zHaytam commented 1 year ago

Are you using Server Side Blazor? Moving a node doesn't use any javascript

mRmEEeZ commented 1 year ago

No, im using blazor wasm .net 6 The early versions has no problmes but the latest one does become laggy ,much laggy

Im using custom nodes btw but they are simple nodes with 2 ports only

zHaytam commented 1 year ago

That's weird. What version are you using?

mRmEEeZ commented 1 year ago

Latest version

zHaytam commented 1 year ago

Latest stable version or prelease? 2.1.6 or 3.x

mRmEEeZ commented 1 year ago

2.1.6

zHaytam commented 1 year ago

Is your project private? Would it be possible for me to have a look at it? If not, can you send me at least the code for the nodes and how you create the diagram so that I try to reproduce the issue?

mRmEEeZ commented 1 year ago

Problems occurs when a link is created and attached then trying to move the node around in the canvas it become laggy

ProcessWorkFlowVisualizer.razor `

    </div>
    <CascadingValue Value="DiagramManager">
        <DiagramCanvas></DiagramCanvas>
    </CascadingValue>
    @* <CascadingValue Name="DiagramManager" Value="DiagramManager">
        <DiagramCanvas>
            <Widgets>
                <Navigator Width="200" Height="150" DefaultStyle="true"></Navigator>
            </Widgets>
        </DiagramCanvas>
    </CascadingValue> *@
    <div style="position: absolute; top: 90%; left: 3%;">
        <NotificationComponent @ref="UpperNotificationComponentRef.Ref" />
        <NotificationComponent @ref="LowerNotificationComponentRef.Ref" />
    </div>
</div>
@{
    CreateNewProcessFlowDialog = @<GroupComponent Title="{New} {ProcessFlow}" Icon="code-branch">
            <CascadingValue Name="Model" Value="Model">
                <LocalizedNameComponent Width="12" />
                <FilterableViewElement Width="12" TModel="ProcessWorkFlow" TProperty="ProcessType"
                                   PropertySelector="a=>a.ProcessType" FetchDataAsync="GetProcessTypes" />
                <button class="btn btn-sm btn-primary" @onclick="CreateNewProcessFlow">@Localizer["Ok"]</button>
            </CascadingValue>
            </GroupComponent>;

  OpenProcessFlowDialog = @<GroupComponent Title="{Open} {ProcessFlow}" Icon="code-branch">
        <CascadingValue Name="Model" Value="VisualizerState">
            <FilterableViewElement Width="12" TModel="ProcessFlowVisualizerState" TProperty="ProcessType"
                               PropertySelector="a=>a.ProcessType" FetchDataAsync="GetProcessTypes" />
            <FilterableViewElement Width="12" TModel="ProcessFlowVisualizerState" TProperty="ProcessWorkFlow" @ref="_filterableElement"
                               PropertySelector="a=>a.CurrentWorkFlow" FetchDataAsync="GetProcessFlows" />
            <button class="btn btn-sm btn-primary" @onclick="OpenProcessFlow">@Localizer["Open"]</button>
        </CascadingValue>
    </GroupComponent>;
}

` ProcessWorkFlowVisualizer.razor.cs

public class ProcessFlowVisualizerState : BaseDocument
{
    private ProcessType _type;

    public bool HasUnsavedChanges { get; set; }
    public bool HasOpenedWorkFlow { get; set; }
    public bool HasSelectedDeletableComponent { get; set; }
    public string SelectedComponent { get; set; }
    public int SelectedProcessFlowIndex { get; set; } = -1;
    public ICollection<ProcessWorkFlow> Flows { get; set; }
    public ProcessType ProcessType { get => _type; set => SetField(ref _type, value); }
    public ProcessWorkFlow CurrentWorkFlow { get; set; }

    public override string ID => "_visualizerState";
}

[Route("system/process/processFlow")]
public partial class ProcessWorkFlowVisualizer : ViewModelComponentBase<ProcessWorkFlow, ProcessWorkFlowVisualizer>
    , IDisposable
{
    FilterableViewElement<ProcessFlowVisualizerState, ProcessWorkFlow> _filterableElement;
    private Diagram DiagramManager { get; set; }
    ProcessFlowVisualizerState VisualizerState { get; set; } = new ProcessFlowVisualizerState();

    //VisualSystemNode _tempSystemNodeDto;
    //VisualTimeNode _tempTimerNodeDto;
    //VisualProcessStateNode _tempProcessState;

    RenderFragment SystemNodeDialog;
    RenderFragment TransferNodeDialog;
    RenderFragment PrintingNodeDialog;
    RenderFragment TimerNodeDialog;
    RenderFragment ProcessStateNodeDialog;
    RenderFragment ProcessWorkFlowNodeDialog;
    RenderFragment ConditionNodeDialog;

    RenderFragment CreateNewProcessFlowDialog;
    RenderFragment OpenProcessFlowDialog;

    Func<Task> OnSystemNodeOkClick;
    Func<Task> OnPrintingNodeOkClick;
    Func<Task> OnTransferNodeOkClick;
    Func<Task> OnTimerNodeOkClick;
    Func<Task> OnProcessStateNodeOkClick;
    Func<Task> OnProcessWorkFlowNodeOkClick;
    Func<Task> OnNoClick;
    public ProcessType SelectedProcessType { get; set; }

    [Inject] IProcessModelsProvider ProcessModelsProvider { get; set; }

    protected override void OnInitialized()
    {
        base.OnInitialized();
        SetDiagramManager();
        VisualizerState.PropertyChanged += (a, b) =>
        {
            if (b.PropertyName == "ProcessType")
                _filterableElement?.GetData();
        };
    }

    void SetDiagramManager()
    {
        var options = new DiagramOptions
        {
            DeleteKey = "Delete", // What key deletes the selected nodes/links
            Zoom = new DiagramZoomOptions(){Inverse = true, }, // Whether to inverse the direction of the zoom when using the wheel
            //Links= new DiagramLinkOptions() { DefaultLinkType = LinkType.Curved,  },
            // DefaultLinkType = LinkType.Curved,
            DefaultNodeComponent = null, // Default component for nodes,
            AllowMultiSelection = false, // Whether to allow multi selection using CTRL
            // GroupingEnabled = true,
            GridSize = 30,
            Links = new DiagramLinkOptions
            {
                DefaultRouter = Routers.Orthogonal,                    
                DefaultPathGenerator = PathGenerators.Straight,
                DefaultLinkComponent = null,
                // DefaultColor = "blue",
                // DefaultSelectedColor = "red"
            },                
        };

        DiagramManager = new Diagram(options);

        // DiagramManager = new DiagramManager(options);
        // DiagramManager.LinkAdded += OnLinkAdded;
        // DiagramManager.LinkAttached += OnLinkAttached;
        // DiagramManager.LinkRemoved += OnLinkRemoved;
        // DiagramManager.NodeAdded += OnNodeAdded;
        // DiagramManager.NodeRemoved += OnNodeRemoved;

            DiagramManager.Links.Added += OnLinkAdded;
            DiagramManager.Links.Removed += OnLinkRemoved;
            DiagramManager.Nodes.Added += OnNodeAdded;
            DiagramManager.Nodes.Removed += OnNodeRemoved;

        DiagramManager.RegisterModelComponent<VisualEntryNode, StartNodeComponent>();
        DiagramManager.RegisterModelComponent<VisualTransferNode, TransferNodeComponent>();
        DiagramManager.RegisterModelComponent<VisualProcessStateNode, ProcessStateComponent>();
        DiagramManager.RegisterModelComponent<VisualPrintingNode, PrintingNodeComponent>();
        DiagramManager.RegisterModelComponent<VisualSystemNode, SystemNodeComponent>();
        DiagramManager.RegisterModelComponent<VisualTimeNode, TimerNodeComponent>();
        DiagramManager.RegisterModelComponent<VisualConditionNode, ConditionNodeComponent>();
        DiagramManager.RegisterModelComponent<VisualProcessWorkFlowNode, ProcessWorkFlowNodeComponent>();

        //_tempSystemNodeDto = new VisualSystemNode();
        //_tempTimerNodeDto = new VisualTimeNode();
        //_tempProcessState = new VisualProcessStateNode();

        OnSystemNodeOkClick = UpdateSystemNode;
        OnTimerNodeOkClick = UpdateTimerNode;
        OnProcessStateNodeOkClick = UpdateProcessStateNode;
        OnProcessWorkFlowNodeOkClick = UpdateProcessWorkFlowNode;
        OnNoClick = CancelUpdatingNode;
    }

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
    }

    async Task<ReadOnlyObservableCollection<ProcessType>> GetProcessTypes(ProcessType type)
    {
        return new ReadOnlyObservableCollection<ProcessType>(new ObservableCollection<ProcessType>(ProcessModelsProvider.ProcessTypes));
    }

    async Task<ReadOnlyObservableCollection<ProcessWorkFlow>> GetProcessFlows(ProcessWorkFlow flow)
    {
        if (VisualizerState.ProcessType != null)
        {
            var _stages = MongoQuery.AsQuerying<ProcessWorkFlow>()
                //.Match(a => a.ProcessTypeRef == VisualizerState.ProcessType.Id)
                .Unwind(a => a.Nodes)
                    .LookUp(a => a.GroupsRefs, a => a.Id, a => a.Groups)
                    .Windup()
                .Stages;
            var _response = await Repository.Get(new MongoPaging<ProcessWorkFlow>() { Stages = _stages });
            if (_response.Status == ResponseStatus.OK)
            {
                _processWorkFlows = _response.Result.ToList();
                return new ReadOnlyObservableCollection<ProcessWorkFlow>
                    (new ObservableCollection<ProcessWorkFlow>(_processWorkFlows.Where(a => a.ProcessTypeRef == VisualizerState.ProcessType.Id)));
            }
        }
        return new ReadOnlyObservableCollection<ProcessWorkFlow>(new ObservableCollection<ProcessWorkFlow>());
    }

    List<ProcessWorkFlow> _processWorkFlows = new();
    void OpenProcessFlow()
    {
        isOpening = true;
        List<BaseNode> nodeModels = new List<BaseNode>();
        Dictionary<PortModel, PortModel[]> Links = new Dictionary<PortModel, PortModel[]>();

        VisualizerState.CurrentWorkFlow.Nodes
            .ForEach(n =>
            {
                if (n is EntryNode)
                {
                    var k = n as EntryNode; //new EntryNode(n);
                    k.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
                    nodeModels.Add(new VisualEntryNode(k) { OnClick = OnNodeClick });
                }
                else if (n is ProcessWorkFlowNode)
                {
                    var k = n as ProcessWorkFlowNode; //new ProcessWorkFlowNode(new SelectableNode(n));
                    k.WorkFlow = _processWorkFlows.FirstOrDefault(a => a.Id == k.ProcessWorkFlowRef);
                    k.EntryNode = k.WorkFlow?.Nodes.FirstOrDefault(a => a.Id == k.EntryNodeRef) as SelectableNode;
                    k.ClosingNodes = k.WorkFlow?.Nodes.Where(a => k.ClosingNodesRefs.Contains(a.Id))
                    .Select(a => (ProcessStateNode)a)
                    .ToObservableCollection();
                    if (k.ClosingNodes == null)
                        k.ClosingNodes = new ObservableCollection<ProcessStateNode>();
                    k.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
                    k.ClosingNodes.CollectionChanged += (a, b) => OnNodePropertyChanged(a, new PropertyChangedEventArgs("ClosingNodes"));
                    nodeModels.Add(new VisualProcessWorkFlowNode(k) { OnClick = OnNodeClick });
                }
                else if (n is ProcessStateNode)
                {
                    var k = n as ProcessStateNode;
                    k.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
                    nodeModels.Add(new VisualProcessStateNode(k) { OnClick = OnNodeClick });
                }
                else if (n is TransferNode)
                {
                    var k = n as TransferNode;
                    k.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);                        
                    try
                    {
                        k.ChildType = ProcessModelsProvider.ProcessTypes.First(a=>a.Id == k.ChildTypeRef);                            
                    }
                    catch (Exception ex)
                    {

                    }
                    nodeModels.Add(new VisualTransferNode(k) { OnClick = OnNodeClick });
                }
                else if (n is SystemNode)
                {
                    var k = n as SystemNode;
                    k.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
                    nodeModels.Add(new VisualSystemNode(k) { OnClick = OnNodeClick });
                }
                else if (n is PrintingNode)
                {
                    var k = n as PrintingNode;
                    k.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
                    nodeModels.Add(new VisualPrintingNode(k) { OnClick = OnNodeClick });
                }
                else if (n is TimeNode)
                {
                    var k = n as TimeNode;
                    k.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
                    nodeModels.Add(new VisualTimeNode(k) { OnClick = OnNodeClick });
                }
                else if (n is ConditionNode)
                {
                    var k = n as ConditionNode;
                    k.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
                    nodeModels.Add(new VisualConditionNode(k) { OnClick = OnNodeClick });
                }
                else
                    throw new NotImplementedException($"Visualizer node is not implmeneted for type:[{n.GetType().Name}]");
            });

        nodeModels.ForEach(n => DiagramManager.Nodes.Add(n));
        foreach (BaseNode nodeModel in nodeModels)
        {
            nodeModels.Where(a => nodeModel.AbstractNode.NextNodesRefs.Any(c => c == a.AbstractNode.Id))
                .Select(a => a.GetPort(PortAlignment.Left))
                .ForEach(p => DiagramManager.Links.Add(new LinkModel(nodeModel.GetPort(PortAlignment.Right), p)));
        }

        // DiagramManager.AddNodes(nodeModels.ToArray());
        // foreach (BaseNode nodeModel in nodeModels)
        // {
        //     nodeModels.Where(a => nodeModel.AbstractNode.NextNodesRefs.Any(c => c == a.AbstractNode.Id))
        //         .Select(a => a.GetPort(PortAlignment.Left))
        //         .ForEach(p => DiagramManager.AddLink(nodeModel.GetPort(PortAlignment.Right), p));
        // }

        Model = VisualizerState.CurrentWorkFlow;
        Model.ProcessType = VisualizerState.ProcessType;
        Model.Nodes = nodeModels.Select(a => a.AbstractNode).ToList();
        VisualizerState.HasOpenedWorkFlow = true;
        VisualizerState.HasUnsavedChanges = true;
        SlideFormRef.Ref.Hide();
        isOpening = false;
    }

    private void OnNodePropertyChanged(object a, PropertyChangedEventArgs b)
    {
        if (b.PropertyName == "Groups")
            (a as Node).GroupsRefs = (a as Node).Groups.Select(a => a.Id).ToList();
        else if (b.PropertyName == "IsAutoTrigger")
        {
            if ((a as SelectableNode).IsAutoTrigger)
                (a as SelectableNode).Onsets.Clear();
        }
        else if (a is SystemNode)
        { }
        else if (a is PrintingNode)
        { }
        else if (a is TimeNode)
        { }
        else if(a is TransferNode)
        {
            // var _node = a as TransferNode;
            // if(b.PropertyName == "ChildType")
            //     _node.ChildTypeRef = _node.ChildType.Id;
        }
        else if (a is ProcessWorkFlowNode)
        {
            var _node = a as ProcessWorkFlowNode;
            if (b.PropertyName == "WorkFlow")
            {
                _node.ProcessWorkFlowRef = _node.WorkFlow?.Id;
                _node.ChildProcessTypeRef = _node.WorkFlow?.ProcessTypeRef;
                DiagramManager.Nodes.First(a => a.Id == _node.Id)
                .AllLinks
                .Where(a => a.TargetPort.Parent != null && a.TargetPort.Parent is VisualConditionNode)
                .Select(l => l.TargetPort.Parent as VisualConditionNode)
                .ForEach(n => { 
                    n.Node.IsChildProcess = true;
                    n.Node.ChildProcessTypeRef = _node.ChildProcessTypeRef;
                });

            }
            else if (b.PropertyName == "ClosingNodes")
            {
                _node.ClosingNodesRefs = _node.ClosingNodes?.Select(a => a.Id).ToList();
            }
            else if (b.PropertyName == "EntryNode")
            {
                _node.EntryNodeRef = _node.EntryNode?.Id;
            }
            else if (b.PropertyName == "NodeType")
            {
                if (_node.NodeType == ProcessWorkFlowType.StartAndContinue)
                {
                    _node.ClosingNodes = null;
                }
            }
        }
        else if (a is ProcessStateNode)
        { }
    }

    void CreateNewProcessFlow()
    {
        ClearBoard();
        SetupModel();

        VisualizerState.HasOpenedWorkFlow = true;
        VisualizerState.HasSelectedDeletableComponent = false;
        VisualizerState.HasUnsavedChanges = true;
        VisualizerState.SelectedComponent = null;

        StateHasChanged();
        SlideFormRef.Ref.Hide();
    }

    // private void OnGroupAdded(Blazor.Diagrams.Core.Models.Group obj)
    // {

    // }

    public void OnNodeRemoved(NodeModel obj)
    {
        try
        {
            Model.Nodes.Remove(Model.Nodes.First(a => a.Id == obj.Id));
            var _nodes = Model.Nodes.Where(a => a.NextNodesRefs.Any(a => a == obj.Id)).ToList();
            foreach (var _node in _nodes)
            {
                _node.NextNodesRefs.Remove(obj.Id);
            }
        }
        catch(Exception e) { Helper.DebugConsole(e.Message); }
    }

    public void OnNodeAdded(NodeModel obj)
    {

    }

    // public void OnLinkRemoved(LinkModel obj)
    // {
    //     try
    //     {

    //         if (obj.IsAttached)
    //         {
    //             Model.Nodes.First(a => a.Id == obj.SourcePort.Parent.Id)
    //                 .NextNodesRefs.Remove(obj.TargetPort.Parent.Id);
    //         }

    //         if (obj.SourcePort.Parent is VisualProcessWorkFlowNode && obj.TargetPort.Parent is VisualConditionNode)
    //         {
    //             (obj.TargetPort.Parent as VisualConditionNode).Node.IsChildProcess = false;
    //             (obj.TargetPort.Parent as VisualConditionNode).Node.ChildProcessTypeRef = null;
    //         }
    //     }
    //     catch { }
    // }

    // private void OnLinkAttached(LinkModel obj)
    // {
    //     if (obj.SourcePort.Alignment == PortAlignment.Left || obj.TargetPort.Alignment == PortAlignment.Right)
    //     {
    //         // DiagramManager.RemoveLink(obj);
    //         DiagramManager.Links.Remove(obj);
    //         return;
    //     }
    //     if (obj.SourcePort.Parent is VisualEntryNode
    //     && (!(obj.TargetPort.Parent is VisualProcessStateNode) && !(obj.TargetPort.Parent is VisualProcessWorkFlowNode)))
    //     {
    //         // DiagramManager.RemoveLink(obj);
    //         DiagramManager.Links.Remove(obj);
    //         return;
    //     }
    //     if(obj.SourcePort.Parent is VisualProcessWorkFlowNode && obj.TargetPort.Parent is VisualTransferNode)
    //         (obj.TargetPort.Parent as VisualTransferNode).Node.ChildType
    //          = ProcessModelsProvider.ProcessTypes.FirstOrDefault(a=>a.Id == (obj.SourcePort.Parent as VisualProcessWorkFlowNode).Node.ChildProcessTypeRef);

    //     if (obj.SourcePort.Parent is VisualProcessWorkFlowNode && obj.TargetPort.Parent is VisualConditionNode)
    //         {
    //             (obj.TargetPort.Parent as VisualConditionNode).Node.IsChildProcess = true;
    //             (obj.TargetPort.Parent as VisualConditionNode).Node.ChildProcessTypeRef
    //              = (obj.SourcePort.Parent as VisualProcessWorkFlowNode).Node.ChildProcessTypeRef;
    //         }

    //     Model.Nodes.First(a => a.Id == obj.SourcePort.Parent.Id)
    //         .NextNodesRefs.Add(obj.TargetPort.Parent.Id);
    // }

    // private void OnLinkAdded(LinkModel obj)
    // {
    // }

    public void OnLinkRemoved(BaseLinkModel obj)
    {
        try
        {

            if (obj.IsAttached)
            {
                Model.Nodes.First(a => a.Id == obj.SourcePort.Parent.Id)
                    .NextNodesRefs.Remove(obj.TargetPort.Parent.Id);
            }

            if (obj.SourcePort.Parent is VisualProcessWorkFlowNode && obj.TargetPort.Parent is VisualConditionNode)
            {
                (obj.TargetPort.Parent as VisualConditionNode).Node.IsChildProcess = false;
                (obj.TargetPort.Parent as VisualConditionNode).Node.ChildProcessTypeRef = null;
            }
        }
        catch(Exception e) { Helper.DebugConsole(e.Message); }
    }

    bool isOpening = false;
    void OnLinkAttached(BaseLinkModel obj,PortModel? source,PortModel? target)
    {
        if (obj.SourcePort.Alignment == PortAlignment.Left || obj.TargetPort.Alignment == PortAlignment.Right)
        {
            // DiagramManager.RemoveLink(obj);                         
            DiagramManager.Links.Remove(obj);
            return;
        }
        if (obj.SourcePort.Parent is VisualEntryNode
        && (!(obj.TargetPort.Parent is VisualProcessStateNode) && !(obj.TargetPort.Parent is VisualProcessWorkFlowNode)))
        {
            // DiagramManager.RemoveLink(obj);
            DiagramManager.Links.Remove(obj);
            return;
        }
        if(obj.SourcePort.Parent is VisualProcessWorkFlowNode && obj.TargetPort.Parent is VisualTransferNode)
            (obj.TargetPort.Parent as VisualTransferNode).Node.ChildType
             = ProcessModelsProvider.ProcessTypes.FirstOrDefault(a=>a.Id == (obj.SourcePort.Parent as VisualProcessWorkFlowNode).Node.ChildProcessTypeRef);

        if (obj.SourcePort.Parent is VisualProcessWorkFlowNode && obj.TargetPort.Parent is VisualConditionNode)
            {
                (obj.TargetPort.Parent as VisualConditionNode).Node.IsChildProcess = true;
                (obj.TargetPort.Parent as VisualConditionNode).Node.ChildProcessTypeRef
                 = (obj.SourcePort.Parent as VisualProcessWorkFlowNode).Node.ChildProcessTypeRef;
            }

        Model.Nodes.First(a => a.Id == obj.SourcePort.Parent.Id)
            .NextNodesRefs.Add(obj.TargetPort.Parent.Id);
    }

    private void OnLinkAdded(BaseLinkModel obj)
    {
        obj.TargetPortChanged += (a,b,c) => OnLinkAttached(a,b,c);
    }

    private void NewStartNodeClicked()
    {
        double x = new Random().Next(1, 300), y = new Random().Next(1, 300);
        var node = new VisualEntryNode(new Point(x, y));
        // DiagramManager.AddNode(node);
        DiagramManager.Nodes.Add(node);
        node.Node.Id = node.Id;
        node.Node.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
        Model.Nodes.Add(node.Node);
    }

    private void NewProcessStateNodeClicked()
    {
        double x = new Random().Next(1, 300), y = new Random().Next(1, 300);
        var node = new VisualProcessStateNode(new Point(x, y));
        node.Node.Id = node.Id;
        node.OnClick = OnNodeClick;
        // DiagramManager.AddNode(node);
        DiagramManager.Nodes.Add(node);
        node.Node.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
        Model.Nodes.Add(node.Node);
    }

    private void NewTimerNodeClicked()
    {
        double x = new Random().Next(1, 300), y = new Random().Next(1, 300);
        var node = new VisualTimeNode(new Point(x, y));
        // DiagramManager.AddNode(node);
        DiagramManager.Nodes.Add(node);
        node.OnClick = OnNodeClick;
        node.Node.Id = node.Id;
        node.Node.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
        Model.Nodes.Add(node.Node);
    }

    private void NewSystemNodeClicked()
    {
        double x = new Random().Next(1, 300), y = new Random().Next(1, 300);
        var node = new VisualSystemNode(new Point(x, y));
        // DiagramManager.AddNode(node);
        DiagramManager.Nodes.Add(node);
        node.Node.Id = node.Id;
        node.OnClick = OnNodeClick;
        node.Node.ProcessType = ProcessModelsProvider.CreateProcess(VisualizerState.CurrentWorkFlow.ProcessType.Id, "").GetType();
        node.Node.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
        Model.Nodes.Add(node.Node);
    }

    void NewWorkFlowNodeClicked()
    {
        Helper.DebugConsole("New Workflow Node");
        double x = new Random().Next(1, 300), y = new Random().Next(1, 300);
        var node = new VisualProcessWorkFlowNode(new Point(x, y));
        // DiagramManager.AddNode(node);
        DiagramManager.Nodes.Add(node);
        node.Node.Id = node.Id;
        node.OnClick = OnNodeClick;
        node.Node.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
        node.Node.ClosingNodes.CollectionChanged += (a, b) => OnNodePropertyChanged(a, new PropertyChangedEventArgs("ClosingNodes"));
        Model.Nodes.Add(node.Node);
    }

    void NewConditionNodeClicked()
    {
        Helper.DebugConsole("New Condition Node");
        double x = new Random().Next(1, 300), y = new Random().Next(1, 300);
        var node = new VisualConditionNode(new Point(x, y));
        // DiagramManager.AddNode(node);
        DiagramManager.Nodes.Add(node);
        node.Node.Id = node.Id;
        node.OnClick = OnNodeClick;
        node.Node.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
        Model.Nodes.Add(node.Node);
    }

    void NewPrintingNodeClicked()
    {
        Helper.DebugConsole("New Printing Node");
        double x = new Random().Next(1, 300), y = new Random().Next(1, 300);
        var node = new VisualPrintingNode(new Point(x, y));
        // DiagramManager.AddNode(node);
        DiagramManager.Nodes.Add(node);
        node.Node.Id = node.Id;
        node.OnClick = OnNodeClick;
        node.Node.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
        Model.Nodes.Add(node.Node);
    }
    void NewTransferNodeClicked()
    {
        Helper.DebugConsole("New Transfer Node");
        double x = new Random().Next(1, 300), y = new Random().Next(1, 300);
        var node = new VisualTransferNode(new Point(x, y));
        // DiagramManager.AddNode(node);
        DiagramManager.Nodes.Add(node);
        node.Node.Id = node.Id;
        node.OnClick = OnNodeClick;
        node.Node.PropertyChanged += (a, b) => OnNodePropertyChanged(a, b);
        Model.Nodes.Add(node.Node);
    }

    string _nodeRef;
    async Task UpdateProcessWorkFlowNode()
    {

    }
    async Task UpdateSystemNode()
    {
        var _dNode = (VisualSystemNode)DiagramManager.Nodes.FirstOrDefault(a => a.Id == _nodeRef);
        SlideFormRef.Ref.Hide();
    }

    async Task UpdateTimerNode()
    {
        var _dNode = (VisualTimeNode)DiagramManager.Nodes.FirstOrDefault(a => a.Id == _nodeRef);
        SlideFormRef.Ref.Hide();
    }

    async Task UpdateProcessStateNode()
    {
        var _dNode = (VisualProcessStateNode)DiagramManager.Nodes.FirstOrDefault(a => a.Id == _nodeRef);
        SlideFormRef.Ref.Hide();
    }

    async Task CancelUpdatingNode()
    {
        SlideFormRef.Ref.Hide();
    }

    void OnNodeClick(string id)
    {
        var _node = Model.Nodes.FirstOrDefault(a => a.Id == id);
        SlideFormRef.Ref.SetWidthPercentage(50);
        _nodeRef = id;
        if (_node is SystemNode)
        {
            SystemNodeDialog = builder =>
            {
                builder.OpenComponent<SystemNodeDialog>(0);
                builder.AddAttribute(0, "SystemNode", (SystemNode)_node);
                builder.AddAttribute(0, "OnOkClick", OnSystemNodeOkClick);
                builder.AddAttribute(0, "OnNoClick", OnNoClick);
                builder.CloseComponent();
            };
            SlideFormRef.Ref.AttachElementByRenderFragment(SystemNodeDialog);
            SlideFormRef.Ref.Show();
        }
        else if (_node is TransferNode)
        {
            TransferNodeDialog = builder =>
            {
                builder.OpenComponent<TransferNodeDialog>(0);
                builder.AddAttribute(0, "TransferNode", (TransferNode)_node);
                builder.AddAttribute(0, "ProcessTypeId",Model.ProcessType.Id);
                builder.AddAttribute(0, "OnOkClick", OnTransferNodeOkClick);
                builder.AddAttribute(0, "OnNoClick", OnNoClick);
                builder.CloseComponent();
            };
            SlideFormRef.Ref.AttachElementByRenderFragment(TransferNodeDialog);
            SlideFormRef.Ref.Show();
        }
        else if (_node is PrintingNode)
        {
            PrintingNodeDialog = builder =>
            {
                builder.OpenComponent<PrintingNodeDialog>(0);
                builder.AddAttribute(0, "PrintingNode", (PrintingNode)_node);
                builder.AddAttribute(0, "OnOkClick", OnPrintingNodeOkClick);
                builder.AddAttribute(0, "OnNoClick", OnNoClick);
                builder.CloseComponent();
            };
            SlideFormRef.Ref.AttachElementByRenderFragment(PrintingNodeDialog);
            SlideFormRef.Ref.Show();
        }
        else if (_node is TimeNode)
        {
            TimerNodeDialog = builder =>
            { // one time intilizing and add Set(node) method for NodeDialog.razor and only use Set(node) rather than re-initiate
                builder.OpenComponent<TimerNodeDialog>(0);
                builder.AddAttribute(0, "TimeNode", (TimeNode)_node);
                builder.AddAttribute(0, "OnOkClick", OnTimerNodeOkClick);
                builder.AddAttribute(0, "OnNoClick", OnNoClick);
                builder.AddAttribute(0, "Paths", new Dictionary<string, string>(new[] {
                    new KeyValuePair<string,string>("Path1","Prop1")
                ,new KeyValuePair<string,string>("Path2","Prop2")}));
                builder.CloseComponent();
            };
            SlideFormRef.Ref.AttachElementByRenderFragment(TimerNodeDialog);
            SlideFormRef.Ref.Show();
        }
        else if (_node is ConditionNode)
        {
            var _condNode = (ConditionNode)_node;
            ConditionNodeDialog = builder =>
            { // one time intilizing and add Set(node) method for NodeDialog.razor and only use Set(node) rather than re-initiate
                builder.OpenComponent<ConditionNodeDialog>(0);
                builder.AddAttribute(0, "ConditionNode", _condNode);
                builder.AddAttribute(0, "ProcessTypeId", _condNode.ChildProcessTypeRef ?? Model.ProcessType.Id);
                builder.AddAttribute(0, "OnNoClick", OnNoClick);
                builder.CloseComponent();
            };
            SlideFormRef.Ref.AttachElementByRenderFragment(ConditionNodeDialog);
            SlideFormRef.Ref.Show();
        }
        else if (_node is ProcessWorkFlowNode)
        {
            ProcessWorkFlowNodeDialog = builder =>
            {
                builder.OpenComponent<ProcessWorkFlowNodeDialog>(0);
                builder.AddAttribute(0, "ProcessWorkFlowNode", (ProcessWorkFlowNode)_node);
                builder.AddAttribute(0, "OnOkClick", OnProcessStateNodeOkClick);
                builder.AddAttribute(0, "OnNoClick", OnNoClick);
                builder.AddAttribute(0, "ProcessTypeId", Model.ProcessType.Id);
                builder.CloseComponent();
            };
            SlideFormRef.Ref.AttachElementByRenderFragment(ProcessWorkFlowNodeDialog);
            SlideFormRef.Ref.Show();
        }
        else if (_node is ProcessStateNode)
        {
            ProcessStateNodeDialog = builder =>
            {
                builder.OpenComponent<ProcessStateDialog>(0);
                builder.AddAttribute(0, "ProcessStateNode", (ProcessStateNode)_node);
                builder.AddAttribute(0, "OnOkClick", OnProcessStateNodeOkClick);
                builder.AddAttribute(0, "OnNoClick", OnNoClick);
                builder.AddAttribute(0, "ProcessTypeId", Model.ProcessType.Id);
                builder.CloseComponent();
            };
            SlideFormRef.Ref.AttachElementByRenderFragment(ProcessStateNodeDialog);
            SlideFormRef.Ref.Show();
        }
    }

    void InfoClicked()
    { }
    async void SavedClicked()
    {
        if (string.IsNullOrWhiteSpace(Model.LocalizedName))
        {
            JSRuntime.ShowToast(Localizer["Current WorkFlow has no name"], StatusType.error);
            return;
        }

        if (DiagramManager.Nodes.All(a => !(a is VisualEntryNode)))
        {
            JSRuntime.ShowToast(Localizer["At least one entry node with link to state node"], StatusType.error);
            return;
        }
        if (DiagramManager.Nodes.Where(a => a is VisualEntryNode).Any(a => a.AllLinks.Count() == 0))
        {
            JSRuntime.ShowToast(Localizer["Entry nodes must be with links to state node"], StatusType.error);
            return;
        }

        Model.Nodes.ForEach(n => n.Point = DiagramManager.Nodes.Where(a => a.Id == n.Id).Select(a => new NodePoint() { X = a.Position.X, Y = a.Position.Y }).First());
        Model.ProcessTypeRef = Model.ProcessType.Id;
        Model.Nodes.ForEach(n => n.OnUpdating());
        await CreateOrUpdateAsync();
        ClearBoard();
        VisualizerState.HasOpenedWorkFlow = false;
        VisualizerState.HasSelectedDeletableComponent = false;
        VisualizerState.HasUnsavedChanges = false;
        VisualizerState.SelectedComponent = null;
        StateHasChanged();
    }

    private void ClearBoard()
    {
        var _enumerator = DiagramManager.Nodes.GetEnumerator();
        var _nodes = new List<NodeModel>();
        while (_enumerator.MoveNext())
        {
            _nodes.Add(_enumerator.Current);
        }
        for (int i = _nodes.Count - 1; i >= 0; i--)
        {
            // DiagramManager.RemoveNode(_nodes[i], false);
            DiagramManager.Nodes.Remove(_nodes[i]);
        }
        DiagramManager.Refresh();
    }

    void NewClicked()
    {
        Model = new ProcessWorkFlow();
        SlideFormRef.Ref.AttachElementByRenderFragment(CreateNewProcessFlowDialog);
        SlideFormRef.Ref.Show();
    }
    void OpenClicked()
    {
        SlideFormRef.Ref.AttachElementByRenderFragment(OpenProcessFlowDialog);
        SlideFormRef.Ref.Show();
    }

    public void Dispose()
    {
        if (DiagramManager != null)
        {
            // DiagramManager.LinkAdded -= OnLinkAdded;
            // DiagramManager.LinkAttached -= OnLinkAttached;
            // DiagramManager.LinkRemoved -= OnLinkRemoved;
            // DiagramManager.NodeAdded -= OnNodeAdded;
            // DiagramManager.NodeRemoved -= OnNodeRemoved;

            DiagramManager.Links.Added -= OnLinkAdded;
            DiagramManager.Links.Removed -= OnLinkRemoved;
            DiagramManager.Nodes.Added -= OnNodeAdded;
            DiagramManager.Nodes.Removed -= OnNodeRemoved;
        }
    }
}

`

juliolitwin commented 1 year ago

@zHaytam

Is the project currently paused? There have been no updates for a while and you said that version 3.0 would come out of beta in December.

Cheers!

zHaytam commented 1 year ago

@zHaytam

Is the project currently paused? There have been no updates for a while and you said that version 3.0 would come out of beta in December.

Cheers!

Health issues again + a job change, I didn't have much time these weeks until this week. I'm still writing the documentation website for 3.0