I want to move the product placed to empty node location without allowing the two occupied node to swap. If the location is occupied I should not be able to move to that node.
Did you want to disallow a drop at such a location? Or did you want to disallow a drag over and a drop at such a location?
For the latter, implement a Part.dragComputation function that prevents the node from going where there already exist nodes. There are a number of examples in the documentation and samples.
- To move a product / bin from one position to another, user can select the product, drag and place it in the new location, provided the new location is empty.
- The product will be cleared from the old position
Could you please tell us more about what kinds of nodes there are, and how they are arranged?
Try this sample:
<!DOCTYPE html>
<html>
<head>
<title>Simple Table with Empty Slots</title>
<!-- Copyright 1998-2023 by Northwoods Software Corporation. -->
</head>
<body>
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "GraphLinksModel",
"nodeDataArray": [
{"key":1,"text":"Alpha","color":"lightblue","row":0,"col":0},
{"key":2,"text":"Beta","color":"orange","row":0,"col":1},
{"key":3,"text":"Gamma","color":"lightgreen","row":1,"col":0},
{"key":4,"text":"Delta","color":"pink","row":1,"col":1},
{"key":5,"text":"Epsilon","color":"lightblue","row":1,"col":3},
{"key":6,"text":"Zeta","color":"orange","row":2,"col":2},
{"key":7,"text":"Eta","color":"lightgreen","row":0,"col":3},
{"key":8,"text":"Theta","color":"pink","row":3,"col":1},
{"key":9,"text":"Iota","color":"lightblue","row":3,"col":3},
{"key":10,"text":"Kappa","color":"orange","row":2,"col":5},
{"key":11,"text":"Lambda","color":"lightgreen","row":2,"col":4},
{"key":12,"text":"Mu","color":"pink","row":2,"col":0},
{"category":"Empty","row":0,"col":2},
{"category":"Empty","row":0,"col":4},
{"category":"Empty","row":0,"col":5},
{"category":"Empty","row":1,"col":2},
{"category":"Empty","row":1,"col":4},
{"category":"Empty","row":1,"col":5},
{"category":"Empty","row":2,"col":1},
{"category":"Empty","row":2,"col":3},
{"category":"Empty","row":3,"col":0},
{"category":"Empty","row":3,"col":2},
{"category":"Empty","row":3,"col":4},
{"category":"Empty","row":3,"col":5}
]}
</textarea>
<script src="https://unpkg.com/gojs"></script>
<script src="https://unpkg.com/gojs/extensions/TableLayout.js"></script>
<script id="code">
const $ = go.GraphObject.make;
const CELLW = 120;
const CELLH = 120;
const TABLEW = 6;
const TABLEH = 4;
const myDiagram =
new go.Diagram("myDiagramDiv",
{
layout: $(TableLayout),
"draggingTool.isGridSnapEnabled": true,
"draggingTool.gridSnapCellSize": new go.Size(CELLW, CELLH),
"draggingTool.gridSnapCellSpot": go.Spot.Center,
"SelectionMoved": e => { // update the data.row and .col
e.subject.each(node => {
const oldrow = node.data.row || 0;
const oldcol = node.data.col || 0;
const row = Math.floor(node.location.y/CELLH);
const col = Math.floor(node.location.x/CELLW);
const oldpart = findPartAt(row, col);
const m = e.diagram.model;
if (oldpart && oldpart.category === "Empty") {
m.set(node.data, "row", row);
m.set(node.data, "col", col);
m.set(oldpart.data, "row", oldrow);
m.set(oldpart.data, "col", oldcol);
}
// else don't move this node! the layout will move it back
});
e.diagram.layout.invalidateLayout();
},
"SelectionCopied": e => { // update the data.row and .col
e.subject.each(node => {
const row = Math.floor(node.location.y/CELLH);
const col = Math.floor(node.location.x/CELLW);
const oldpart = findPartAtRowCol(row, col);
const m = e.diagram.model;
if (oldpart && oldpart.category === "Empty") {
m.removeNodeData(oldpart.data);
m.set(node.data, "row", row);
m.set(node.data, "col", col);
}
});
e.diagram.layout.invalidateLayout();
},
"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();
}
}
});
// don't use the infinite Diagram.grid, but just a fixed, limited grid in the "Grid" layer
myDiagram.add(
$(go.Part, "Grid",
{ layerName: "Grid", position: new go.Point(0, 0) },
{ width: TABLEW*CELLW+1, height: TABLEH*CELLH+1, gridCellSize: new go.Size(CELLW, CELLH) },
$(go.Shape, "LineH", { stroke: "gray" }),
$(go.Shape, "LineV", { stroke: "gray" })
));
// look up the Node or Empty Part that is at a particular row/column
function findPartAt(r, c) {
const a = myDiagram.model.nodeDataArray;
for (let i = 0; i < a.length; i++) {
if (a[i].row === r && a[i].col === c) return myDiagram.findPartForData(a[i]);
}
return null;
}
myDiagram.nodeTemplate =
$(go.Node, "Auto",
{
width: CELLW, height: CELLH,
locationSpot: go.Spot.Center,
minLocation: new go.Point(CELLW/2, CELLH/2), // don't let nodes be moved beyond the table
maxLocation: new go.Point(TABLEW*CELLW-CELLW/2, TABLEH*CELLH-CELLH/2)
},
new go.Binding("row"),
new go.Binding("column", "col"),
new go.Binding("layerName", "isSelected", s => s ? "Foreground" : "").ofObject(),
$(go.Shape,
{ fill: "white", stroke: null },
new go.Binding("fill", "color")),
$(go.TextBlock,
new go.Binding("text"))
);
myDiagram.nodeTemplateMap.add("Empty",
$(go.Part, "Auto",
{ width: CELLW, height: CELLH, locationSpot: go.Spot.Center, layerName: "Background" },
{ selectable: false, movable: false, copyable: false, deletable: false },
new go.Binding("row"),
new go.Binding("column", "col"),
$(go.Shape,
{ fill: "white", stroke: null, pickable: false }),
$(go.TextBlock, "Drag & Drop\nSKU / Bin",
{ stroke: "gray", font: "italic 12pt sans-serif", textAlign: "center" })
));
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").textContent);
</script>
</body>
</html>