(the pointer is over the “Gamma” choice)
Of course you’ll want to style this the way that you want…
<!DOCTYPE html>
<html>
<head>
<title>Simple Choices Selector Adornment</title>
<!-- Copyright 1998-2024 by Northwoods Software Corporation. -->
</head>
<body>
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
<textarea id="mySavedModel" style="width:100%;height:150px"></textarea>
<script src="https://unpkg.com/gojs"></script>
<script id="code">
const myDiagram =
new go.Diagram("myDiagramDiv", {
"BackgroundSingleClicked": e => showChoices(null),
"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();
}
}
});
// the Adornment holding the list of choices
const ChoicesAdornment =
new go.Adornment("Spot")
.add(
new go.Placeholder(), // placeholder for the TextBlock in the Node
new go.Panel("Auto", { alignment: go.Spot.BottomLeft, alignmentFocus: go.Spot.TopLeft })
.add(
new go.Shape("RoundedRectangle", { fill: "white", stroke: "gray", strokeWidth: 2 }),
new go.Panel("Vertical", {
margin: 4,
defaultStretch: go.Stretch.Horizontal,
itemTemplate:
new go.Panel({
isActionable: true, // to allow a click event in an Adornment
click: (e, item) => {
const tb = item.part.adornedPart.findObject("TB");
if (!tb) return;
e.diagram.commit(diag => {
tb.text = item.data;
showChoices(null);
});
},
// for mouse-over highlighting
background: "transparent",
mouseEnter: (e, item) => item.background = "cyan",
mouseLeave: (e, item) => item.background = "transparent"
})
.add(
new go.TextBlock({ margin: 1 })
.bind("text", "") // TextBlock.text gets the whole Array item value
)
})
.bind("itemArray", "choices")
)
);
function showChoices(node) {
myDiagram.commit(diag => {
if (!node) {
const oldnode = ChoicesAdornment.adornedPart;
if (oldnode) {
const oldshp = oldnode.findObject("SHP");
if (oldshp) oldshp.figure = "LineDown";
oldnode.removeAdornment("Choices");
}
} else {
const tb = node.findObject("TB");
const shp = node.findObject("SHP");
if (!tb || !shp) return;
showChoices(null); // remove from any other node
shp.figure = "LineUp";
const ad = ChoicesAdornment;
ad.adornedObject = tb;
node.addAdornment("Choices", ad);
if (!Array.isArray(node.data.choices) || node.data.choices.length === 0) {
ad.data = { choices: ["Alpha", "Beta", "Gamma", "Delta"] }; // default choices Array
}
}
}, null); // skipsUndoManager
}
myDiagram.nodeTemplate =
new go.Node("Auto")
.add(
new go.Shape({ fill: "white" }),
new go.Panel("Vertical")
.add(
new go.TextBlock({ margin: 4, font: "bold 12pt sans-serif" })
.bind("text", "title"),
new go.Panel("Horizontal", {
click: (e, pnl) => { // show or hide the ChoicesAdornment
e.handled = true;
showChoices((pnl.findObject("SHP").figure === "LineDown") ? pnl.part : null);
},
// for mouse-over highlighting
background: "transparent",
mouseEnter: (e, pnl) => pnl.background = "lightgray",
mouseLeave: (e, pnl) => pnl.background = "transparent"
})
.add(
new go.TextBlock("(choose)", {
name: "TB",
width: 100,
margin: new go.Margin(4, 4, 2, 4),
font: "italic 10pt sans-serif",
stroke: "blue"
})
.bindTwoWay("text", "value")
.bind("font", "value", v => v ? "bold 10pt sans-serif" : "italic 10pt sans-serif")
.bind("stroke", "value", v => v ? "black" : "blue"),
new go.Shape("LineDown", {
name: "SHP",
width: 14, height: 12,
margin: 2,
strokeWidth: 2,
})
)
)
);
myDiagram.model = new go.GraphLinksModel(
[
{ key: 1, title: "Alpha", choices: ["one", "two", "three"], value: "one" },
{ key: 2, title: "Beta", choices: ["hello", "goodbye"] },
{ key: 3, title: "Gamma", choices: ["only"] },
{ key: 4, title: "Delta" }, // use a default choices Array
]);
</script>
</body>
</html>