Hi all :)
I have this graph:
with this layout:
public initDiagram(): go.Diagram {
function TableCellLayout() {
TableLayout.call(this);
this._cellLayout = null; // this is applied to each table cell's collection of Parts, if there is more than one
}
go.Diagram.inherit(TableCellLayout, TableLayout);
Object.defineProperty(TableCellLayout.prototype, "cellLayout", {
get: function() { return this._cellLayout; },
set: function(val) {
if (val !== null && !(val instanceof go.Layout)) throw new Error("new TableCellLayout.cellLayout must be a Layout or null");
if (val !== this._cellLayout) {
this._cellLayout = val;
this.invalidateLayout();
}
}
});
// don't have the cellLayout layout Parts for which this is true --
// Parts that span cells
TableCellLayout.prototype.isNotInCell = function(part) {
return (part.rowSpan > 1 || part.columnSpan > 1);
}
TableCellLayout.prototype.beforeMeasure = function(parts, rowcol) {
var lay = this.cellLayout;
if (!lay) return;
lay.diagram = this.diagram;
var coll = new go.List();
var bnds = new go.Rect();
var tmp = new go.Rect();
// for each row i ...
for (var i = 0; i < rowcol.length; i++) {
var rows = rowcol[i];
if (!rows) continue;
var rowDef = this.getRowDefinition(i);
rowDef.originalMinimum = rowDef.minimum;
rowDef.minimum = 0;
for (var j = 0; j < rows.length; j++) {
// for each column j in row i ...
var parts = rows[j];
if (!parts) continue;
if (parts.length === 0) continue;
// collect the Parts to be laid out within the cell
coll.clear();
for (var k = 0; k < parts.length; k++) {
var part = parts[k];
if (this.isNotInCell(part)) continue;
coll.add(part);
if (part instanceof go.Node) {
// add Links that connect with another Node in this same cell
part.findLinksConnected().each(function(link) {
if (!link.isLayoutPositioned) return;
var other = link.getOtherNode(part);
if (other && other.row === i && other.column === j && other.rowSpan === 1 && other.columnSpan === 1) {
coll.add(link);
}
})
}
}
if (coll.count === 0) continue;
if (coll.count === 1) {
//@ts-ignore
coll.first().alignment = go.Spot.Default;
continue;
}
lay.doLayout(coll); // do the layout of just this cell's Parts
// determine the area occupied by the laid-out parts
bnds.setTo(0, 0, 0, 0);
var colDef = this.getColumnDefinition(j);
colDef.originalMinimum = colDef.minimum;
colDef.minimum = 0;
//@ts-ignore
for (var k = coll.iterator; k.next(); ) {
//@ts-ignore
var part = k.value;
if (part instanceof go.Link) continue;
tmp.set(part.actualBounds);
tmp.addMargin(part.margin);
if (!tmp.isReal()) continue;
if (k === 0) {
bnds.set(tmp);
} else {
bnds.unionRect(tmp);
}
}
// now make sure the RowColDefinitions are expanded if needed
rowDef.minimum = Math.max(rowDef.minimum, bnds.height);
colDef.minimum = Math.max(colDef.minimum, bnds.width);
// and assign alignment on each of the Parts
var mx = bnds.centerX;
var my = bnds.centerY;
//@ts-ignore
for (var k = coll.iterator; k.next(); ) {
//@ts-ignore
var part = k.value;
if (part instanceof go.Link) continue;
part.alignment = new go.Spot(0.5, 0.5, part.actualBounds.centerX - mx, part.actualBounds.centerY - my);
}
}
}
}
TableCellLayout.prototype.afterArrange = function(parts, rowcol) {
if (this.cellLayout) {
// restore all RowColDefinition.minimum
for (var i = 0; i < rowcol.length; i++) {
var columns = rowcol[i];
if (!columns) continue;
var rowDef = this.getRowDefinition(i);
if (typeof rowDef.originalMinimum === "number") rowDef.minimum = rowDef.originalMinimum;
for (var j = 0; j < columns.length; j++) {
var parts = columns[j];
if (!parts) continue;
if (parts.length <= 1) continue; // don't bother laying out just one node in a cell
var colDef = this.getColumnDefinition(j);
if (typeof colDef.originalMinimum === "number") colDef.minimum = colDef.originalMinimum;
}
}
}
this.updateTableGrid();
}
TableCellLayout.prototype.updateTableGrid = function() {
var lay = this;
var part = null;
lay.diagram.findLayer("Background").parts.each(function(p) { if (p.name === "TABLEGRID") part = p; });
if (!part) return;
var numcols = lay.columnCount;
var numrows = lay.rowCount;
var firstcolindex = 1;
var firstrowindex = 1;
var firstcoldef = lay.getColumnDefinition(firstcolindex);
var firstrowdef = lay.getRowDefinition(firstrowindex);
var lastcoldef = lay.getColumnDefinition(numcols-1);
var lastrowdef = lay.getRowDefinition(numrows-1);
// determine the extent of the grid
var left = firstcoldef.position;
var width = lastcoldef.position + lastcoldef.total - firstcoldef.position;
var top = firstrowdef.position;
var height = lastrowdef.position + lastrowdef.total - firstrowdef.position;
var eltIdx = 0;
var prevLine = part.elements.count > 0 ? part.elt(0) : new go.Shape();
function nextLine(fig, w, h) { // reuse any existing Shapes
var shp = null;
if (eltIdx < part.elements.count) {
shp = part.elt(eltIdx++);
} else {
shp = prevLine.copy();
eltIdx++;
part.add(shp);
}
shp.figure = fig;
shp.width = w;
shp.height = h;
return shp;
}
// set up the verticals
for (var i = firstcolindex; i < numcols; i++) {
var def = lay.getColumnDefinition(i);
var shp = nextLine("LineV", 0, height);
shp.position = new go.Point(def.position, top);
}
// final line on right side
var shp = nextLine("LineV", 0, height);
shp.position = new go.Point(lastcoldef.position + lastcoldef.total, top);
// set up the horizontals
for (var i = firstrowindex; i < numrows; i++) {
var def = lay.getRowDefinition(i);
var shp = nextLine("LineH", width, 0);
shp.position = new go.Point(left, def.position);
}
// final line at bottom
var shp = nextLine("LineH", width, 0);
shp.position = new go.Point(left, lastrowdef.position + lastrowdef.total);
// get rid of any unneeded shapes
while (part.elements.count > eltIdx) part.removeAt(eltIdx);
}
const $ = go.GraphObject.make;
const dia =
$(go.Diagram,
{
layout:
// @ts-ignore
$(TableCellLayout,
$(go.RowColumnDefinition, { row: 0, height: 50, minimum: 50, alignment: go.Spot.Bottom }),
$(go.RowColumnDefinition, { column: 0, width: 100, minimum: 100, alignment: go.Spot.Right }),
{
cellLayout: $(go.GridLayout, {wrappingColumn: 2, spacing: new go.Size(100, 100) })
}
),
"SelectionCopied": updateCells, // reassign cell of each copied or moved node
"SelectionMoved": updateCells,
"animationManager.isInitial": true,
"undoManager.isEnabled": true,
"initialContentAlignment": go.Spot.Center,
model: $(go.GraphLinksModel,
{
linkToPortIdProperty: 'toPort',
linkFromPortIdProperty: 'fromPort',
linkKeyProperty: 'key' // IMPORTANT! must be defined for merges and data sync when using GraphLinksModel
}
),
});
function updateCells(e) {
var lay = e.diagram.layout;
e.subject.each(function(node) {
if (node instanceof go.Node) {
var c = lay.findColumnForDocumentX(node.location.x);
node.column = Math.min(Math.max(1, c), lay.columnCount-1); // not into first column
var r = lay.findRowForDocumentY(node.location.y);
node.row = Math.min(Math.max(1, r), lay.rowCount-1); // not into first row
}
});
lay.invalidateLayout();
}
I want to move nodes freely in their cells OR move it outside the table but NOT move it to another cell?
How can I do that?
Thank you all :)
BR,
Amit.