Hmmm, that is odd. I do not see that issue on my end at all, routing is working for me after animation has finished. Just to be on the same page, I did update the script to target 3.0.8. Here is the full blown snippet on my end, updated with 3.0.8 scripts.
<!DOCTYPE html>
<html>
<head>
<title>Splice Node into Link with DropZone</title>
<!-- Copyright 1998-2024 by Northwoods Software Corporation. -->
</head>
<body>
<div style="width: 100%; display: flex; justify-content: space-between">
<div style="display: flex; flex-direction: column; margin: 0 2px 0 0">
<div id="myPaletteDiv" style="flex-grow: 1; width: 100px; background-color: floralwhite; border: solid 1px black"></div>
<div id="myOverviewDiv" style="margin: 2px 0 0 0; width: 100px; height: 100px; background-color: whitesmoke; border: solid 1px black"></div>
</div>
<div id="myDiagramDiv" style="flex-grow: 1; height: 400px; border: solid 1px black"></div>
</div>
<textarea id="mySavedModel" style="width: 100%; height: 250px"></textarea>
<script src="https://unpkg.com/[email protected]/release/go.js"></script>
<script src="https://unpkg.com/[email protected]/dist/extensions/NonRealtimeDraggingTool.js"></script>
<script id="code">
class DropZoneDraggingTool extends NonRealtimeDraggingTool {
constructor(init) {
super();
this.subtreeOffset = new go.Point(100, 0);
const $ = go.GraphObject.make;
this.DropZone = $(
go.Adornment,
"Auto",
{ locationSpot: go.Spot.Center },
$(go.Shape, "RoundedRectangle", { fill: "white", strokeWidth: 5, stroke: "lightblue" }),
$(
go.Panel,
"Auto",
{ width: 100, height: 60, margin: 5 },
$(go.Shape, "RoundedRectangle", { fill: "white", stroke: "lightblue", strokeDashArray: [4, 2] }),
$(
go.Panel,
"Spot",
$(go.Shape, { fill: "white", stroke: "lightgray", strokeDashArray: [4, 2], width: 30, height: 20 }),
$(go.Shape, {
alignment: new go.Spot(0.5, 0.5, 0, -10),
geometryString: "M5 0L5 20 M0 15 L5 20 10 15",
stroke: "lightgray",
strokeWidth: 2,
})
)
)
);
this.DropZone.ensureBounds();
// internal state
this._subtree = null;
this._wasAvoidsNodes = false;
this._draggedNode = null;
if (init) Object.assign(this, init);
}
// User must drag a single Node
findDraggablePart() {
if (this.diagram.selection.count > 1) return null;
const part = super.findDraggablePart();
if (part instanceof go.Node) {
this._draggedNode = part;
return part;
}
return null;
}
// Show a DropZone if dragging over a Link (other than one connected with _draggedNode)
doDragOver(pt, obj) {
// method override
// find Part at PT, ignoring temporary Parts except for the DropZone (temporary because it's an Adornment)
var trgt = this.diagram.findObjectAt(
pt,
(x) => x.part,
(x) => x === this.DropZone || !x.layer.isTemporary
);
if (trgt instanceof go.Link) {
const link = trgt;
if (link.fromNode === this._draggedNode || link.toNode === this._draggedNode) return;
if (this.DropZone.adornedPart === null) {
const mid = link.routeBounds.center;
const needsShift = link.routeBounds.width < this.DropZone.actualBounds.width + 100 || link.routeBounds.height < this.DropZone.actualBounds.height + 100;
const oldskips = this.diagram.skipsUndoManager;
this.diagram.skipsUndoManager = true;
if (needsShift) {
this._wasAvoidsNodes = link.routing === go.Link.AvoidsNodes;
if (this._wasAvoidsNodes) link.routing = go.Link.Orthogonal;
this._subtree = link.toNode.findTreeParts();
// shift subtree rightward
this.diagram.moveParts(this._subtree, this.subtreeOffset);
}
link.isHighlighted = true;
this.DropZone.adornedObject = link;
link.addAdornment("DropZone", this.DropZone);
this.DropZone.location = new go.Point(mid.x + 50, mid.y);
this.diagram.skipsUndoManager = oldskips;
}
} else if (trgt !== this.DropZone) {
this.cleanup();
}
}
cleanup() {
const link = this.DropZone.adornedPart;
if (link) {
const oldskips = this.diagram.skipsUndoManager;
this.diagram.skipsUndoManager = true;
if (this._subtree) {
// shift subtree leftward
this.diagram.moveParts(this._subtree, new go.Point(-this.subtreeOffset.x, -this.subtreeOffset.y));
this._subtree = null;
if (this._wasAvoidsNodes) link.routing = go.Link.AvoidsNodes;
this._wasAvoidsNodes = false;
}
link.isHighlighted = false;
link.removeAdornment("DropZone");
this.DropZone.adornedObject = null;
this.diagram.skipsUndoManager = oldskips;
}
}
// If dropped into DropZone, splice it into the corresponding Link
// (Note, not using doDropOnto due to undo problems.
// Overriding doMouseUp means needing "ExternalObjectsDropped" listener too,
// duplicating some of the work.)
doMouseUp() {
// method override
const link = this.DropZone.adornedPart;
const node = this._draggedNode;
const pt = this.diagram.lastInput.documentPoint;
const trgt = this.diagram.findObjectAt(
pt,
(x) => x.part,
(x) => x === this.DropZone
);
if (trgt === this.DropZone) {
this.cleanup();
this.spliceIntoLink(link, node);
} else {
this.cleanup();
}
super.doMouseUp();
}
// Splice the _draggedNode into the dropped-onto Link
spliceIntoLink(link, node) {
if (!link || !node) return;
const diag = this.diagram;
if (!diag) return;
// disconnect node being dropped (copy collection to avoid iterating over modifications)
new go.List(node.findLinksConnected()).each((l) => diag.remove(l));
const to = link.toNode;
const linkdata = {};
diag.model.addLinkData(linkdata);
const newlink = diag.findLinkForData(linkdata);
if (newlink !== null) {
// splice in that node
link.toNode = node;
newlink.fromNode = node;
newlink.toNode = to;
}
}
doDeactivate() {
this._subtree = null;
this._draggedNode = null;
super.doDeactivate();
}
doCancel() {
this.cleanup();
super.doCancel();
}
}
const $ = go.GraphObject.make;
const myDiagram = new go.Diagram("myDiagramDiv", {
contentAlignment: go.Spot.Center,
layout: new go.TreeLayout(),
// install the replacement DraggingTool:
draggingTool: new DropZoneDraggingTool({ duration: 400 }),
ExternalObjectsDropped: (e) => {
const tool = e.diagram.toolManager.draggingTool;
const pt = e.diagram.lastInput.documentPoint;
const trgt = e.diagram.findObjectAt(
pt,
(x) => x.part,
(x) => x === tool.DropZone
);
if (trgt === tool.DropZone) {
const link = tool.DropZone.adornedPart;
const node = e.diagram.selection.first();
tool.cleanup();
tool.spliceIntoLink(link, node);
}
},
"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", { locationSpot: go.Spot.Center }, $(go.Shape, { fill: "white" }, new go.Binding("fill", "color")), $(go.TextBlock, { margin: 8 }, new go.Binding("text")));
myDiagram.linkTemplate = $(
go.Link,
{ routing: go.Link.AvoidsNodes, reshapable: true },
// the highlight path Shape
$(
go.Shape,
{ isPanelMain: true, strokeWidth: 7, stroke: "transparent" },
// when highlighted, show this thick Shape in red
new go.Binding("stroke", "isHighlighted", (h) => (h ? "red" : "transparent")).ofObject()
),
// the normal path Shape
$(go.Shape, { isPanelMain: true, strokeWidth: 1.5 }),
$(go.Shape, { toArrow: "OpenTriangle" })
);
myDiagram.model = new go.GraphLinksModel(
[
{ key: 1, text: "Alpha", color: "lightblue" },
{ key: 3, text: "Gamma", color: "lightgreen" },
{ key: 4, text: "Delta", color: "pink" },
{ key: 5, text: "Epsilon", color: "yellow" },
],
[
{ from: 1, to: 3 },
{ from: 3, to: 4 },
{ from: 4, to: 5 },
]
);
// initialize Palette
myPalette = new go.Palette("myPaletteDiv", {
nodeTemplateMap: myDiagram.nodeTemplateMap,
model: new go.GraphLinksModel([
{ text: "red node", color: "red" },
{ text: "green node", color: "green" },
{ text: "blue node", color: "blue" },
{ text: "orange node", color: "orange" },
]),
});
myPalette.toolManager.draggingTool.doCancel = function () {
myDiagram.toolManager.draggingTool.doCancel();
go.DraggingTool.prototype.doCancel.call(this);
};
// initialize Overview
myOverview = new go.Overview("myOverviewDiv", {
observed: myDiagram,
contentAlignment: go.Spot.Center,
});
</script>
</body>
</html>