nope/resources/ui/gojs/nodes/lanes.ts
Martin Karkowski 838d910c2f adding gojs
2020-12-01 13:05:35 +01:00

167 lines
6.2 KiB
TypeScript

import go from "gojs";
const $ = go.GraphObject.make;
// swimlanes
const MINLENGTH = 400; // this controls the minimum length of any swimlane
const MINBREADTH = 20; // this controls the minimum breadth of any non-collapsed swimlane
// some shared functions
// this is called after nodes have been moved or lanes resized, to layout all of the Pool Groups again
function relayoutDiagram() {
myDiagram.layout.invalidateLayout();
myDiagram.findTopLevelGroups().each(function (g) {
if (g.category === "Pool" && g.layout !== null) g.layout.invalidateLayout();
});
myDiagram.layoutDiagram();
}
// compute the minimum size of a Pool Group needed to hold all of the Lane Groups
function computeMinPoolSize(pool: go.Group) {
// assert(pool instanceof go.Group && pool.category === "Pool");
let len = MINLENGTH;
pool.memberParts.each(function (lane) {
// pools ought to only contain lanes, not plain Nodes
if (!(lane instanceof go.Group)) return;
const holder = lane.placeholder;
if (holder !== null) {
const sz = holder.actualBounds;
len = Math.max(len, sz.width);
}
});
return new go.Size(len, NaN);
}
// compute the minimum size for a particular Lane Group
function computeLaneSize(lane: go.Group) {
// assert(lane instanceof go.Group && lane.category !== "Pool");
const sz = computeMinLaneSize(lane);
if (lane.isSubGraphExpanded) {
const holder = lane.placeholder;
if (holder !== null) {
const hsz = holder.actualBounds;
sz.height = Math.max(sz.height, hsz.height);
}
}
// minimum breadth needs to be big enough to hold the header
const hdr = lane.findObject("HEADER");
if (hdr !== null) sz.height = Math.max(sz.height, hdr.actualBounds.height);
return sz;
}
// determine the minimum size of a Lane Group, even if collapsed
function computeMinLaneSize(lane: go.Group) {
if (!lane.isSubGraphExpanded) return new go.Size(MINLENGTH, 1);
return new go.Size(MINLENGTH, MINBREADTH);
}
// define a custom ResizingTool to limit how far one can shrink a lane Group
class LaneResizingTool extends go.ResizingTool {
public isLengthening() {
return this.handle !== null && this.handle.alignment === go.Spot.Right;
}
public computeMinSize(): go.Size {
if (this.adornedObject === null) return new go.Size(MINLENGTH, MINBREADTH);
const lane = this.adornedObject.part;
if (!(lane instanceof go.Group))
return go.ResizingTool.prototype.computeMinSize.call(this);
// assert(lane instanceof go.Group && lane.category !== "Pool");
const msz = computeMinLaneSize(lane); // get the absolute minimum size
if (lane.containingGroup !== null && this.isLengthening()) {
// compute the minimum length of all lanes
const sz = computeMinPoolSize(lane.containingGroup);
msz.width = Math.max(msz.width, sz.width);
} else {
// find the minimum size of this single lane
const sz = computeLaneSize(lane);
msz.width = Math.max(msz.width, sz.width);
msz.height = Math.max(msz.height, sz.height);
}
return msz;
}
public resize(newr: go.Rect): void {
if (this.adornedObject === null) return;
const lane = this.adornedObject.part;
if (!(lane instanceof go.Group))
return go.ResizingTool.prototype.resize.call(this, newr);
if (
lane instanceof go.Group &&
lane.containingGroup !== null &&
this.isLengthening()
) {
// changing the length of all of the lanes
lane.containingGroup.memberParts.each((l) => {
if (!(l instanceof go.Group)) return;
const shape = l.resizeObject;
if (shape !== null) {
// set its desiredSize length, but leave each breadth alone
shape.width = newr.width;
}
});
} else {
// changing the breadth of a single lane
super.resize.call(this, newr);
}
relayoutDiagram(); // now that the lane has changed size, layout the pool again
}
}
// end LaneResizingTool class
// define a custom grid layout that makes sure the length of each lane is the same
// and that each lane is broad enough to hold its subgraph
class PoolLayout extends go.GridLayout {
public cellSize = new go.Size(1, 1);
public wrappingColumn = 1;
public wrappingWidth = Infinity;
public isRealtime = false; // don't continuously layout while dragging
public alignment = go.GridLayout.Position;
// This sorts based on the location of each Group.
// This is useful when Groups can be moved up and down in order to change their order.
public comparer = function (a: go.Part, b: go.Part) {
const ay = a.location.y;
const by = b.location.y;
if (isNaN(ay) || isNaN(by)) return 0;
if (ay < by) return -1;
if (ay > by) return 1;
return 0;
};
public doLayout(coll: go.Diagram | go.Group | go.Iterable<go.Part>) {
const diagram = this.diagram;
if (diagram === null) return;
diagram.startTransaction("PoolLayout");
const pool = this.group;
if (pool !== null && pool.category === "Pool") {
// make sure all of the Group Shapes are big enough
const minsize = computeMinPoolSize(pool);
pool.memberParts.each(function (lane) {
if (!(lane instanceof go.Group)) return;
if (lane.category !== "Pool") {
const shape = lane.resizeObject;
if (shape !== null) {
// change the desiredSize to be big enough in both directions
const sz = computeLaneSize(lane);
shape.width = isNaN(shape.width)
? minsize.width
: Math.max(shape.width, minsize.width);
shape.height = !isNaN(shape.height)
? Math.max(shape.height, sz.height)
: sz.height;
const cell = lane.resizeCellSize;
if (!isNaN(shape.width) && !isNaN(cell.width) && cell.width > 0)
shape.width = Math.ceil(shape.width / cell.width) * cell.width;
if (!isNaN(shape.height) && !isNaN(cell.height) && cell.height > 0)
shape.height =
Math.ceil(shape.height / cell.height) * cell.height;
}
}
});
}
// now do all of the usual stuff, according to whatever properties have been set on this GridLayout
super.doLayout.call(this, coll);
diagram.commitTransaction("PoolLayout");
}
}
// end PoolLayout class