Drawflow icon indicating copy to clipboard operation
Drawflow copied to clipboard

How to group nodes

Open Flyfot-Finesse opened this issue 2 years ago • 36 comments

Hello @jerosoler ,

Firstly great library!!

I am using this library on a project but we have a customized requirement to group Nodes in a wrapper and allow to link nodes from multiple groups to connect to each other. Something like on demo of another library link below: https://demo.jsplumbtoolkit.com/groups/

Can you please guide how can we achieve this with Drawflow?

Thanks & Regards, Ankit

Flyfot-Finesse avatar May 17 '22 03:05 Flyfot-Finesse

Hi @apmkstudio

It's not possible.

The only thing I can think of is: An editor inside the node. This would be possible.

Duplicate:

  • #355
  • #93

jerosoler avatar May 17 '22 06:05 jerosoler

Hi @jerosoler ,

Thanks for your quick reply.

The editor inside the node doesn't support the goal we are trying to achieve. Any plan of adding this in the near future as I see this has been requested by many other users?

Or if you can guide how we can customize the library to achieve this?

Appreciate your help!

Regards, Ankit

Flyfot-Finesse avatar May 19 '22 05:05 Flyfot-Finesse

I have been doing a test and it seems to work, without modifying the library. Listening to the events

https://user-images.githubusercontent.com/30957047/169275592-672745cf-3a48-4674-ac03-38a3451fc56f.mp4

Complet code 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>
<div id="drawflow"></div>
  <style>
    #drawflow { 
      position: relative;
      text-align:initial;
      width: 100%;
      height: 800px;
      border: 1px solid red;
    }
    .drawflow svg {
        z-index: 1;
    }
    .drawflow .drawflow-node.GROUP {
        width: 500px;
        height: 500px;
        z-index: 0;
    }
    .drawflow .drawflow-node.GROUP.hover-drop {
        background: pink;
    }
</style>
<script>
    var id = document.getElementById("drawflow");
    const editor = new Drawflow(id);
    
    editor.start();    
    editor.addNode('aaa', 1, 1, 600, 200, 'aaa', {}, `aaa` );
    editor.addNode('bbb', 1, 1, 850, 200, 'bbb', {}, `bbb` );
    editor.addNode('ccc', 1, 1, 850, 370, 'ccc', {}, `ccc`);
    editor.addConnection(1, 2, 'output_1', 'input_1');
    editor.addConnection(2, 3, 'output_1', 'input_1');
    editor.addNode('GROUP', 0, 0, 1200, 100, 'GROUP', { elements: []},  `` );
    editor.addNode('GROUP', 0, 0, 0, 100, 'GROUP', { elements: []},  `` );


    let dragElementHover = null;
    let last_x = 0;
    let last_y = 0;
    editor.on("mouseMove", ({x,y}) => {
        if(editor.node_selected && editor.drag && editor.node_selected.classList[1] !== "GROUP") {
            const eles = document.elementsFromPoint(x,y);
            const ele = eles.filter( ele => ele.classList[1] === "GROUP");
            if(ele.length > 0) {
                dragElementHover = ele[0];
                dragElementHover.classList.add("hover-drop");
            } else {
                if(dragElementHover != null) {
                    dragElementHover.classList.remove("hover-drop");
                    dragElementHover = null;
                }
            }
        } else if(editor.node_selected && editor.drag && editor.node_selected.classList[1] == "GROUP") {
            const dragNode = editor.node_selected.id.slice(5);
            const dragNodeInfo = editor.getNodeFromId(dragNode);
            const elements = dragNodeInfo.data.elements;
            elements.forEach(eleN => {

                const node = document.getElementById(`node-${eleN}`);
                var xnew = (last_x - x) * editor.precanvas.clientWidth / (editor.precanvas.clientWidth * editor.zoom);
                var ynew = (last_y - y) * editor.precanvas.clientHeight / (editor.precanvas.clientHeight * editor.zoom);

                node.style.top = (node.offsetTop - ynew) + "px";
                node.style.left = (node.offsetLeft - xnew) + "px";

                editor.drawflow.drawflow[editor.module].data[eleN].pos_x = (node.offsetLeft - xnew);
                editor.drawflow.drawflow[editor.module].data[eleN].pos_y = (node.offsetTop - ynew);
                editor.updateConnectionNodes(`node-${eleN}`);

            });
        }
        last_x = x;
        last_y = y;
    })

    editor.on("nodeMoved", (id) => {
        const dragNode = id;
        if(dragElementHover !== null) {
            const dropNode = dragElementHover.id.slice(5);
            if(dragNode !== dropNode) {
                removeOfGroupNode(dragNode);
                dragElementHover.classList.remove("hover-drop");
                const dropNodeInfo = editor.getNodeFromId(dropNode);
                const dropNodeInfoData = dropNodeInfo.data;
                if(dropNodeInfoData.elements.indexOf(dragNode) === -1) {
                    dropNodeInfoData.elements.push(dragNode);
                    editor.updateNodeDataFromId(dropNode, dropNodeInfoData);
                }
            }
            dragElementHover = null;
        } else {
            removeOfGroupNode(dragNode);
        }
    })

    editor.on("nodeRemoved", (id) => {
        removeOfGroupNode(id);
    });

    function removeOfGroupNode(id) {
        Object.keys(editor.drawflow.drawflow[editor.module].data).forEach(ele => {
            if(editor.drawflow.drawflow[editor.module].data[ele].class === "GROUP") {
                const findIndex = editor.drawflow.drawflow[editor.module].data[ele].data.elements.indexOf(id);
                if(findIndex !== -1) {
                    editor.drawflow.drawflow[editor.module].data[ele].data.elements.splice(findIndex, 1);
                }
            }
       })
    }
</script>
</body>
</html>

jerosoler avatar May 19 '22 10:05 jerosoler

Hi @jerosoler ,

Thanks a lot for the sample code this works great for now.

Appreciate your support !!

Regards, Ankit

Flyfot-Finesse avatar May 20 '22 08:05 Flyfot-Finesse

Hello @jerosoler,

Can you help me with one more task please, I want the ability that the group node is height adjustable how can I do that can you please guide me? ie when I click on the group node I can have the option to increase the height of the node.

Thanks & Regards, Ankit

Flyfot-Finesse avatar May 22 '22 09:05 Flyfot-Finesse

Use resize both, to modify the node.

Change css to:

 .drawflow .drawflow-node.GROUP {
        width: 500px;
        height: 300px;
        z-index: 0;
        resize: both;
        overflow: auto;
    }

The problem is that when an element is dragable it doesn't work and you have to do this trick.

And add javascript:

editor.on("clickEnd", (e) => {
        if(editor.node_selected) {
            const diffClick = 15;
            const height = editor.node_selected.getBoundingClientRect().height/editor.zoom - diffClick;
            const width = editor.node_selected.getBoundingClientRect().width/editor.zoom - diffClick;
            const xClick = e.offsetX;
            const yClick = e.offsetY;
            if(yClick >= height && xClick >= width) {
                editor.drag = false;
            } 
        }
    });

In this example, these changes will not be saved in the export, but you can save them in the node. For when I get the import back. Same as done in the element group.

jerosoler avatar May 23 '22 16:05 jerosoler

Thanks @jerosoler ,

Works fine, can you please give a sample of how to save height / width in the node? and when import again we can how the height of the node will be same as it was set before ?

Appreciate it thanks!!

Regards, Ankit

Flyfot-Finesse avatar May 25 '22 16:05 Flyfot-Finesse

Also is there any hook/ callback before the nodes load basically I want to do is group elements and show them as in kanban board so how to set X / Y axis dynamically on flow load?

Flyfot-Finesse avatar May 25 '22 16:05 Flyfot-Finesse

Use event "import" to the detect load complete and move node.

View move node by javascript:

  • https://github.com/jerosoler/Drawflow/issues/112#issuecomment-776870546

jerosoler avatar May 25 '22 16:05 jerosoler

Hi @jerosoler ,

I tried event import it doesn't work for me editor.on('import', function (id) { console.log("import done"); })

Basically I want to save the width and height of the group column and on load (import) the height should be same as before

Thanks, Ankit

Flyfot-Finesse avatar May 26 '22 06:05 Flyfot-Finesse

Did you add it before the import or after? You have to go before.

jerosoler avatar May 26 '22 06:05 jerosoler

I tried after - let me try before and get back to you

Flyfot-Finesse avatar May 26 '22 06:05 Flyfot-Finesse

I tried before it triggers but cannot modify the content of node editor.on('import', function (data) { editor.drawflow.drawflow.Home.data[14].class = 'test'; console.log(editor.drawflow.drawflow.Home.data[14]) })

Flyfot-Finesse avatar May 26 '22 07:05 Flyfot-Finesse

If you modify the class in the node, it is not updated in the view automatically.

Or you modify it before importing. Or you modify the node as well.

jerosoler avatar May 26 '22 07:05 jerosoler

Do you mean directly modifying the variable before import?

Flyfot-Finesse avatar May 26 '22 08:05 Flyfot-Finesse

If you modify the variable before importing.

When importing, it loads the node correctly.

If not you will have to modify the node class by javascript. Since when updating the data is not reflected automatically.

jerosoler avatar May 26 '22 08:05 jerosoler

Hello @jerosoler ,

Thanks for the reply!

I tried before import also, but I am not able to save the custom width/height of the node can you please guide me on it how to set the width/height of the node on import and save it when drag-drop, thanks!

Basically what we would like is once the user stretch the width/height of the node (we save into the database the json) and again when the user comes to the page the node width/height is the same as the user set it previously.

Regards, Ankit

Flyfot-Finesse avatar May 31 '22 06:05 Flyfot-Finesse

Hello @jerosoler ,

Can you please guide me on the above as it's critical for the project that the width and height are saved properly.

Appreciate your support.

Thanks, Ankit

Flyfot-Finesse avatar Jun 02 '22 05:06 Flyfot-Finesse

So you can get the height and width of element resized.

    let resizeElement = false;
    editor.on("clickEnd", (e) => {
        if(editor.node_selected) {
            const diffClick = 20;
            const height = editor.node_selected.getBoundingClientRect().height/editor.zoom - diffClick;
            const width = editor.node_selected.getBoundingClientRect().width/editor.zoom - diffClick;
            const xClick = e.offsetX;
            const yClick = e.offsetY;
            if(yClick >= height && xClick >= width) {
                resizeElement = true;
                editor.drag = false;
            }
        }
    });


    editor.on("mouseUp", (e) => { 
        if(resizeElement) {
            console.log(`Resize Node ${editor.node_selected.id} - Width: ${editor.node_selected.style.width} - Height: ${editor.node_selected.style.height}`)
            // editor.drawflow.drawflow[editor.module].data[editor.node_selected.id].width =  ...;
            resizeElement = false;
        }
    });

jerosoler avatar Jun 02 '22 06:06 jerosoler

Hello @jerosoler ,

Yes thanks for that but the issue is how I save it in the json so that this data can be used again to set height /width of the node on page reload ?

Thanks, Ankit

Flyfot-Finesse avatar Jun 02 '22 07:06 Flyfot-Finesse

Uncomment this line.

            // editor.drawflow.drawflow[editor.module].data[editor.node_selected.id].width =  ...;

Create the line for the height.

Use the on "import" event method. To set width and height.

jerosoler avatar Jun 02 '22 08:06 jerosoler

Hello @jerosoler ,

Sorry but I am not able to modify the css class on import event can you please give an example how we can achieve it easily, thanks!

Flyfot-Finesse avatar Jul 11 '22 17:07 Flyfot-Finesse

@apmkstudio view example:

  • https://github.com/jerosoler/Drawflow/issues/267

jerosoler avatar Jul 12 '22 06:07 jerosoler

Hello @jerosoler,

I have followed the example code but I am not able to modify the node for example change class or style or any other node json parameter. See screenshot below

In console the first parameter only returns string "import" image

Code screenshot image

Thanks, Ankit

Flyfot-Finesse avatar Jul 13 '22 17:07 Flyfot-Finesse

Event import only return one parameter.

you have to loop through the array and modify the class...

jerosoler avatar Jul 13 '22 17:07 jerosoler

Yes but that parameter is just a string so I cannot modify it.

Flyfot-Finesse avatar Jul 13 '22 17:07 Flyfot-Finesse

You can loop through the same object that you passed in when importing. Or editor.drawflow. Or editor.export();

jerosoler avatar Jul 13 '22 17:07 jerosoler

Yes, I tried that too but unfortunately, it modifies the JSON object but not DOM even after updating using editor.updateConnectionNodes(node- + index) I want the dom class to update so I can do some changes infact I want height to update dynamically based on whats saved in node.

Flyfot-Finesse avatar Jul 13 '22 17:07 Flyfot-Finesse

Just for your information, I am not using VUE JS I am using simple javascript on the page. Technically in a Laravel blade file if this helps...

Flyfot-Finesse avatar Jul 13 '22 17:07 Flyfot-Finesse

First of all does it save the height and width of the node? Where in?.

Second, editor.updateConnectionNodes updates connections, not nodes.

Understand what you are doing at each moment.

jerosoler avatar Jul 13 '22 17:07 jerosoler