Maybe something like this:
<!DOCTYPE html>
<html>
<head>
<title>Minimal GoJS Editor</title>
<!-- Copyright 1998-2022 by Northwoods Software Corporation. -->
<script src="https://unpkg.com/gojs"></script>
<script id="code">
function init() {
var $ = go.GraphObject.make;
// initialize main Diagram
myDiagram =
$(go.Diagram, "myDiagramDiv",
{
layout: $(go.LayeredDigraphLayout,
{ direction: 90, layerSpacing: 50, setsPortSpots: false }),
"undoManager.isEnabled": true
});
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{ minSize: new go.Size(100, 50) },
$(go.Shape,
{ fill: "white", portId: "", cursor: "pointer",
fromSpot: go.Spot.BottomSide, toSpot: go.Spot.TopSide,
fromLinkable: true, toLinkable: true,
fromLinkableDuplicates: true, toLinkableDuplicates: true
},
new go.Binding("figure", "shape"),
new go.Binding("fill", "shape", function(s) { return s === "Triangle" ? "lightgreen" : "lightblue"; })),
$(go.TextBlock,
new go.Binding("text", "key", function(k) { return "Entity " + k; }))
);
myDiagram.linkTemplate =
$(go.Link,
{ fromEndSegmentLength: 0, toEndSegmentLength: 0 },
$(go.Shape),
$(go.TextBlock, { segmentIndex: 1, background: "white" },
new go.Binding("text", "", function(d) { return (d.label || "") + "[Code " + (d.code || "") + "]"; }),
new go.Binding("segmentFraction", "key", function(k) { return k % 2 === 0 ? 0.333 : 0.667; }))
);
load();
}
// save a model to and load a model from Json text, displayed below the Diagram
function save() {
var str = myDiagram.model.toJson();
document.getElementById("mySavedModel").value = str;
}
function load() {
var str = document.getElementById("mySavedModel").value;
myDiagram.model = go.Model.fromJson(str);
}
</script>
</head>
<body onload="init()">
<div id="myDiagramDiv" style="width: 100%; height: 400px; border: solid 1px black"></div>
<div id="buttons">
<button id="loadModel" onclick="load()">Load</button>
<button id="saveModel" onclick="save()">Save</button>
</div>
<textarea id="mySavedModel" style="width:100%;height:200px">
{ "class": "GraphLinksModel",
"linkKeyProperty": "key",
"nodeDataArray": [
{"key":1,"shape":"Triangle"},
{"key":2,"shape":"Triangle"},
{"key":3,"shape":"Triangle"},
{"key":4,"shape":"Rectangle"},
{"key":5,"shape":"Rectangle"},
{"key":6,"shape":"Rectangle"}
],
"linkDataArray": [
{"key":1,"from":1,"to":4,"label":"75%","code":1},
{"key":2,"from":2,"to":4,"label":"13%","code":2},
{"key":3,"from":3,"to":4,"label":"12%","code":3},
{"key":4,"from":1,"to":5,"label":"100%","code":4},
{"key":5,"from":1,"to":4,"label":"30%","code":6},
{"key":6,"from":2,"to":6,"label":"25%","code":7},
{"key":7,"from":3,"to":6,"label":"77%","code":8}
]}
</textarea>
</body>
</html>
It depends on setting LayeredDigraphLayout.setsPortSpots to false so that the node/port can specify “…Side” Spots for fromSpot and toSpot.
It also depends on changing the segmentFraction so that the labels don’t overlap as much.