Here’s a complete stand-alone sample:
<!DOCTYPE html>
<html>
<head>
<title>Modal GoJS Tool</title>
<!-- Copyright 1998-2023 by Northwoods Software Corporation. -->
<meta name="description" content="Demonstrate a mode where the user can click to insert a node; the mode is controlled outside of the Diagram">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
<button id="myTestButton">Start Insertion Mode</button>
<button id="myStopButton" disabled>Stop Mode</button>
<textarea id="mySavedModel" style="width:100%;height:350px"></textarea>
<script src="../release/go.js"></script>
<script id="code">
const $ = go.GraphObject.make;
const myDiagram =
new go.Diagram("myDiagramDiv",
{
"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, "Auto",
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
$(go.Shape,
{ fill: "white" },
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: 8 },
new go.Binding("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" }
],
[
{ from: 1, to: 2 },
{ from: 1, to: 3 },
{ from: 2, to: 2 },
{ from: 3, to: 4 },
{ from: 4, to: 1 }
]);
// This custom tool implements a "mode" where the only thing that the user can do
// is click to insert a node or cancel the tool by hitting the Escape key.
class CustomTool extends go.Tool {
isMultiShot = false;
doActivate() {
this.isActive = true;
this.diagram.defaultCursor = "crosshair";
}
doDeactivate() {
this.isActive = false;
this.diagram.defaultCursor = "";
document.getElementById("myStopButton").disabled = true;
}
doMouseUp() {
if (!this.isBeyondDragSize()) {
this.diagram.model.commit(m => {
m.addNodeData({
text: "Inserted by\nCustomTool",
loc: go.Point.stringify(this.diagram.lastInput.documentPoint),
color: "magenta"
});
});
// This is a one-shot tool -- it stops after successfully operating by inserting a node.
// Remove this call if you want the user to be able to keep inserting nodes by clicking.
// But then you'll need another way to stop this tool unless the user knows that they
// can cancel the tool by hitting Escape, which is not possible on touch-only devices.
// So we suggest that you only remove this call if you have that other mechanism,
// such as the "Stop Mode" button in this sample.
if (!this.isMultiShot) this.stopTool();
}
}
}
document.getElementById("myTestButton").addEventListener("click", e => {
myDiagram.currentTool = new CustomTool();
myDiagram.currentTool.doActivate();
document.getElementById("myStopButton").disabled = false;
});
document.getElementById("myStopButton").addEventListener("click", e => {
myDiagram.currentTool.stopTool();
});
</script>
</body>
</html>
[EDIT] I have extended the sample to support multi-shot operation of the CustomTool by allowing removal of the call to Tool.stopTool in the doMouseUp override. Users could then stop the tool by hitting that “Stop Model” HTML button.
[EDIT 2} I have extended it even further by adding a CustomTool.isMultiShot property.