Drop target using ExternalObjectsDropped

Hi,

Can we get drop target of a node with ExternalObjectsDropped event when dropped from palette to main diagram?

Basically I want to know if the drop target is a Node or a Link or Canvas.

When you want to have different behaviors for different kinds of targets, it’s common to set the GraphObject.mouseDrop event handler (and for feedback, the mouseDragEnter and mouseDragLeave event handlers), rather than using a global “ExternalObjectsDropped” DiagramEvent listener.

I suppose it’s convenient that in the latter case you know that it’s an external-drag-and-drop, whereas for the mouseDrag… and mouseDrop event handlers it could be either internal or external. If you want the same behavior for both internal and external, then that’s good; but if you want different behavior you can just check whether .diagram.currentTool instanceof go.DraggingTool or not. For internal drags, it’s the DraggingTool that’s running; for external drags, it’s another Diagram’s DraggingTool that’s running, but not the target Node’s or Link’s Diagram’s DraggingTool.

Yes we need different behaviours for Drop from Palette and drop within the diagram. For some reason diagram.currentTool instanceof go.DraggingTool is always false for me. I tried to check this in both mouseDragEnter and mouseDrop. It is false in both the places.

Really? I just tried this, and it produced the results that I expected as I tried dropping nodes onto a node either from a Palette or from within the same Diagram.

const myDiagram =
  new go.Diagram("myDiagramDiv", {
      mouseDrop: e => console.log("Background",
            e.diagram.currentTool.name,
            e.diagram.currentTool instanceof go.DraggingTool)
    });

myDiagram.nodeTemplate =
  new go.Node("Auto", {
      mouseDrop: (e, node) => console.log(node.key,
            e.diagram.currentTool.name,
            e.diagram.currentTool instanceof go.DraggingTool)
    })
    . . .

Hi Walter,

I suspect there might be an issue with the way I’ve positioned the Palette and Diagram in my development environment. This could be why diagram.currentTool instanceof go.DraggingTool is always returning false for me. I also think this div placement issue might be related to another unresolved issue I’ve encountered: Drag from palette to diagram is not showing the shape - #17 by tejeshguggilla.

Regarding the current issue, I’m exploring various ways to identify drop targets from ExternalObjectsDropped, and I was considering the workaround below. When I drop a node within the diagram, newNodePart.position gives me the correct coordinates, and I can locate the part using these coordinates:

diagram.findPartAt(new go.Point(newNodePart.position.x, newNodePart.position.y))

However, when I drag a node from the Palette and drop it onto the Diagram canvas, newNodePart.position provides the coordinates of the Palette rather than the drop location. As a result, the workaround doesn’t work when dropping nodes from the Palette. Could this again be related to the

placement issue?

Make sure that the target Diagram HTMLDivElement has the size and position in your page that you are expecting.

BTW, you could just call diagram.findPartAt(newNodePart.position).

Have you registered an “ExternalObjectsDropped” DiagramEvent listener only on the target Diagram, yes? How did you get the value for newNodePart in that listener?

Yes Walter. I have “ExternalObjectsDropped” only on target diagram. However, when I drop a node within the diagram, I have tested newNodePart.position with mouseDrop event instead where I can see the correct coordinates. I would like to achieve the same from ExternalObjectsDropped as well when I drop node from Palette.

That’s why I was asking how you got the value for your newNodePart variable.

For the “ExternalObjectsDropped” event the DiagramEvent.subject will be the collection of Parts that were dropped (i.e. copied into the target Diagram). I suppose you could ignore all but the first() of that collection.

I retrieved the value for newNodePart because the node was already on the canvas as an orphan, initially added from the Palette. When I drag and drop this orphan node onto another canvas node and check newNodePart.position via the mouseDrop event, it provides the correct coordinates. Here, newNodePart refers to an existing orphan node on the canvas.

However, when I drag a node directly from the Palette onto an existing canvas node, the newNodePart.position in the ExternalObjectsDropped event shows the coordinates relative to the Palette diagram rather than the target diagram. In this scenario, newNodePart represents a node from the Palette.

For a drag-and-drop from one source Diagram/Palette to a different target Diagram, the user starts the Palette’s DraggingTool, which is dragging the selected Part(s) in the Palette.

When the drop has happened, in the “ExternalObjectsDropped” listener, the DiagramEvent.subject gives you the collection of dropped copies of the dragged Parts. Those Parts will be in the target Diagram. The listener is called within the transaction executed for the insertion of those new Parts.

That is why I am asking again how you got the value for newNodePart during the “ExternalObjectsDropped” DiagramEvent listener call.

I’m not sure if I’m fully understanding your question. If you’re asking how I obtained the newNodePart and where it’s coming from,

I’m using diagram.selection.first() in ExternalObjectsDropped event listener. Where diagram refers to Palette diagram.

So that way your newNodePart will be an existing Node in the Palette, not the newly copied Node that was dropped into the target Diagram.

If instead you referred to the target Diagram’s selection, or the DiagramEvent.subject:

  new go.Diagram(. . ., {
    "ExternalObjectsDropped": e => {
      const newNodePart = e.subject.first(); // or e.diagram.selection.first()
      . . .
    },
    . . .
  })

you would get the new Node in the target Diagram.

There are examples of this if you search the samples, such as in Flowgrammer

I have tried to incorporate the same Walter. Below is my revised code,

ExternalObjectsDropped: function(event) {
            const newNodePart = event.diagram.selection.first();
            *console.log(newNodePart.position);*
        },

But newNodePart.position is still giving me the coordinates as that of Palette diagram instead of target diagram Walter. Anything I am missing here?

When I try to reproduce the behavior that you describe, I do not see it. Here’s my code:

<!DOCTYPE html>
<html>
<head>
  <title>Minimal GoJS Editor</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>
  <div>
    <button id="myLoadButton">Load</button>
    <button id="mySaveButton">Save</button>
  </div>
  <textarea id="mySavedModel" style="width:100%;height:200px">
{ "class": "go.GraphLinksModel",
  "nodeDataArray": [
{"key":1, "text":"hello", "color":"green", "pos":"0 0"},
{"key":2, "text":"world", "color":"red", "pos":"70 0"}
  ],
  "linkDataArray": [
{"from":1, "to":2}
  ]}
  </textarea>

  <script src="https://cdn.jsdelivr.net/npm/gojs/release/go.js"></script>
  <script id="code">
// initialize main Diagram
const myDiagram =
  new go.Diagram("myDiagramDiv", {
      "ExternalObjectsDropped": e => console.log(go.Point.stringifyFixed(0)(e.subject.first().position)),
      "undoManager.isEnabled": true
    });

myDiagram.nodeTemplate =
  new go.Node("Auto")
    .bindTwoWay("position", "pos", go.Point.parse, go.Point.stringifyFixed(0))
    .add(
      new go.Shape({ fill: "white", stroke: "gray", strokeWidth: 2 })
        .bind("stroke", "color"),
      new go.TextBlock({ margin: new go.Margin(5, 5, 3, 5), font: "10pt sans-serif", editable: true })
        .bindTwoWay("text")
    );

// 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" }
      ])
    });

// initialize Overview
myOverview =
  new go.Overview("myOverviewDiv", {
      observed: myDiagram,
      contentAlignment: go.Spot.Center
    });

// save a model to and load a model from Json text, displayed below the Diagram
function save() {
  const str = myDiagram.model.toJson();
  document.getElementById("mySavedModel").value = str;
}
document.getElementById("mySaveButton").addEventListener("click", save);

function load() {
  const str = document.getElementById("mySavedModel").value;
  myDiagram.model = go.Model.fromJson(str);
}
document.getElementById("myLoadButton").addEventListener("click", load);

load();
  </script>
</body>
</html>