reactjsalgorithmreact-flow

Flatten flow chart diagram to a text format (reactflow)


We are looking for a way to map a reactflow diagram to a flat plaintext style. The tricky part is to capture the decision functionality, especially the part where the paths connect. The whole system is similar to dialogue game engine where one decision can lead to another but in the end they can all lead to the same ending.

A quick illustration:

       B
a -> {   } -> D
       C

Should produce such text:

A
// Decision: node_3
  B
// Decision end: node_3
// Decision: node_4
  C
// Decision end: node_4
D

I've created a stackblitz example that shows the problem, it contains example input data but of course the chart can be expanded. I believe the example data to be complete, i.e. if the solution can handle this data it can handle any data.

https://stackblitz.com/edit/vitejs-vite-xlcdmc

The function that does the transformation can be found here:
components/Toolbar.tsx::handleExportComments

The desired output should look like this:

intro
intro 2

// Decision: node_3
  fruits
  // Decision: node_4
    apple
  // Decision end: node_4
  // Decision: node_5
    banana
  // Decision end: node_5
  fruit shake
// Decision end: node_3

// Decision: node_6
  vegetables
  // Decision: node_7
    onion
  // Decision end: node_7
  // Decision: node_8
    lettuce
    lettuce with jogurt
  // Decision end: node_8
  salad
// Decision end: node_6

middle1
middle2

// Decision: node_14
  top
// Decision end: node_14
// Decision: node_15
  bottom
// Decision end: node_15

pre-ending
end

My current solution produces duplicates and seems to be plain out wrong when when we have multiple divergences.

It seems all the data to produce this is available, i.e. we have all the Edges (connections) and Nodes, could this be used somehow?

Functionality notes:

I have a suspicion this is a known math / CS problem but I hope there is a solution to this :)

I'm open to use a different tool but I need to have the ability to expand this with more fields.

Lastly, if you don't want / can't solve the issue even pointing me in the right direction or naming the problem so that I can google it better will be helpful.


Solution

  • I had to abruplty end working on that project and unfortunately I did not manage to implement this functionality in it's entirety. For what it's worth It seems I did manage to crack the problem in the end:

    const nodes = {
      intro: {
        parents: [],
        children: ["fruit", "vegetables"],
      },
      fruit: {
        parents: ["intro"],
        children: ["apple", "orange"],
      },
      apple: {
        parents: ["fruit"],
        children: ["fruit_mix"],
      },
      orange: {
        parents: ["fruit"],
        children: ["fruit_mix"],
      },
      fruit_mix: {
        parents: ["apple", "orange"],
        children: ["middle1"],
      },
      vegetables: {
        parents: ["intro"],
        children: ["carrot", "lettuce"],
      },
      carrot: {
        parents: ["vegetables"],
        children: ["middle1"],
      },
      lettuce: {
        parents: ["vegetables"],
        children: ["lettucejogurt"],
      },
      lettucejogurt: {
        parents: ["lettuce"],
        children: ["middle1"],
      },
      middle1: {
        parents: ["lettuce", "carrot"],
        children: ["middle2"],
      },
      middle2: {
        parents: ["middle1"],
        children: ["up", "down"],
      },
      up: {
        parents: ["middle2"],
        children: ["end"],
      },
      down: {
        parents: ["middle2"],
        children: ["end"],
      },
      end: {
        parents: ["up", "down"],
        children: [],
      },
    };
    
    function exportComments() {
      const output = [];
      const visited = new Set();
    
      function visit(nodeId) {
        if (visited.has(nodeId)) {
          return;
        }
    
        visited.add(nodeId);
    
        // Add the current node to the output before visiting its children
        output.push(nodeId);
    
        const node = nodes[nodeId];
    
        // Visit all children
        node.children.forEach((childId) => {
          // Only visit the child if all its parents have been visited
          if (nodes[childId].parents.every((parentId) => visited.has(parentId))) {
            visit(childId);
          }
        });
      }
    
      // Start traversal from the "intro" node
      visit("intro");
    
      console.log("output", output);
    }
    
    exportComments();
    

    The code got really simplified and the main logic change is to check if a node has been visited or not.