Drawflow icon indicating copy to clipboard operation
Drawflow copied to clipboard

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

Open francoisschwarzentruber opened this issue 2 years ago • 9 comments

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

francoisschwarzentruber avatar Feb 24 '22 20:02 francoisschwarzentruber

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>');

jerosoler avatar Feb 25 '22 07:02 jerosoler

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.

  • Do you think it would be possible to adapt the library so that nodes are not at the same level?
  • Do you think it would be possible to modify DrawFlow in order to refer to elements directly rather than to their ids? editor.addConnection(element1, element2) instead of editor.addConnection(element1.id, element2.id)

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

francoisschwarzentruber avatar Feb 25 '22 08:02 francoisschwarzentruber

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.

jerosoler avatar Feb 25 '22 08:02 jerosoler

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

francoisschwarzentruber avatar Feb 25 '22 08:02 francoisschwarzentruber

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

jerosoler avatar Feb 25 '22 09:02 jerosoler

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

  • Yes ====> Yes Randomized Facts
  • No =====> New Block 5 Right?

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

francoisschwarzentruber avatar Feb 25 '22 09:02 francoisschwarzentruber

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>

jerosoler avatar Feb 25 '22 09:02 jerosoler

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

  • to disable all the editing functionalities (in my need, the graph just evolves by clicking on nodes)
  • to hide nodes (and the related links)
  • it would be easier also to nodes via DOM like editor.addNode(domElement) and to refer to DOM elements directly editor.addConnection(domElement1, domElement2), because here, it requires the user to handle ids manually (generation of fake ids etc.) But this depends on the usage. Adding as it is now is good too.

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

francoisschwarzentruber avatar Feb 25 '22 09:02 francoisschwarzentruber

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

RaviGuggilam avatar May 04 '22 13:05 RaviGuggilam