jerosoler / Drawflow

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

Canvas lock and drag mechanism #620

Closed gpack closed 1 year ago

gpack commented 1 year ago

Hi, I would like to be able to have the canvas locked so I can drag around anywhere with the mouse (and not move any nodes), but as soon as you want to move a node, you can just click/select it and then it'll unlock the canvas so you can move the node around. But, as soon as node is unselected, the canvas is automatically locked again by default.

Is this possible?

thanks.

jerosoler commented 1 year ago

There are several ways in the i editor if you can't use events to block.

See the next issue:

gpack commented 1 year ago

I've tried locking the editor (editor.editor_mode = 'view'), that works great, but then I can't detect when a node is selected to unlock the editor so you can drag the node because editor.on('nodeSelected') doesn't work or return value when the editor is locked in "view" mode.

Do you have any suggestions on how I can use "view" mode for editor, but be allowed to also select a node or drag a node if I click on it?

thanks.

jerosoler commented 1 year ago

Try with events:

gpack commented 1 year ago

Thanks for response. I had a look at #321.

Using those events, how would I implement it? So is the editor mode set to "view" by default, and then when do I call "changeMode()" ?

Thanks.

jerosoler commented 1 year ago

Hello

Example with event change on click target.

    const editor = new Drawflow(id);
    editor.editor_mode = 'view';
    editor.on("click", (e) => {

        if(e.target.closest(".drawflow_content_node") != null || e.target.classList.contains("drawflow-node")) { 
            // Click on node content or parent
            // Exclude inputs and ouptus
            editor.editor_mode = 'edit';
            console.log('edit');
        } else {
            // Click ouput node
            editor.editor_mode = 'view';
            console.log('view');
        }
    });
    editor.start();
gpack commented 1 year ago

Thanks for the great example Jero.

It's close to the functionality I was after, but with your example I still cannot drag the node in "view" mode. I only want to change to "edit" mode if the user clicks/selects the node for editing (not dragging). And once the node is unselected, then the editor goes back to "view" mode.

So I changed the code a bit and it works and unlocks the editor, but I need it to auto-select (highlight) the node the user clicked on:

editor.on("mouseUp", (e) => {
        if(x_click === last_x && y_click === last_y && (e.target.closest(".drawflow_content_node") != null || e.target.classList.contains("drawflow-node"))) {          
            editor.editor_mode = 'edit';
            console.log('edit');
        } else {
            editor.editor_mode = 'view';
            console.log('view');
        }

    });

Can you suggest how to auto-select the node once the editor is unlocked, and when the node is unselected (user clicks away), the editor goes back to "view" mode again.

thanks.

jerosoler commented 1 year ago

I don't quite understand the behavior you are looking for.

This is how the library selects a node:

         this.node_selected = this.ele_selected;
         this.node_selected.classList.add("selected");
gpack commented 1 year ago

Sorry if I didn't explain it clearly. let me try again....

Basically, I don't want to force the user to do extra clicks on "unlock/lock" buttons. So I'm trying to create a more intuitive/smarter way of combining the 2 editor modes.

I would like the editor to be in the locked "view" mode at all times. But as soon as user wants to edit a node, they do a simple "click" on a node and it selects/highlights the node (unlocks editor). Or if they want to drag a connection line then this works too.

I think it's not hard to achieve, we just need to detect 2 states (Drag and Click):

  1. If there are no nodes selected and the user is dragging, then just pan the canvas around (just like "view" mode).
  2. As soon as user "clicks" on a node, highlight the node and the user can now drag the node around (just like "edit" mode).
  3. Detect if user is dragging a connection input/output and allow the user to connect to another node (just like "edit" mode).

At all other times (when there are no nodes selected) when user is dragging, the editor is always in "view" mode.

Hope that makes it a bit easier to understand.

jerosoler commented 1 year ago

Hello,

As I see that the predominant method is "editor".

I think it is better that he is active.

Which we force deselection of the node.

Hope this can help you:

    var id = document.getElementById("drawflow");

    const editor = new Drawflow(id);

    let x_click = 0;
    let y_click = 0;
    let node_selected = null;
    let restric_move = true;
    editor.on("clickEnd", (e) => {
        x_click = editor.pos_x;
        y_click = editor.pos_y;
        if(editor.node_selected !=  null && restric_move) {
          node_selected = editor.node_selected;
          editor.node_selected.classList.remove("selected");
          editor.node_selected = null;
          editor.ele_selected = editor.container.firstChild;
          editor.drag = false;
          editor.editor_selected = true;
        } else {
          node_selected = null;
        }
    })

    editor.on("mouseMove", (e) => {
      x_click = e.x;
      y_click = e.y;
    })

    editor.on("mouseUp", (e) => {
        if(x_click === editor.pos_x && y_click === editor.pos_y && node_selected !=  null) {
          editor.node_selected = node_selected;
          editor.node_selected.classList.add("selected");
          restric_move  = false;
        } else {
          restric_move  = true;
        }
    });
    editor.start();
gpack commented 1 year ago

Jero, that is amazing. You are a legend! Yes, keeping the editor in "edit" mode and restricting the move is better way to think about the solution. Brilliant work!

Thanks so much for writing that code out, it works perfectly and exactly what I was trying to achieve.