About the movement of the node when TreeExpanderButton is displayed only when hovering over the node

I added a TreeExpanderButton to the node to make it visible on mouse hover.

I want to prevent adjacent nodes from moving when the TreeExpanderButton is displayed. How can I achieve that?"

bc4f9218a0ebff38a36839a859eb734b

Thanks

Yes – when a Node changes size, it normally invalidates its Layout, which is then performed again. In your case it adapts to the wider node in order to maintain a constant TreeLayout.layerSpacing.

So, you can either not change the node size or you can have it avoid invalidating the layout.

The former you could implement by moving the “TreeExpanderButton” into an Adornment that is shown on mouseHover.

The latter you could implement by setting Part.layoutConditions to exclude the go.Part.LayoutNodeSized flag:

$(go.Node,
  {
    layoutConditions: go.Part.LayoutStandard & ~go.Part.LayoutNodeSized,
    . . .
  },
  . . .
)
1 Like

Thanks.

I tried with the code you provided:

$(go.Node,
{
layoutConditions: go.Part.LayoutStandard & ~go.Part.LayoutNodeSized,
. . .
},
. . .
)

When I hover the mouse, nearby nodes do not move, but when I click a node, nearby nodes start to move. How can I resolve this issue?

e16beeb0917d0afefef8e0652971270a (2)

What does your click event handler do? (Or “ObjectSingleClicked” DiagramEvent listener.)

Event handlers are not used. Instead, we are using a function called nodeSelectionChanged when a node is clicked, as shown in the code below, to display the node using the code obj.findObject('TreeExpanderBtn').visible = true .

export function createNoteTemplate(self) {
  return $(
    go.Node,
    'Spot',
    {
      // ...
      selectionChanged: (obj) => nodeSelectionChanged(self, obj),
      // ...
    }
  );
}

In this code, the selectionChanged event of the node triggers the nodeSelectionChanged function when a node is clicked, and this function is responsible for making the node visible.

This Node property should prevent the layout from being invalidated and performed again at the end of the transaction just because you made the Node bigger or smaller by making elements visible or invisible.

This sample demonstrates that setting Node.layoutConditions works.
Before selecting everything:


After selecting everything:

<!DOCTYPE html>
<html>
<head>
  <title>Minimal GoJS Sample</title>
  <!-- Copyright 1998-2023 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
  <textarea id="mySavedModel" style="width:100%;height:250px"></textarea>

  <script src="https://unpkg.com/gojs"></script>
  <script id="code">
const $ = go.GraphObject.make;

const myDiagram =
  new go.Diagram("myDiagramDiv",
    {
      layout: $(go.TreeLayout, { angle: 0, layerSpacing: 20 }),
      "undoManager.isEnabled": true,
      "ModelChanged": e => {     // just for demonstration purposes,
        if (e.isTransactionFinished) {  // show the model data in the page's TextArea
          document.getElementById("mySavedModel").textContent = e.model.toJson();
        }
      }
    });

myDiagram.nodeTemplate =
  $(go.Node, "Spot",
    {
      locationObjectName: "BODY", locationSpot: go.Spot.Center,
      selectionChanged: node => {
        const button = node.findObject("BUTTON");
        if (button) button.visible = node.isSelected;
      },
      layoutConditions: go.Part.LayoutStandard & ~go.Part.LayoutNodeSized
    },
    $(go.Panel, "Auto",
      { name: "BODY", width: 120, height: 60 },
      $(go.Shape,
        { fill: "white", strokeWidth: 3 },
        new go.Binding("fill", "color")),
      $(go.TextBlock,
        { stroke: "white", font: "bold 11pt sans-serif" },
        new go.Binding("text")),
    ),
    $(go.Panel,
      { name: "BUTTON", alignment: go.Spot.Right, visible: false },
      $("TreeExpanderButton",
        {
          "ButtonBorder.figure": "Circle",
          "ButtonIcon.visible": false
        },
        $(go.TextBlock, "XYZ",
          new go.Binding("text", "value"))
      )
    )
  );

myDiagram.model = new go.GraphLinksModel(
[
  { key: 1, text: "ABC", color: "purple", value: 1 },
  { key: 2, text: "CAN", color: "darkred", value: 10 },
  { key: 3, text: "DRE2", color: "darkred", value: 1000 },
  { key: 4, text: "DEF", color: "slateblue" },
],
[
  { from: 1, to: 2 },
  { from: 2, to: 3 },
  { from: 3, to: 4 },
]);
  </script>
</body>
</html>

1bb634c7ae1725f91f52a7da47b923a3 (1)

I added the following code, but when I click, adjacent nodes are moving.
layoutConditions: go.Part.LayoutStandard & ~go.Part.LayoutNodeSized,

Here is the detailed code:

function createNodeTemplate(self) {
  return $(
    go.Node,
    'Spot',
    {
      cursor: 'move',
      locationSpot: go.Spot.Left,
      selectionObjectName: 'NODE',
      layoutConditions: go.Part.LayoutStandard & ~go.Part.LayoutNodeSized,
      selectionChanged: (obj) => nodeSelectionChanged(self, obj),
      mouseHover: (_e, obj) => nodeMouseHover(self, obj),
      mouseLeave: (_e, obj) => nodeMouseLeave(self, obj),

      selectionAdornmentTemplate: $(
        go.Adornment,
        'Spot',
        $(go.Panel, 'Auto', $(go.Placeholder)),
      ),
      contextMenu: $(go.HTMLInfo, {
        show: self.showNodeContextMenu,
        hide: self.hideNodeContextMenu,
      }),
    },
    // Add other node properties and elements here
  );
}

How are nodeSelectionChanged, nodeMouseHover, and nodeMouseLeave defined?

Thank you walter.

I have sent the code necessary for analysis via email. Additionally, I will supplement the code that was not sent via email here:

function createNodeTemplate(self) {
  return $(
    go.Node,
    'Spot',
    {
      cursor: 'move',
      locationSpot: go.Spot.Left,
      selectionObjectName: 'NODE',
      layoutConditions: go.Part.LayoutStandard & ~go.Part.LayoutNodeSized,
      selectionChanged: (obj) => nodeSelectionChanged(self, obj),
      mouseHover: (_e, obj) => nodeMouseHover(self, obj),
      mouseLeave: (_e, obj) => nodeMouseLeave(self, obj),

      selectionAdornmentTemplate: $(
        go.Adornment,
        'Spot',
        $(go.Panel, 'Auto', $(go.Placeholder)),
      ),
      contextMenu: $(go.HTMLInfo, {
        show: self.showNodeContextMenu,
        hide: self.hideNodeContextMenu,
      }),
    },
    // ..
    $(
      'TreeExpanderButton',
      { name: 'NodeTreeExpanderBtn' },
      {
        alignment: go.Spot.Right,
        'ButtonIcon.strokeWidth': 0,
        'ButtonBorder.figure': 'Circle',
      },
      new go.Binding('visible', '', (obj) => {
        return !(obj.isTreeLeaf || obj.isTreeExpanded && !obj.isTreeLeaf);
      }).ofObject()
    ),
    // ..
  );
}

There is still so much that remains undefined, so I continue to be unable to reproduce the problem.

Thank you for your response, Walter.

I set resizable to true with layoutConditions: go.Part.LayoutStandard & ~go.Part.LayoutNodeSized to enable resizing of the node. However, as a result, a larger frame appeared around the node. Could this be causing movement in adjacent nodes?

Is there a way to exclude the size of the TreeExpanderButton from the node?
I would like the adjacent nodes not to move when clicking on a node.

8f41745951da9a78b7d49ee45d1aae52 (1)

From that video it appears that whatever code that is making the ports visible is also causing a layout to happen. It has nothing to do with the visibility of the button.

So, what is the user doing in the video when the layout happens? What is all of code that executes then? That code was not included in what you had shared with me separately.

The presence of the code below seems to determine whether the port is displayed or not.

function createBaseNodeTemplate(self) {
  return $(
    go.Node,
    'Spot',
    {
     // ..
      resizable: true, // This part is relevant.

Whether or not a node is resizable has no bearing on whether or when it might be showing separate ports at the middle of each side of the node.

Find the code that shows/hides ports, and change that code so that it sets the opacity of each port to 0.0 or 1.0 instead of setting visible to false or true.