Try this:
<!DOCTYPE html>
<html>
<head>
<title>Parts with only context click</title>
<!-- Copyright 1998-2024 by Northwoods Software Corporation. -->
</head>
<body>
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
"Lockable" Parts are normally in the un-<b>pickable</b> Layer named "Lockable".
But ContextMenuTool has been customized to support context clicks on "Lockable" Parts.
Users can edit the text of a "Lockable" Part only if it is unlocked (i.e. in the default Layer).
<textarea id="mySavedModel" style="width:100%;height:250px"></textarea>
<script src="https://unpkg.com/gojs"></script>
<script id="code">
// Allow context clicks to find Parts in the "Lockable" Layer
class LockableContextMenuTool extends go.ContextMenuTool {
findObjectWithContextMenu(obj) {
let found = null;
const lay = this.diagram.findLayer("Lockable");
if (lay) {
this.diagram.commit(diag => {
lay.pickable = true;
lay.isTemporary = false;
found = super.findObjectWithContextMenu(obj);
lay.pickable = false;
lay.isTemporary = true;
}, null); // skipsUndoManager
} else {
found = super.findObjectWithContextMenu(obj);
}
return found;
}
}
const myDiagram =
new go.Diagram("myDiagramDiv", {
// install custom ContextMenuTool
contextMenuTool: new LockableContextMenuTool(),
"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();
}
}
});
// create a new Layer just for "Lockable"s
myDiagram.addLayerAfter(
new go.Layer({ name: "Lockable", isTemporary: true, pickable: false }),
myDiagram.findLayer("Grid")
);
myDiagram.nodeTemplate =
new go.Node("Auto")
.add(
new go.Shape({ fill: "white" })
.bind("fill", "color"),
new go.TextBlock({ margin: 8 })
.bind("text")
);
// The special kind of Part that is normally in the "Lockable" Layer.
// It has a context menu that supports unlocking the Part by moving it to the default Layer.
// Such a Part's context menu will then support locking that Part by moving it back to the "Lockable" Layer.
myDiagram.nodeTemplateMap.add("Lockable",
new go.Part("Auto", {
layerName: "Lockable",
contextMenu:
go.GraphObject.build("ContextMenu")
.add(
go.GraphObject.build("ContextMenuButton", {
click: (e, button) => {
e.diagram.commit(diag => {
const part = button.part.adornedPart;
part.layerName = part.layerName === "Lockable" ? "" : "Lockable";
part.isSelected = (part.layerName !== "Lockable");
});
}
})
.add(
new go.TextBlock("Unlock")
.bindObject("text", "adornedPart", part => part.layerName === "Lockable" ? "Unlock" : "Lock")
)
),
width: 100, height: 100
})
.bindTwoWay("location", "loc", go.Point.parse, go.Point.stringify)
.add(
new go.Shape({ fill: "whitesmoke" }),
new go.TextBlock({ editable: true })
.bindTwoWay("text")
));
myDiagram.model = new go.GraphLinksModel(
[
{ key: 1, text: "Alpha", color: "lightblue" },
{ key: 2, text: "Beta", color: "orange" },
{ key: 3, text: "Gamma", color: "lightgreen" },
{ key: 4, text: "Delta", color: "pink" },
{ category: "Lockable", text: "Lockable #1", loc: "0 0" },
{ category: "Lockable", text: "Lockable #2", loc: "-120 -120" },
{ category: "Lockable", text: "Lockable #3", loc: "150 150" },
],
[
{ from: 1, to: 2 },
{ from: 1, to: 3 },
{ from: 2, to: 2 },
{ from: 3, to: 4 },
{ from: 4, to: 1 }
]);
</script>
</body>
</html>