Check out this simple decision-tree editor created using GoJS:
https://i.stack.imgur.com/5owGP.png
For the full source code, refer to:
<!DOCTYPE html>
<html>
<head>
<title>Minimal Decision Tree Editor</title>
<!-- Copyright 1998-2023 by Northwoods Software Corporation. -->
</head>
<body>
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:500px"></div>
Choosing a node displays various buttons, including a button for adding a new child node and link.
<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",
{
// move or delete entire subtrees
"draggingTool.dragsTree": true,
"commandHandler.deletesTree": true,
layout:
$(go.TreeLayout,
{ layerSpacing: 100, setsPortSpot: false, setsChildPortSpot: false }),
"undoManager.isEnabled": true,
"ModelChanged": e => {
if (e.isTransactionFinished) {
document.getElementById("mySavedModel").textContent = e.model.toJson();
}
}
});
// Define the node template
myDiagram.nodeTemplate =
$(go.Node, "Auto",
// prevent deletion of tree root nodes
new go.Binding("deletable", "", node => !!node.findTreeParentNode()).ofObject(),
$(go.Shape, "RoundedRectangle",
{ fill: "tomato", stroke: "lightgray" },
new go.Binding("fill", "isSelected", s => s ? go.Brush.darken("tomato") : "tomato").ofObject()),
$(go.Panel, "Table",
{ margin: 6 },
$(go.TextBlock,
{
row: 0, name: "Title",
stroke: "white", font: "bold 11pt sans-serif",
margin: new go.Margin(0, 0, 4, 0), editable: true
},
new go.Binding("text", "title").makeTwoWay()),
$(go.TextBlock,
{
row: 1, name: "Description",
stroke: "white", editable: true
},
new go.Binding("text", "description").makeTwoWay()),
)
);
// Define the selection adornment template for nodes
myDiagram.nodeTemplate.selectionAdornmentTemplate =
$(go.Adornment, "Spot",
$(go.Placeholder, { margin: 12 }),
$("Button",
$(go.Shape, { geometryString: "M12 0 L3 9 M2 10 L0 12", strokeWidth: 3 }),
{
alignment: new go.Spot(0, 0, -5, 15),
click: (e, button) => e.diagram.commandHandler.editTextBlock(button.part.adornedPart.findObject("Title"))
}),
// Additional buttons for editing description and expanding/collapsing subtree
);
// Define the link template
myDiagram.linkTemplate =
$(go.Link,
{
selectable: false,
click: (e, link) => e.diagram.commandHandler.editTextBlock(link.findObject("Text"))
},
$(go.Shape,
{ stroke: "gray", strokeWidth: 2 }),
$(go.Panel, "Auto",
$(go.Shape, "RoundedRectangle",
{ fill: "white", strokeWidth: 0 }),
$(go.TextBlock, "choice",
{
name: "Text", maxSize: new go.Size(100, NaN),
stroke: "gray", textAlign: "center", editable: true
},
new go.Binding("text").makeTwoWay())
)
);
// Create the model with nodes and links
myDiagram.model = new go.GraphLinksModel(
[
{ key: 1, title: "Decision 1", description: "Consider a, b, and c.\nDo you want to do X?" },
{ key: 2, title: "Decision 2", description: "Do you want to do Y?" },
{ key: 3, title: "Outcome 1", description: "The end" },
{ key: 4, title: "Outcome 1", description: "one solution" },
{ key: 5, title: "Outcome 2", description: "another solution" },
],
[
{ from: 1, to: 2, text: "Yes" },
{ from: 1, to: 3, text: "No" },
{ from: 2, to: 4, text: "Yes" },
{ from: 2, to: 5, text: "No" },
]);
</script>
</body>
</html>