forked from NorthwoodsSoftware/GoJS
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkanban.html
More file actions
297 lines (281 loc) · 14.4 KB
/
Copy pathkanban.html
File metadata and controls
297 lines (281 loc) · 14.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
<!DOCTYPE html>
<html>
<head>
<title>Kanban Board</title>
<meta name="description" content="An interactive Kanban board editor, a visual control used for organizing work items." />
<!-- Copyright 1998-2016 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="go.js"></script>
<link href="../assets/css/goSamples.css" rel="stylesheet" type="text/css" /> <!-- you don't need to use this -->
<script src="goSamples.js"></script> <!-- this is only for the GoJS Samples framework -->
<script id="code">
// These parameters need to be set before defining the templates.
var MINLENGTH = 200; // this controls the minimum length of any swimlane
var MINBREADTH = 100; // this controls the minimum breadth of any non-collapsed swimlane
// some shared functions
// this is called after nodes have been moved
function relayoutDiagram() {
myDiagram.selection.each(function(n) { n.invalidateLayout(); });
myDiagram.layoutDiagram();
}
// compute the minimum size of the whole diagram needed to hold all of the Lane Groups
function computeMinPoolSize() {
var len = MINLENGTH;
myDiagram.findTopLevelGroups().each(function(lane) {
var holder = lane.placeholder;
if (holder !== null) {
var sz = holder.actualBounds;
len = Math.max(len, sz.height);
}
var box = lane.selectionObject;
len = Math.max(len, box.actualBounds.height);
});
return new go.Size(NaN, len);
}
// compute the minimum size for a particular Lane Group
function computeLaneSize(lane) {
// assert(lane instanceof go.Group);
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 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.spacing = new go.Size(0, 0);
this.alignment = go.GridLayout.Position;
}
go.Diagram.inherit(PoolLayout, go.GridLayout);
/** @override */
PoolLayout.prototype.doLayout = function(coll) {
var diagram = this.diagram;
if (diagram === null) return;
diagram.startTransaction("PoolLayout");
// make sure all of the Group Shapes are big enough
var minsize = computeMinPoolSize();
diagram.findTopLevelGroups().each(function(lane) {
if (!(lane instanceof go.Group)) return;
var shape = lane.selectionObject;
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",
{
// start everything in the middle of the viewport
initialContentAlignment: go.Spot.Center,
// use a simple layout to stack the top-level Groups next to each other
layout: $(PoolLayout),
allowDrop: true, // support drag-and-drop from the Palette
// disallow nodes to be dragged to the diagram's background
mouseDrop: function(e) {
e.diagram.currentTool.doCancel();
},
// 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
});
myDiagram.nodeTemplate =
$(go.Node, "Auto",
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
$(go.Shape, "Rectangle",
{ fill: "white", strokeWidth: 0 },
new go.Binding("fill", "color")),
$(go.TextBlock,
{ margin: 5, editable: true, maxSize: new go.Size(130, NaN) },
new go.Binding("text", "text").makeTwoWay())
);
myDiagram.groupTemplate =
$(go.Group, "Vertical",
{
selectionObjectName: "SHAPE", // selecting a lane causes the body of the lane to be highlit, not the label
layerName: "Background", // all lanes are always behind all nodes and links
background: "transparent", // can grab anywhere in bounds
movable: false, // can't move lanes
copyable: false, // can't copy lanes
deletable: false, // can't delete lanes
layout: $(go.GridLayout, // automatically lay out the lane's subgraph
{
wrappingColumn: 1,
cellSize: new go.Size(1, 1),
spacing: new go.Size(5, 5),
alignment: go.GridLayout.Position,
comparer: function(a, b) { // can re-order tasks within a lane
var ay = a.location.y;
var by = b.location.y;
if (isNaN(ay) || isNaN(by)) return 0;
if (ay < by) return -1;
if (ay > by) return 1;
return 0;
}
}),
computesBoundsAfterDrag: true, // needed to prevent recomputing Group.placeholder bounds too soon
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
// don't allow drag-and-dropping a mix of regular Nodes and Groups
if (e.diagram.selection.all(function(n) { return !(n instanceof go.Group); })) {
var ok = grp.addMembers(grp.diagram.selection, true);
if (!ok) grp.diagram.currentTool.doCancel();
}
},
subGraphExpandedChanged: function(grp) {
var shp = grp.selectionObject;
if (grp.diagram.undoManager.isUndoingRedoing) return;
if (grp.isSubGraphExpanded) {
shp.width = grp._savedBreadth;
} else {
grp._savedBreadth = shp.width;
shp.width = NaN;
}
}
},
new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
new go.Binding("isSubGraphExpanded", "expanded").makeTwoWay(),
// the lane header consisting of a TextBlock and an expander button
$(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.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", stroke: "gray" },
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
load();
myPalette =
$(go.Palette, "myPaletteDiv",
{
nodeTemplateMap: myDiagram.nodeTemplateMap,
groupTemplateMap: myDiagram.groupTemplateMap,
"model.nodeDataArray": [
{ text: "note\nwith editable text", color: "white" },
{ text: "note\nwith editable text", color: "lightgray" },
{ text: "note\nwith editable text", color: "lightblue" },
{ text: "note\nwith editable text", color: "lightgreen" },
{ text: "note\nwith editable text", color: "lightyellow" },
{ text: "note\nwith editable text", color: "orange" },
{ text: "note\nwith editable text", color: "pink" }
]
}
);
} // end init
// Show the diagram's model in JSON format
function save() {
document.getElementById("mySavedModel").value = myDiagram.model.toJson();
myDiagram.isModified = false;
}
function load() {
myDiagram.model = go.Model.fromJson(document.getElementById("mySavedModel").value);
myDiagram.delayInitialization(relayoutDiagram);
}
</script>
</head>
<body onload="init()">
<div id="sample">
<div id="myPaletteDiv" style="border: solid 1px gray; width:100%; height:100px"></div>
<div id="myDiagramDiv" style="border: solid 1px blue; width:100%; height:400px; background:darkgray"></div>
<p>
This design and implementation were adapted from the <a href="swimLanesVertical.html">Swim Lanes (vertical)</a> sample.
Unlike that sample:
<ul>
<li>there are no Links</li>
<li>lanes cannot be nested into "pools"</li>
<li>lanes cannot be resized</li>
<li>the user can drag-and-drop tasks from the <b>Palette</b> at the top</li>
<li>the user cannot drop tasks into the diagram's background</li>
<li>all tasks are ordered within a single column; the user can rearrange the order</li>
<li>tasks can freely be moved into other lanes</li>
<li>lanes are not movable or deletable</li>
</ul>
</p>
<button id="SaveButton" onclick="save()">Save</button>
<button onclick="load()">Load</button>
Diagram Model saved in JSON format:
<br />
<textarea id="mySavedModel" style="width:100%;height:300px">
{ "class": "go.GraphLinksModel",
"nodeDataArray": [
{"key":"Problems", "text":"Problems", "isGroup":true, "color":"white", "loc":"0 23.52284749830794", "size":"108 200"},
{"key":"Reproduced", "text":"Reproduced", "isGroup":true, "color":"lightblue", "loc":"109 23.52284749830794", "size":"124.60618337477278 200"},
{"key":"Identified", "text":"Identified", "isGroup":true, "color":"lightgreen", "loc":"235 23.52284749830794", "size":"107 200"},
{"key":"Fixing", "text":"Fixing", "isGroup":true, "color":"lightyellow", "loc":"343 23.52284749830794", "size":"107 200"},
{"key":"Reviewing", "text":"Reviewing", "isGroup":true, "color":"orange", "loc":"451 23.52284749830794", "size":"109.22284444655013 200"},
{"key":"Testing", "text":"Testing", "isGroup":true, "color":"pink", "loc":"562 23.52284749830794", "size":"108 200"},
{"key":"Customer", "text":"Customer", "isGroup":true, "color":"white", "loc":"671 23.52284749830794", "size":"108 200"},
{"key":1, "text":"text for oneA", "group":"Problems", "color":"lightgreen", "loc":"12 35.52284749830794"},
{"key":2, "text":"text for oneB", "group":"Problems", "color":"lightblue", "loc":"12 65.52284749830794"},
{"key":3, "text":"text for oneC", "group":"Problems", "color":"orange", "loc":"12 95.52284749830794"},
{"key":4, "text":"text for oneD", "group":"Problems", "color":"lightyellow", "loc":"12 125.52284749830794"},
{"key":5, "text":"text for twoA", "group":"Reproduced", "color":"pink", "loc":"121 35.52284749830794"},
{"key":6, "text":"text for twoB", "group":"Reproduced", "color":"lightblue", "loc":"121 65.52284749830794"},
{"key":7, "text":"text for twoC", "group":"Identified", "color":"orange", "loc":"247 35.52284749830794"},
{"key":8, "text":"text for twoD", "group":"Fixing", "color":"pink", "loc":"355 35.52284749830794"},
{"key":9, "text":"text for twoE", "group":"Reviewing", "color":"lightgreen", "loc":"463 35.52284749830794"},
{"key":10, "text":"text for twoF", "group":"Reviewing", "color":"orange", "loc":"463 65.52284749830794"},
{"key":11, "text":"text for twoG", "group":"Testing", "color":"lightyellow", "loc":"574 35.52284749830794"},
{"key":12, "text":"text for fourA", "group":"Customer", "color":"lightyellow", "loc":"683 35.52284749830794"},
{"key":13, "text":"text for fourB", "group":"Customer", "color":"pink", "loc":"683 65.52284749830794"},
{"key":14, "text":"text for fourC", "group":"Customer", "color":"orange", "loc":"683 95.52284749830794"},
{"key":15, "text":"text for fourD", "group":"Customer", "color":"lightblue", "loc":"683 125.52284749830794"},
{"key":16, "text":"text for fiveA", "group":"Customer", "color":"lightgreen", "loc":"683 155.52284749830795"}
],
"linkDataArray": []}
</textarea>
</div>
</body>
</html>