Dropping from Palette action works on first drop only

I’m getting an unexpected situation where dropping a Node from Palette onto Diagram works on first drop, but then on 2nd drop, the same target (orange prepend) Node no longer takes the drop.

Do I need to run some update/redraw method on every drop?

Attaching screenshots and code…



diagram.requestUpdate() didn’t do anything in this context…

const handleDocletTypeDrop = (
		droppedNode: go.ObjectData,
		dModel: go.GraphLinksModel,
		pModel: go.GraphLinksModel
	) => {
		const prependLink = dModel.linkDataArray[0];
		const firstNode = dModel.nodeDataArray[1];

		dModel.commit((m: go.Model) => {
			m.set(droppedNode, 'state', GoJsNodeState.Diagram);

			dModel.removeLinkData(prependLink);

			dModel.addLinkDataCollection([
				{
					from: prependLink.from,
					to: droppedNode.key,
				},
				{
					from: droppedNode.key,
					to: firstNode.key,
				},
			]);

			pModel.nodeDataArray.forEach((pNode: any) => {
				if (pNode.title === droppedNode.title) {
					pModel.commit((m: go.Model) => {
						m.set(pNode, 'state', GoJsNodeState.Copied);
					});
				}
			});
		});
	};

No, between transactions everything should be correct and up-to-date.

It’s odd that your code is assuming that the “prependLink” is the first link data object and the “firstNode” is the second node data object, but maybe that’s because the code is experimental and you haven’t yet transformed the drop point into document coordinates so that you can find the Node or Link that was dropped upon (if any) via Diagram.findPartAt.

I assume you have already closely examined the code in HTML Drag and Drop, and external Clipboard pasting

Thank you. Following your example these calls get me the error Argument of type ‘“Drop”’ is not assignable to parameter of type ‘DiagramEventName’.

diagram.addDiagramListener('Drop', mouseDrop);
diagram.addDiagramListener('drop', mouseDrop);

Looking at the code example you referenced and the DiagramEvent.html#name Docs, do I really need to reference the actual div element in order to add the “drop” event? I don’t see a drop DiagramEventName listed…

The main difference I noticed is that your code example adds the event listeners directly to the Diagram whereas I am adding them on the mouseDrop prop of a go.Node template.

“drop” is an HTML element event: HTMLElement: drop event - Web APIs | MDN, not a DiagramEvent.

I’m trying my best to stick to ReactDiagram design and avoid working on the native HTML element but I’m implementing your code example because I honestly don’t know what else to do to make it work.

The ‘drop’ event handler is not firing on any type of drop from the Palette.

<ReactDiagram
					ref={diagramRef}
					initDiagram={initDiagram}
					divClassName="goJsDiagram"
					nodeDataArray={diagramData.nodeDataArray}
					linkDataArray={diagramData.linkDataArray}
					onModelChange={onModelChange}
				/>

const initDiagram = () => {
		const $ = go.GraphObject.make;

		const diagram = $(go.Diagram, {
			isTreePathToChildren: false,
			allowMove: false,
			contentAlignment: go.Spot.TopCenter,			
			layout: $(go.TreeLayout, {
				layerSpacing: 10,
				angle: 270,
			}),
			model: new go.GraphLinksModel({
				linkKeyProperty: 'key',
			}),
			allowDrop: true,
			maxSelectionCount: 1,
			'undoManager.isEnabled': true, // must be set to allow for model change listening
		});

		const diagramDiv = document.getElementsByClassName('goJsDiagram')[0];

		if (diagramDiv) {
			// Add Event Listener
			diagramDiv.addEventListener('drop', mouseDrop);
		}

		// Add Node Templates
		diagram.nodeTemplateMap.add(goJsCategory.DropDocletType, dropDocletType);
		diagram.nodeTemplateMap.add(goJsCategory.DocletTypeNode, docletTypeNodes);
		diagram.nodeTemplateMap.add(goJsCategory.ImportNode, importNode);

		// Add Link Template
		diagram.linkTemplateMap.add('', linkTemplate);

		// Disable animations
		diagram.animationManager.isEnabled = false;

		// Return Diagram instance
		return diagram;
	};

Can we focus on my original issue please? I feel we got side-tracked… and I’m dead in the water here.

I previously got side-tracked finding the Parts but got that resolved so I deleted that post.

My original issue persists, after a successful Drop, the Diagram loses the event handlers on the Nodes that I modified through the Drop handler logic.

dModel.commit((m: go.Model) => {
					m.set(newNode, 'state', GoJsNodeState.Diagram);

					dModel.removeLinkData(linkToRemove);

					dModel.addLinkDataCollection([
						{
							from: linkToRemove.from,
							to: newNode.key,
						},
						{
							from: newNode.key,
							to: nextNode.key,
						},
					]);
				});

As previously stated, adding this div.addEventListener("drop", mouseDrop) HTML drop event handler is not firing and I’m not sure how my logic behavior would be any different after a Drop event using this event handler as opposed to the GoJs event diagram.addDiagramListener('ExternalObjectsDropped', externalObjectsDropped);

We feel that it is a Diagram re-drawing issue… after a drop the mouse hovers act erratically and the more Palette Nodes I drop the weirder it gets… what would be a force re-draw method to call after calling Model.set()?

Calling Model methods to modify model state causes some updates of GraphObjects and the Diagram, but really the bulk of the updating only happens at the end of a transaction.

As you have seen in the HTML Drag and Drop sample, HTML Drag and Drop, and external Clipboard pasting, the “drop” code makes changes to the model within a transaction. The code that you have quoted seems perfectly reasonable to me.

As far as the “drop” HTML DOM event is concerned, you’ll want to check that you are adding the event handler on the actual Div, not on any virtual DOM element.

I had actually already tried that but I looked closely and still no luck after first Drop.

I also tried the method calls removeLinkData() addLinkDataCollection() commitTransaction() calls inside the commit() method but it’s always the same behaviour.

const handleDocletTypeDrop = (
		newNode: go.ObjectData,
		targetNode: go.Part,
		dModel: go.GraphLinksModel,
		diagram: go.Diagram
	) => {
		let linkToRemove: any;
		let nextNode: any;

		switch (targetNode.key) {
			case 'prepend':
				diagram.startTransaction('handleDocletTypeDrop');
				linkToRemove = dModel.linkDataArray[0];
				nextNode = dModel.nodeDataArray[1];

				dModel.commit((m: go.Model) => {
					m.set(newNode, 'state', GoJsNodeState.Diagram);
				});

				dModel.removeLinkData(linkToRemove);

				dModel.addLinkDataCollection([
					{
						from: linkToRemove.from,
						to: newNode.key,
					},
					{
						from: newNode.key,
						to: nextNode.key,
					},
				]);

				diagram.commitTransaction('handleDocletTypeDrop');

				break;
		}
	};

Executing a transaction within a transaction is OK, just wasteful, so I would remove that call to commit and just call set directly. But this suggestion does not change any behavior.

I’m sorry I cannot help more without more knowledge about the situation.

Maybe you could experiment with an HTML button that calls this code, so that you can make sure this code works correctly. Afterwards you can concentrate on getting the drag-and-drop to work.

I’m working on a freshly created create-react-app app and a bare-bone ReactDiagram & ReactPalette configuration.

I have a question: for ReactDiagram & ReactPalette to work as you’d expect, does the app state MUST be updated through the onModelChange callback?

I want to feed the initial nodeDataArray from a static object in a .ts file and keep the components stateless because I want to remove everything that is NOT required for GoJs to work as it’s expected.

Thank you!

Have you seen the react-palette sample?

I put together this public repo and info steps in its README to share with you so you can see the issue which I am still seeing even when I take your suggested code base and add my mouse drop logic to it… the issue persists.

Thanks – we’ll look at it.

The problem is that your link has a very large strokeWidth and ends up covering the “prepend” node.

Your diagram starts with the node being on top of the link. When you drop the first node, the newly created link ends up on top of the node due to its large strokeWidth.

A simple solution is to put your links in the background layer.

  const linkTemplate = $(
    go.Link,
    { layerName: "Background" },
    $(go.Shape, { isPanelMain: true, strokeWidth: 1 }),
    $(go.Shape, {
      isPanelMain: true,
      stroke: "transparent",
      strokeWidth: 150,
    }),
    $(go.Shape, {
      toArrow: "Feather",
      strokeWidth: 2,
    })
  );

Example: https://codesandbox.io/p/sandbox/drop-on-node-4w34yj
Note that I made some other changes to your code, particularly the handleDocletTypeDrop function. It is now closer to what we do in our Flowgrammer sample.

WOW! it’d have taken me months to figure that out :-( thank you and happy holidays! hopefully I won’t be posting again until January… I’m off to vacations this Friday :-)

A quick solution was only possible because you provided a (non)working sample that wasn’t too difficult for us to understand.