GOJS Tree View Scoll diagram when moving node from one parent to another

I am using gojs diagram to display nodes in Tree Layout. In my tree layout the functionality is implemented to change the parent of nodes using drag-drop events.

The problem with drag-drop events is that when there are large number of nodes, while moving the node diagram doesn’t get scrolled automatically.
To meet same requirement i found the following sample on your site -

With this sample and using approach of similar to this i am facing two issues -

  1. If i try to move node in upward direction and once the top most root element is reach then the tree view starts moving in down. Expectation is - it should be top aligned and moving should stop. In your sample diagram keeps scrolling to left or top. New node should not have negative position.

  2. As i move the node, its associated links starts moving also. With tree layout it doesn’t look nice. But when i press ctrl button and then start moving a new node gets created and it starts moving as expected without any link. How can i get that type of behaviour without using ctrl button in tree layout.

During dragging (DraggingTool), the diagram autoscrolls when the mouse is just inside the edge of the viewport, within the Diagram.autoScrollRegion: Diagram | GoJS API. This is the default behavior, as you can see in one sample that lets the user reparent subtrees: Org Chart Editor.

Yes. With the sample for organization chart, the behavior is valid - because as you move upward with topmost node the line height also increases and distance between these nodes also increases.

In my case, nodes are displays as a tree view ( like windows explorer) where my topmost root node should remain at the position where it is. When my diagram has large number of item and i want to change parent of some item, i need to scroll the diagram to choose different parent.
(As of now, i scale down the diagram so all items are visible and then i change the parent of any node. but with lots of item scale down diagram (zoom in) is also not a valid solution for me).

I started with the TreeView sample, GoJS Tree View.

I removed the setting of Diagram.allowMove, set DraggingTool.dragsTree and Diagram.maxSelectionCount, and added a Diagram.mouseDrop event handler to prevent dropping nodes onto the background:

        $(go.Diagram, "myDiagramDiv",
          {
            "draggingTool.dragsTree": true,
            maxSelectionCount: 1,
            mouseDrop: function(e) { e.diagram.currentTool.doCancel(); },
            allowCopy: false,
            allowDelete: false,
            . . .

Then I added GraphObject.mouseDragEnter, mouseDragLeave, and mouseDrop event handlers on the Node template, along with a Binding of Part.layerName:

        $(go.Node,
          new go.Binding("layerName", "isSelected", function(s) { return s ? "Foreground" : ""; }).ofObject(),
          {
            mouseDragEnter: function(e, node) { node.elt(1).background = "red"; },
            mouseDragLeave: function(e, node) { node.elt(1).background = null; },
            mouseDrop: function(e, node) {
              e.diagram.model.commit(function(m) {
                m.setParentKeyForNodeData(e.diagram.selection.first().data, node.key);
              }, "reparented");
            },
            // no Adornment: instead change panel background color by binding to Node.isSelected
            selectionAdorned: false,
            . . .

Presumably you have already added these event handlers to your app in order to perform the re-parenting.

Doesn’t this do more-or-less what you want? As I drag a node or subtree down, just above the bottom of the viewport, the diagram/document/tree appears to move upward because the auto-scrolling is scrolling the viewport downwards. Just hold the mouse there, and it continues scrolling. And the same going upwards, when the mouse is just below the top of the viewport.

Hi Walter

Thanks for the needed change. Now there is one issue only -
As you see in the image, if top most node is reached then also tree moves downwards, that should not be possible. How to prevent it ? (Also if user tries to move the item in down direction reaching the last node it should stop. Now the tree starts moving upward and leaves empty area - where obviously user is not allowed to drop)

Implement Diagram | GoJS API
Read GoJS Coordinate Systems-- Northwoods Software

Hello @walter,

As per ‘Position Computation’ doc, I have added changes in my code,

positionComputation: function (diagram, pt) {
                return new go.Point(Math.floor(pt.x), Math.floor(pt.y < 0 ? 0 : pt.y));
            },

As you can see in above code I made one change and now I am able to restrict user from scrolling upward while dragging node to top by not allowing point-Y value less than 0.

But for downward drag and scroll How can I define point-Y bounds?

You could also limit the Y value to make sure it is never greater than some number. I don’t know your app and what you want, so I cannot be specific.

that number varies diagram to diagram i.e. some tree diagram are very small and some of them are large in structure so I can’t specify specific number.

Please let me know you need more details.

In an “InitialLayoutCompleted” DiagramEvent listener you could note the Diagram.documentBounds.bottom value and have the positionComputation function make use of that Y limit when computing the new Diagram.position.

Remember that the documentBounds are the whole area covered by all nodes and links, and the Node.location .y value probably will never get to match the documentBounds.bottom value due to the height of the node itself.

not providing accurate value.

How so? What’s the value of Diagram.documentBounds?

Hello @walter,

I have created one example, can you please check.

To reproduce this scenario please follow bellow steps,

  1. Scroll to the bottom of the tree view by using scrollbar.

  2. Now drag any item towards the bottom of tree and after dragging node to bottom, tree view will start to scroll upward and it will show empty area at the bottom.

So please guide me to avoid this.

I still don’t understand all of your requirements, but my best guess at this time is something like:

        $(go.Diagram, "myDiagramDiv",
          {
            "LayoutCompleted": function(e) {
              e.diagram._lastBottom = e.diagram.documentBounds.bottom;
            },
            "SelectionMoved": function(e) {
              e.diagram._lastBottom = e.diagram.documentBounds.bottom;
            },
            positionComputation: function(diag, newpos) {
              if (diag._lastBottom === undefined) return newpos;
              var dh = diag.documentBounds.height;
              var vh = diag.viewportBounds.height;
              if (dh <= vh) return newpos;
              var y = Math.max(newpos.y, diag.documentBounds.y);
              if (y > diag._lastBottom - vh) y = diag._lastBottom - vh;
              return new go.Point(newpos.x, y);
            },
            . . .

Yes, it is working with above change, thank you so much @walter for your help.

I just made one change in positionComputation to avoid top scroll and now it is working as expected.

    LayoutCompleted: function (e) {
      e.diagram._lastBottom = e.diagram.documentBounds.bottom;
    },
    SelectionMoved: function (e) {
      e.diagram._lastBottom = e.diagram.documentBounds.bottom;
    },
    positionComputation: function (diag, newpos) {
      if (!diag._lastBottom) return newpos;
      var dh = diag.documentBounds.height;
      var vh = diag.viewportBounds.height;
      if (dh <= vh) return newpos;
      var y = Math.max(newpos.y, diag.documentBounds.y);
      if (y > diag._lastBottom - vh) y = diag._lastBottom - vh;
      return new go.Point(newpos.x, y < 0 ? 0 : y);
    },

A bug in my code – it should be:

if (diag._lastBottom === undefined) return newpos;