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>