@adriana wrote:
I am trying to create a diagram from which to drag&drop into swimlanes nodes and groups.
I have used the samples from Vertical Swimlanes and Plantogram.Using /go-1.8.14-debug.js
// These parameters need to be set before defining the templates. var MINLENGTH = 200; // this controls the minimum length of any swimlane var MINBREADTH = 20; // this controls the minimum breadth of any non-collapsed swimlane // some shared functions // this may be called to force the lanes to be laid out again function relayoutLanes() { myDiagram.nodes.each(function(lane) { if (!(lane instanceof go.Group)) return; if (lane.category === "Pool") return; lane.layout.isValidLayout = false; // force it to be invalid }); myDiagram.layoutDiagram(); } // 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.invalidateLayout(); }); myDiagram.layoutDiagram(); } // compute the minimum size of a Pool Group needed to hold all of the Lane Groups function computeMinPoolSize(pool) { // assert(pool instanceof go.Group && pool.category === "Pool"); var len = MINLENGTH; pool.memberParts.each(function(lane) { // pools ought to only contain lanes, not plain Nodes if (!(lane instanceof go.Group)) return; var holder = lane.placeholder; if (holder !== null) { var sz = holder.actualBounds; len = Math.max(len, sz.height); } }); return new go.Size(NaN, len); } // compute the minimum size for a particular Lane Group function computeLaneSize(lane) { // assert(lane instanceof go.Group && lane.category !== "Pool"); var sz = computeMinLaneSize(lane); if (lane.isSubGraphExpanded) { var holder = lane.placeholder; if (holder !== null) { var hsz = holder.actualBounds; sz.width = Math.max(sz.width, hsz.width); } } // minimum breadth needs to be big enough to hold the header var hdr = lane.findObject("HEADER"); if (hdr !== null) sz.width = Math.max(sz.width, hdr.actualBounds.width); return sz; } // determine the minimum size of a Lane Group, even if collapsed function computeMinLaneSize(lane) { if (!lane.isSubGraphExpanded) return new go.Size(1, MINLENGTH); return new go.Size(MINBREADTH, MINLENGTH); } // define a custom ResizingTool to limit how far one can shrink a lane Group function LaneResizingTool() { go.ResizingTool.call(this); } go.Diagram.inherit(LaneResizingTool, go.ResizingTool); LaneResizingTool.prototype.isLengthening = function() { return (this.handle.alignment === go.Spot.Bottom); }; LaneResizingTool.prototype.computeMinPoolSize = function() { var lane = this.adornedObject.part; // assert(lane instanceof go.Group && lane.category !== "Pool"); var msz = computeMinLaneSize(lane); // get the absolute minimum size if (this.isLengthening()) { // compute the minimum length of all lanes var sz = computeMinPoolSize(lane.containingGroup); msz.height = Math.max(msz.height, sz.height); } else { // find the minimum size of this single lane var sz = computeLaneSize(lane); msz.width = Math.max(msz.width, sz.width); msz.height = Math.max(msz.height, sz.height); } return msz; }; LaneResizingTool.prototype.resize = function(newr) { var lane = this.adornedObject.part; if (this.isLengthening()) { // changing the length of all of the lanes lane.containingGroup.memberParts.each(function(lane) { if (!(lane instanceof go.Group)) return; var shape = lane.resizeObject; if (shape !== null) { // set its desiredSize length, but leave each breadth alone shape.height = newr.height; } }); } else { // changing the breadth of a single lane go.ResizingTool.prototype.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 function PoolLayout() { go.GridLayout.call(this); this.cellSize = new go.Size(1, 1); this.wrappingColumn = Infinity; this.wrappingWidth = Infinity; this.isRealtime = false; // don't continuously layout while dragging this.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. this.comparer = function(a, b) { var ax = a.location.x; var bx = b.location.x; if (isNaN(ax) || isNaN(bx)) return 0; if (ax < bx) return -1; if (ax > bx) return 1; return 0; }; } go.Diagram.inherit(PoolLayout, go.GridLayout); PoolLayout.prototype.doLayout = function(coll) { var diagram = this.diagram; if (diagram === null) return; diagram.startTransaction("PoolLayout"); var pool = this.group; if (pool !== null && pool.category === "Pool") { // make sure all of the Group Shapes are big enough var minsize = computeMinPoolSize(pool); pool.memberParts.each(function(lane) { if (!(lane instanceof go.Group)) return; if (lane.category !== "Pool") { var shape = lane.resizeObject; if (shape !== null) { // change the desiredSize to be big enough in both directions var sz = computeLaneSize(lane); shape.width = (!isNaN(shape.width)) ? Math.max(shape.width, sz.width) : sz.width; shape.height = (isNaN(shape.height) ? minsize.height : Math.max(shape.height, minsize.height)); var 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 go.GridLayout.prototype.doLayout.call(this, coll); diagram.commitTransaction("PoolLayout"); }; // end PoolLayout class function init() { if (window.goSamples) goSamples(); // init for these samples -- you don't need to call this var $$ = go.GraphObject.make; myDiagram = $$(go.Diagram, "myDiagramDiv", { grid: $$(go.Panel, "Grid", $$(go.Shape, "LineH", { stroke: "lightgray", strokeWidth: 0.5 }), $$(go.Shape, "LineH", { stroke: "gray", strokeWidth: 0.5, interval: 10 }), $$(go.Shape, "LineV", { stroke: "lightgray", strokeWidth: 0.5 }), $$(go.Shape, "LineV", { stroke: "gray", strokeWidth: 0.5, interval: 10 }) ), //// start everything in the middle of the viewport //initialContentAlignment: go.Spot.Center, // use a custom ResizingTool (along with a custom ResizeAdornment on each Group) resizingTool: new LaneResizingTool(), // use a simple layout that ignores links to stack the top-level Pool Groups next to each other layout: $$(PoolLayout), // don't allow dropping onto the diagram's background unless they are all Groups (lanes or pools) mouseDragOver: function(e) { if (!e.diagram.selection.all(function(n) { return n instanceof go.Group; })) { e.diagram.currentCursor = 'not-allowed'; } }, mouseDrop: function(e) { if (!e.diagram.selection.all(function(n) { return n instanceof go.Group; })) { e.diagram.currentTool.doCancel(); } }, allowDrop: true, // must be true to accept drops from the Palette "draggingTool.dragsLink": true, // a clipboard copied node is pasted into the original node's group (i.e. lane). "commandHandler.copiesGroupKey": true, // automatically re-layout the swim lanes after dragging the selection "SelectionMoved": relayoutDiagram, // this DiagramEvent listener is "SelectionCopied": relayoutDiagram, // defined above "animationManager.isEnabled": false, // enable undo & redo "undoManager.isEnabled": true, "draggingTool.isGridSnapEnabled": true, }); myDiagram._palettes = []; // this is a Part.dragComputation function for limiting where a Node may be dragged function stayInGroup(part, pt, gridpt) { // don't constrain top-level nodes var grp = part.containingGroup; if (grp === null) return pt; // try to stay within the background Shape of the Group var back = grp.resizeObject; if (back === null) return pt; // allow dragging a Node out of a Group if the Shift key is down if (part.diagram.lastInput.shift) return pt; var p1 = back.getDocumentPoint(go.Spot.TopLeft); var p2 = back.getDocumentPoint(go.Spot.BottomRight); var b = part.actualBounds; var loc = part.location; // find the padding inside the group's placeholder that is around the member parts var m = grp.placeholder.padding; // now limit the location appropriately var x = Math.max(p1.x + m.left, Math.min(pt.x, p2.x - m.right - b.width - 1)) + (loc.x-b.x); var y = Math.max(p1.y + m.top, Math.min(pt.y, p2.y - m.bottom - b.height - 1)) + (loc.y-b.y); return new go.Point(x, y); } myDiagram.nodeTemplate = $$(go.Node, "Auto", new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify), $$(go.Shape, "Rectangle", { fill: "white", portId: "", cursor: "pointer", fromLinkable: true, toLinkable: true }), $$(go.TextBlock, { margin: 5 }, new go.Binding("text", "key")), { dragComputation: stayInGroup } // limit dragging of Nodes to stay within the containing Group, defined above ); function groupStyle() { // common settings for both Lane and Pool Groups return [ { layerName: "Background", // all pools and lanes are always behind all nodes and links background: "transparent", // can grab anywhere in bounds movable: true, // allows users to re-order by dragging copyable: false, // can't copy lanes or pools avoidable: false, // don't impede AvoidsNodes routed Links minLocation: new go.Point(-Infinity, NaN), // only allow horizontal movement maxLocation: new go.Point(Infinity, NaN) }, new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify) ]; } // hide links between lanes when either lane is collapsed function updateCrossLaneLinks(group) { group.findExternalLinksConnected().each(function(l) { l.visible = (l.fromNode.isVisible() && l.toNode.isVisible()); }); } // each Group is a "swimlane" with a header on the left and a resizable lane on the right myDiagram.groupTemplate = $$(go.Group, "Vertical", groupStyle(), { selectionObjectName: "SHAPE", // selecting a lane causes the body of the lane to be highlit, not the label resizable: true, resizeObjectName: "SHAPE", // the custom resizeAdornmentTemplate only permits two kinds of resizing layout: $$(go.LayeredDigraphLayout, // automatically lay out the lane's subgraph { isInitial: false, // don't even do initial layout isOngoing: false, // don't invalidate layout when nodes or links are added or removed direction: 90, columnSpacing: 10, layeringOption: go.LayeredDigraphLayout.LayerLongestPathSource }), computesBoundsAfterDrag: true, // needed to prevent recomputing Group.placeholder bounds too soon computesBoundsIncludingLinks: false, // to reduce occurrences of links going briefly outside the lane computesBoundsIncludingLocation: true, // to support empty space at top-left corner of lane handlesDragDropForMembers: true, // don't need to define handlers on member Nodes and Links mouseDrop: function(e, grp) { // dropping a copy of some Nodes and Links onto this Group adds them to this Group if (!e.shift) return; // cannot change groups with an unmodified drag-and-drop // don't allow drag-and-dropping a mix of regular Nodes and Groups if (!e.diagram.selection.any(function(n) { return n instanceof go.Group; })) { var ok = grp.addMembers(grp.diagram.selection, true); if (ok) { updateCrossLaneLinks(grp); } else { grp.diagram.currentTool.doCancel(); } } else { e.diagram.currentTool.doCancel(); } }, subGraphExpandedChanged: function(grp) { var shp = grp.resizeObject; if (grp.diagram.undoManager.isUndoingRedoing) return; if (grp.isSubGraphExpanded) { shp.width = grp._savedBreadth; } else { grp._savedBreadth = shp.width; shp.width = NaN; } updateCrossLaneLinks(grp); } }, new go.Binding("isSubGraphExpanded", "expanded").makeTwoWay(), //new go.Binding("position", "pos", go.Point.parse).makeTwoWay(go.Point.stringify), //{ // what to do when a drag-over or a drag-drop occurs on a Group // mouseDragEnter: function(e, grp, prev) { highlightGroup(grp, true); }, // mouseDragLeave: function(e, grp, next) { highlightGroup(grp, false); }, // mouseDrop: function(e, grp) { // var ok = grp.addMembers(grp.diagram.selection, true); // if (!ok) grp.diagram.currentTool.doCancel(); // } //}, // the lane header consisting of a Shape and a TextBlock $$(go.Panel, "Horizontal", { name: "HEADER", angle: 0, // maybe rotate the header to read sideways going up alignment: go.Spot.Center }, $$(go.Panel, "Horizontal", // this is hidden when the swimlane is collapsed new go.Binding("visible", "isSubGraphExpanded").ofObject(), $$(go.Shape, "Diamond", { width: 8, height: 8, fill: "white" }, new go.Binding("fill", "color")), $$(go.TextBlock, // the lane label { font: "bold 13pt sans-serif", editable: true, margin: new go.Margin(2, 0, 0, 0) }, new go.Binding("text", "text").makeTwoWay()) ), $$("SubGraphExpanderButton", { margin: 5 }) // but this remains always visible! ), // end Horizontal Panel $$(go.Panel, "Auto", // the lane consisting of a background Shape and a Placeholder representing the subgraph $$(go.Shape, "Rectangle", // this is the resized object { name: "SHAPE", fill: "white" }, new go.Binding("fill", "color"), new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify)), $$(go.Placeholder, { padding: 12, alignment: go.Spot.TopLeft }), $$(go.TextBlock, // this TextBlock is only seen when the swimlane is collapsed { name: "LABEL", font: "bold 13pt sans-serif", editable: true, angle: 90, alignment: go.Spot.TopLeft, margin: new go.Margin(4, 0, 0, 2) }, new go.Binding("visible", "isSubGraphExpanded", function(e) { return !e; }).ofObject(), new go.Binding("text", "text").makeTwoWay()) ) // end Auto Panel ); // end Group // define a custom resize adornment that has two resize handles if the group is expanded myDiagram.groupTemplate.resizeAdornmentTemplate = $$(go.Adornment, "Spot", $$(go.Placeholder), $$(go.Shape, // for changing the length of a lane { alignment: go.Spot.Bottom, desiredSize: new go.Size(50, 7), fill: "lightblue", stroke: "dodgerblue", cursor: "row-resize" }, new go.Binding("visible", "", function(ad) { if (ad.adornedPart === null) return false; return ad.adornedPart.isSubGraphExpanded; }).ofObject()), $$(go.Shape, // for changing the breadth of a lane { alignment: go.Spot.Right, desiredSize: new go.Size(7, 50), fill: "lightblue", stroke: "dodgerblue", cursor: "col-resize" }, new go.Binding("visible", "", function(ad) { if (ad.adornedPart === null) return false; return ad.adornedPart.isSubGraphExpanded; }).ofObject()) ); myDiagram.groupTemplateMap.add("Pool", $$(go.Group, "Auto", groupStyle(), { // use a simple layout that ignores links to stack the "lane" Groups next to each other layout: $$(PoolLayout, { spacing: new go.Size(0, 0) }) // no space between lanes }, $$(go.Shape, { fill: "white" }, new go.Binding("fill", "color")), $$(go.Panel, "Table", { defaultRowSeparatorStroke: "black" }, $$(go.Panel, "Horizontal", { row: 0, angle: 0 }, $$(go.TextBlock, { font: "bold 16pt sans-serif", editable: true, margin: new go.Margin(2, 0, 0, 0) }, new go.Binding("text").makeTwoWay()) ), $$(go.Placeholder, { row: 1 }) ) )); myDiagram.linkTemplate = $$(go.Link, { routing: go.Link.AvoidsNodes, corner: 5 }, { relinkableFrom: true, relinkableTo: true }, $$(go.Shape), $$(go.Shape, { toArrow: "Standard" }) ); function highlightGroup(grp, show) { if (!grp) return; if (show) { // check that the drop may really happen into the Group var tool = grp.diagram.toolManager.draggingTool; var map = tool.draggedParts || tool.copiedParts; // this is a Map if (grp.canAddMembers(map.toKeySet())) { grp.isHighlighted = true; return; } } grp.isHighlighted = false; } // decide what kinds of Parts can be added to a Group myDiagram.commandHandler.memberValidation = function(grp, node) { if (grp instanceof go.Group && node instanceof go.Group) return false; // cannot add Groups to Groups // but dropping a Group onto the background is always OK return true; }; myDiagram.groupTemplateMap.add("MyGroup", $$(go.Group, "Auto", { background: "transparent", ungroupable: true, // highlight when dragging into the Group mouseDragEnter: function (e, grp, prev) { GroupFunctions.highlightGroup(e, grp, true); }, mouseDragLeave: function (e, grp, next) { GroupFunctions.highlightGroup(e, grp, false); }, computesBoundsAfterDrag: true, // when the selection is dropped into a Group, add the selected Parts into that Group; // if it fails, cancel the tool, rolling back any changes mouseDrop: GroupFunctions.finishDrop, handlesDragDropForMembers: true, // don't need to define handlers on member Nodes and Links // Groups containing Nodes lay out their members vertically layout: $$(go.GridLayout, { wrappingColumn: 1, alignment: go.GridLayout.Position, cellSize: new go.Size(1, 1), spacing: new go.Size(4, 4) }) }, new go.Binding("background", "isHighlighted", function (h) { return h ? "rgba(255,0,0,0.2)" : "transparent"; }).ofObject(), $$(go.Shape, "Rectangle", { fill: null, stroke: "#33D3E5", strokeWidth: 2 }), $$(go.Panel, "Vertical", // title above Placeholder $$(go.Panel, "Horizontal", // button next to TextBlock { stretch: go.GraphObject.Horizontal, background: "#33D3E5" }, $$("SubGraphExpanderButton", { alignment: go.Spot.Right, margin: 5 }), $$(go.TextBlock, { alignment: go.Spot.Left, editable: true, margin: 5, font: "bold 16px sans-serif", opacity: 0.75, stroke: "#404040" }, new go.Binding("text", "text").makeTwoWay()) ), // end Horizontal Panel $$(go.Placeholder, { padding: 5, alignment: go.Spot.TopLeft }) ) // end Vertical Panel ); // end Group and call to add to template Map); myDiagram.nodeTemplateMap.add("My node", return $$(go.Node, "Auto", { mouseDragEnter: function (e, node) { e.handled = true; node.findObject("SHAPE").fill = "red"; highlightGroup(node.containingGroup, false); }, mouseDragLeave: function (e, node) { node.updateTargetBindings(); }, mouseDrop: function (e, node) { // disallow dropping anything onto an "item" node.diagram.currentTool.doCancel(); } }, $$(go.Shape, "RoundedRectangle", new go.Binding("fill")), $$(go.Panel, "Table", { defaultAlignment: go.Spot.Left }, $$(go.TextBlock, { row: 0, column: 0, columnSpan: 2, font: "bold 12pt sans-serif" }, new go.Binding("text", "name")), $$(go.TextBlock, { row: 1, column: 0 }, new go.Binding("text", "operator").makeTwoWay()), $$(go.TextBlock, { row: 1, column: 1 }, " "), $$(go.TextBlock, { row: 1, column: 2 }, new go.Binding("text", "fontSize").makeTwoWay())), { dragComputation: SwimlaneGroupTemplate.stayInGroup }); // limit dragging of Nodes to stay within the containing Group, defined above); relayoutLanes(); initializePalettes(); $$(function () { var inspector = new Inspector('myInfoDiv', myDiagram, { properties: { // key would be automatically added for nodes, but we want to declare it read-only also: "key": { readOnly: true, show: Inspector.showIfPresent }, "name": { readOnly: true, show: Inspector.showIfPresent }, "category": { show: false }, // fill and stroke would be automatically added for nodes, but we want to declare it a color also: "fill": { show: false, type: 'color' }, "stroke": { show: false, type: 'color' } } }); }); } // end init function initializePalettes() { myDiagram.model = new go.GraphLinksModel([ { "key": "Pool", "text": "Pool", "isGroup": true, "category": "Pool", "loc": "0.5 26.59846649169922" }, { "key": "Lane1", "text": "Lane 1", "isGroup": true, "group": "Pool", "color": "lightblue", "loc": "0.5 50.12131399000715", "size": "202 527" }, { "key": "Lane2", "text": "Lane 2", "isGroup": true, "group": "Pool", "color": "lightgreen", "loc": "203.5 50.12131399000715", "size": "213.90087890625 527" }, { "key": "Lane3", "text": "Lane 3", "isGroup": true, "group": "Pool", "color": "lightyellow", "size": "253 527", "loc": "418.5 50.12131399000715" }, { "key": "Lane4", "text": "Lane 4", "isGroup": true, "group": "Pool", "color": "orange", "loc": "672.5 50.12131399000715", "size": "266 527" } ]); paletteDiv = new DiagramPalette("paletteDiv", myDiagram, [ { key: 1, text: "My Group", isGroup: true, category: "MyGroup", group:"Lane1" }, { category: "My Node", name: "My Node", fill: "#ACE600", group:'Lane1' }] }
In this case, everything I drag from the palette has
group:undefined
. If I manually change the group into "Lane1", it can be dropped into the first lane.If I uncomment
new go.Binding("position", "pos", go.Point.parse).makeTwoWay(go.Point.stringify),
{ // what to do when a drag-over or a drag-drop occurs on a Group
mouseDragEnter: function(e, grp, prev) { highlightGroup(grp, true); },
mouseDragLeave: function(e, grp, next) { highlightGroup(grp, false); },
mouseDrop: function(e, grp) {
var ok = grp.addMembers(grp.diagram.selection, true);
if (!ok) grp.diagram.currentTool.doCancel();
}
},I can drop only nodes and they take the group of the lane in which they are dropped, so no restriction to drop in the lane that they are assigned to. Also groups cannot be dragged into the swimlanes. Ideas? Thank you!!!
Posts: 1
Participants: 1