jerosoler / Drawflow

Simple flow library 🖥️🖱️
https://jerosoler.github.io/Drawflow/
MIT License
4.43k stars 713 forks source link

Is it possible to connect nodes that are not on the same level in the DOM? #363

Open francoisschwarzentruber opened 2 years ago

francoisschwarzentruber commented 2 years ago

First thank you for this very nice project! Let me explain my need on an example. Suppose the DOM tree is

          body
                div 
                          div of id A
                div of id B

I want to connect A and B. Is it possible?

I tried to connect it but I get the error

Uncaught (in promise) TypeError: this.drawflow.drawflow[t] is undefined
    getNodeFromId https://cdn.jsdelivr.net/gh/jerosoler/Drawflow/dist/drawflow.min.js:1
    addConnection https://cdn.jsdelivr.net/gh/jerosoler/Drawflow/dist/drawflow.min.js:1
jerosoler commented 2 years ago

How did you add the node?

When a node is added with the addnode function they are always at the same level.

What if a node can have several levels. Example:

editor.addNode('node1', 0, 1, 150, 300, 'node1', {}, '<div>Hi!</div>');

editor.addNode('node2', 0, 1, 300, 300, 'node2', {}, '<div><div><div><div><div>Hii!</div></div></div></div></div>');
francoisschwarzentruber commented 2 years ago

Thank you for your answer. OK I see. I added the node via the DOM (appendChild), that is maybe one source of the problem. Nodes are created via DOM manipulations.

When a node has a deeper level, its container may have other children too so unfortunately editor.addNode('node2', 0, 1, 300, 300, 'node2', {}, '<div><div><div><div><div>Hii!</div></div></div></div></div>'); will not work, because it will consider node2 has the whole container instead of just the inner element.

Anyway, thank you for your nice library that works when it is used "in the standard way".

jerosoler commented 2 years ago

What is the reason for having several nodes at different levels? Maybe there is some other solution.

I think the library could be adapted, even though you would have to touch a lot of code. You can fork and try.

francoisschwarzentruber commented 2 years ago

The reason is that the organization of nodes at different levels enable to benefit from CSS layouting. In the same time, I need to connect two nodes. See picture, where the left part is organized via CSS, and I want to connect some inner node of it to the node of the right.

image

jerosoler commented 2 years ago

Are they multiple nodes? If there are multiple nodes, have you thought about adding a new editor to a drawflow node? Maybe that could be your solution.

If it's not multiple nodes and it's just style, does it mean something like this?

image

francoisschwarzentruber commented 2 years ago

Thank you! Maybe your solution could work indeed! In your example, the connections are:

Is it possible for the edges (connections) to go over the block "Ask questions"?

jerosoler commented 2 years ago

Yes it's possible.

image

In this case I use an output displacement technique. I use the class link followed by the output to which it is directed to move the element.

    editor.on('nodeCreated', (id) => {
        const links =  document.querySelectorAll(`#node-${id} .drawflow_content_node .link`);
        links.forEach((item) => {
            const target = document.querySelector(`#node-${id} .outputs .${item.classList[1]}`);
            if(target != null) {
                const pos = item.getBoundingClientRect();
                const targetPos = target.getBoundingClientRect();
                target.style.top = `${pos.y - targetPos.y}px`;
                target.style.left = `${pos.x - targetPos.x}px`;
            }
        })
    })

    editor.start();

    editor.addNode('question', 1, 3, 300, 200, 'question', {}, `
        <div class="title">Ask question</div>
        <div class="panel">Nice to meet yout (name)...</div>
        <div class="panel">Custom API</div>
        <div class="multiple">
            <div class="panel">Yes<div class="link output_1"></div></div>
            <div class="panel">No<div class="link output_2"></div></div>
            <div class="panel">No Match<div class="link output_3"></div></div>
        </div>
    ` );

In this case it is only done for the outputs and new nodes, the same would have to be done with the import event.

I put the complete code of the example.


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jerosoler/Drawflow/dist/drawflow.min.css"/>
  <script src="https://cdn.jsdelivr.net/gh/jerosoler/Drawflow/dist/drawflow.min.js"></script>
</head>
<body>
<div id="drawflow"></div>
  <style>
    #drawflow { 
      position: relative;
      text-align:initial;
      width: 100%;
      height: 800px;
      border: 1px solid red;
      font-size: 14px;
    }
    .multiple {
        border-radius: 4px;
    }
    .multiple .panel {
        margin: 0px;
        border-radius: 0px;
    }
    .panel {
        display: block;
        position: relative;
        background: white;
        padding: 10px;
        margin: 10px 0px;
        border-radius: 4px;
        border: 1px solid #eaeef3;
    }

    .link {
        position: absolute;
        right: 5px;
        top: 15px;

        display: block;
        width: 10px;
        height: 10px; 
    }

    .drawflow .drawflow-node .output, .drawflow .drawflow-node .input  {
        width: 8px;
        height: 8px;
        border: 2px solid #75889a;
    }

    .drawflow .inputs {
        position: absolute;
        top: 10px;
        opacity: 1;
    }
    .drawflow .connection .main-path {
        stroke-width: 2px;
    }

    .drawflow .drawflow-node {
        border: 2px solid white;
        width: 260px;

    }
    .drawflow .drawflow-node .title {
        font-weight: bold;
    }

    .drawflow-node.question {
        outline: 2px solid #eaeef3;
        background: #eaeef3;
    }
    .drawflow-node.question .title {
        color: #75889a;
    }

    .drawflow-node.yes {
        outline: 2px solid #e9f0e9;
        background: #e9f0e9;
    }
    .drawflow-node.yes .title {
        color: #758375;
    }

    .drawflow-node.newblock .inputs {
        top: 50px;
    }
    .drawflow-node.newblock {
        outline: 2px solid #eaeef3;
        background: #eaeef3;
    }
    .drawflow-node.newblock .title {
        color: #75889a;
    }

    .drawflow-node.no .inputs {
        top: 50px;
    }
    .drawflow-node.no {
        outline: 2px solid #ffdee6;
        background: #ffdee6;
    }
    .drawflow-node.no .title {
        color: #ad516a;
    }
    .drawflow .drawflow-node {
        z-index: initial;
    }

</style>
<script>
    var id = document.getElementById("drawflow");
    const editor = new Drawflow(id);

    editor.on('nodeCreated', (id) => {
        const links =  document.querySelectorAll(`#node-${id} .drawflow_content_node .link`);
        links.forEach((item) => {
            const target = document.querySelector(`#node-${id} .outputs .${item.classList[1]}`);
            if(target != null) {
                const pos = item.getBoundingClientRect();
                const targetPos = target.getBoundingClientRect();
                target.style.top = `${pos.y - targetPos.y}px`;
                target.style.left = `${pos.x - targetPos.x}px`;
            }
        })
    })

    editor.start();

    editor.addNode('question', 1, 3, 300, 200, 'question', {}, `
        <div class="title">Ask question</div>
        <div class="panel">Nice to meet yout (name)...</div>
        <div class="panel">Custom API</div>
        <div class="multiple">
            <div class="panel">Yes<div class="link output_1"></div></div>
            <div class="panel">No<div class="link output_2"></div></div>
            <div class="panel">No Match<div class="link output_3"></div></div>
        </div>
    ` );

    editor.addNode('yes', 1, 0, 850, 200, 'yes', {}, `
        <div class="title">Yes Randomized Facts</div>
        <div class="panel">Did you know.... Vieflow power over 6000 Alexa Skills and Google Actions</div>
    ` );

    editor.addNode('newblock', 1, 0, 850, 370, 'newblock', {}, `
        <div class="title">New Block 5</div>
        <div class="panel">Connect a flow to this step</div>
    ` );

    editor.addNode('no', 1, 1, 850, 500, 'no', {}, `
        <div class="title">No - Goodbye</div>
        <div class="panel">No problem. Bye for now!<div class="link output_1"></div></div>
    ` );

    editor.addConnection(1, 2, 'output_1', 'input_1');

    editor.addConnection(1, 3, 'output_2', 'input_1');
    //editor.addConnection(1, 4, 'output_3', 'input_1');

</script>
</body>
</html>
francoisschwarzentruber commented 2 years ago

Thank you! I will study that! Then my need would be:

Also, your library deserves a nice tutorial! :) Have a nice day!

RaviGuggilam commented 2 years ago

Hi @jerosoler , using above example, creating node using json structure but those connecting lines are not placing properly. \ like nodeCreated event for addNode is there any event available while creating nodes using json PFB angular example in stackbliz angular-stackbliz-example