To add new node next to currently selected node

Hi Team, We are trying to replicate the lucid chart behaviour for positioning the nodes that are getting added to the canvas.

We have a left panel where all the shapes(images) will be there, Upon clicking them we are adding appropriate GOJS Nodes to the Canvas.

But while adding the node we want to position new node next to the currently selected node in canvas. How can we be able to do that ?

You also need to handle the case when there’s nothing selected in the main Diagram. In this case, I’m assuming the node is placed at the center of the viewport. But perhaps you don’t want to do anything.

  new go.Palette("myPaletteDiv",
    {
      "ObjectSingleClicked": e => {
        const node = e.subject.part;
        let p;
        if (myDiagram.selection.count > 0) {
          const b = myDiagram.computePartsBounds(myDiagram.selection);
          p = new go.Point(b.right + 30 + node.actualBounds.width/2, b.centerY);
        } else {
          p = myDiagram.viewportBounds.center;
        }
        myDiagram.model.commit(m => {
          const d = m.copyNodeData(node.data);
          d.location = go.Point.stringify(p);
          m.addNodeData(d);
        }, "added from palette");
      },
      . . .

Also you should decide what you want to do when the user repeated clicks a node in the Palette. In this case I’m just adding the node to the Diagram each time.

Here’s the complete sample:

<!DOCTYPE html>
<html>
<head>
  <title>Minimal GoJS Editor</title>
  <!-- Copyright 1998-2023 by Northwoods Software Corporation. -->
</head>
<body>
  <div style="width: 100%; display: flex; justify-content: space-between">
    <div style="display: flex; flex-direction: column; margin: 0 2px 0 0">
      <div id="myPaletteDiv" style="flex-grow: 1; width: 100px; background-color: floralwhite; border: solid 1px black"></div>
      <div id="myOverviewDiv" style="margin: 2px 0 0 0; width: 100px; height: 100px; background-color: whitesmoke; border: solid 1px black"></div>
    </div>
    <div id="myDiagramDiv" style="flex-grow: 1; height: 400px; border: solid 1px black"></div>
  </div>
  <div>
    <button id="myLoadButton">Load</button>
    <button id="mySaveButton">Save</button>
  </div>
  <textarea id="mySavedModel" style="width:100%;height:200px">
{ "class": "go.GraphLinksModel",
  "nodeDataArray": [
{"key":1, "text":"hello", "color":"green", "location":"0 0"},
{"key":2, "text":"world", "color":"red", "location":"70 0"}
  ],
  "linkDataArray": [
{"from":1, "to":2}
  ]}
  </textarea>

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

// initialize main Diagram
const myDiagram =
  new go.Diagram("myDiagramDiv",
    {
      "undoManager.isEnabled": true
    });

myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    { locationSpot: go.Spot.Center },
    new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
    $(go.Shape,
      {
        fill: "white", stroke: "gray", strokeWidth: 2,
        portId: "", fromLinkable: true, toLinkable: true,
      },
      new go.Binding("stroke", "color")),
    $(go.TextBlock,
      {
        margin: new go.Margin(5, 5, 3, 5), font: "10pt sans-serif",
        minSize: new go.Size(16, 16), maxSize: new go.Size(120, NaN),
        editable: true
      },
      new go.Binding("text").makeTwoWay())
  );

// initialize Palette
myPalette =
  new go.Palette("myPaletteDiv",
    {
      "ObjectSingleClicked": e => {
        const node = e.subject.part;
        let p;
        if (myDiagram.selection.count > 0) {
          const b = myDiagram.computePartsBounds(myDiagram.selection);
          p = new go.Point(b.right + 30 + node.actualBounds.width/2, b.centerY);
        } else {
          p = myDiagram.viewportBounds.center;
        }
        myDiagram.model.commit(m => {
          const d = m.copyNodeData(node.data);
          d.location = go.Point.stringify(p);
          m.addNodeData(d);
        }, "added from palette");
      },
      nodeTemplateMap: myDiagram.nodeTemplateMap,
      model: new go.GraphLinksModel([
        { text: "red node", color: "red" },
        { text: "green node", color: "green" },
        { text: "blue node", color: "blue" },
        { text: "orange node", color: "orange" }
      ])
    });

// initialize Overview
myOverview =
  new go.Overview("myOverviewDiv",
    {
      observed: myDiagram,
      contentAlignment: go.Spot.Center
    });

// save a model to and load a model from Json text, displayed below the Diagram
function save() {
  const str = myDiagram.model.toJson();
  document.getElementById("mySavedModel").value = str;
}
document.getElementById("mySaveButton").addEventListener("click", save);

function load() {
  const str = document.getElementById("mySavedModel").value;
  myDiagram.model = go.Model.fromJson(str);
}
document.getElementById("myLoadButton").addEventListener("click", load);

load();
  </script>
</body>
</html>

Thanks @walter, This is working. I’ve one more question on top of it… We want to add new node after certain gap of the existing node.

For that, we want to depend on nodeSpacing property in TreeLayout, But this will not work incase of Grid Layout. In GridLayout we have to depend on spacing.width property. Is there any generic property available in diagram object which will give the spacing value according to the given layout and configuration?

Each Layout is different, and some Layouts don’t guarantee any particular spacing.