UNPKG

24.5 kBJavaScriptView Raw
1/*
2* Copyright (C) 1998-2021 by Northwoods Software Corporation. All Rights Reserved.
3*/
4(function (factory) {
5 if (typeof module === "object" && typeof module.exports === "object") {
6 var v = factory(require, exports);
7 if (v !== undefined) module.exports = v;
8 }
9 else if (typeof define === "function" && define.amd) {
10 define(["require", "exports", "../release/go.js"], factory);
11 }
12})(function (require, exports) {
13 "use strict";
14 Object.defineProperty(exports, "__esModule", { value: true });
15 /*
16 * This is an extension and not part of the main GoJS library.
17 * Note that the API for this class may change with any version, even point releases.
18 * If you intend to use an extension in production, you should copy the code to your own source directory.
19 * Extensions can be found in the GoJS kit under the extensions or extensionsTS folders.
20 * See the Extensions intro page (https://gojs.net/latest/intro/extensions.html) for more information.
21 */
22 var go = require("../release/go.js");
23 // These are the definitions for all of the predefined buttons.
24 // You do not need to load this file in order to use buttons.
25 // A 'Button' is a Panel that has a Shape surrounding some content
26 // and that has mouseEnter/mouseLeave behavior to highlight the button.
27 // The content of the button, whether a TextBlock or a Picture or a complicated Panel,
28 // must be supplied by the caller.
29 // The caller must also provide a click event handler.
30 // Typical usage:
31 // $('Button',
32 // $(go.TextBlock, 'Click me!'), // the content is just the text label
33 // { click: function(e, obj) { alert('I was clicked'); } }
34 // )
35 // Note that a button click event handler is not invoked upon a click if isEnabledObject() returns false.
36 go.GraphObject.defineBuilder('Button', function (args) {
37 // default colors for 'Button' shape
38 var buttonFillNormal = '#F5F5F5';
39 var buttonStrokeNormal = '#BDBDBD';
40 var buttonFillOver = '#E0E0E0';
41 var buttonStrokeOver = '#9E9E9E';
42 var buttonFillPressed = '#BDBDBD'; // set to null for no button pressed effects
43 var buttonStrokePressed = '#9E9E9E';
44 var buttonFillDisabled = '#E5E5E5';
45 // padding inside the ButtonBorder to match sizing from previous versions
46 var paddingHorizontal = 2.76142374915397;
47 var paddingVertical = 2.761423749153969;
48 var button = /** @type {Panel} */ (go.GraphObject.make(go.Panel, 'Auto', {
49 isActionable: true,
50 enabledChanged: function (btn, enabled) {
51 if (btn instanceof go.Panel) {
52 var shape = btn.findObject('ButtonBorder');
53 if (shape !== null) {
54 shape.fill = enabled ? btn['_buttonFillNormal'] : btn['_buttonFillDisabled'];
55 }
56 }
57 },
58 cursor: 'pointer',
59 // save these values for the mouseEnter and mouseLeave event handlers
60 '_buttonFillNormal': buttonFillNormal,
61 '_buttonStrokeNormal': buttonStrokeNormal,
62 '_buttonFillOver': buttonFillOver,
63 '_buttonStrokeOver': buttonStrokeOver,
64 '_buttonFillPressed': buttonFillPressed,
65 '_buttonStrokePressed': buttonStrokePressed,
66 '_buttonFillDisabled': buttonFillDisabled
67 }, go.GraphObject.make(go.Shape, // the border
68 {
69 name: 'ButtonBorder',
70 figure: 'RoundedRectangle',
71 spot1: new go.Spot(0, 0, paddingHorizontal, paddingVertical),
72 spot2: new go.Spot(1, 1, -paddingHorizontal, -paddingVertical),
73 parameter1: 2,
74 parameter2: 2,
75 fill: buttonFillNormal,
76 stroke: buttonStrokeNormal
77 })));
78 // There's no GraphObject inside the button shape -- it must be added as part of the button definition.
79 // This way the object could be a TextBlock or a Shape or a Picture or arbitrarily complex Panel.
80 // mouse-over behavior
81 button.mouseEnter = function (e, btn, prev) {
82 if (!btn.isEnabledObject())
83 return;
84 if (!(btn instanceof go.Panel))
85 return;
86 var shape = btn.findObject('ButtonBorder'); // the border Shape
87 if (shape instanceof go.Shape) {
88 var brush = btn['_buttonFillOver'];
89 btn['_buttonFillNormal'] = shape.fill;
90 shape.fill = brush;
91 brush = btn['_buttonStrokeOver'];
92 btn['_buttonStrokeNormal'] = shape.stroke;
93 shape.stroke = brush;
94 }
95 };
96 button.mouseLeave = function (e, btn, prev) {
97 if (!btn.isEnabledObject())
98 return;
99 if (!(btn instanceof go.Panel))
100 return;
101 var shape = btn.findObject('ButtonBorder'); // the border Shape
102 if (shape instanceof go.Shape) {
103 shape.fill = btn['_buttonFillNormal'];
104 shape.stroke = btn['_buttonStrokeNormal'];
105 }
106 };
107 button.actionDown = function (e, btn) {
108 if (!btn.isEnabledObject())
109 return;
110 if (!(btn instanceof go.Panel))
111 return;
112 if (btn['_buttonFillPressed'] === null)
113 return;
114 if (e.button !== 0)
115 return;
116 var shape = btn.findObject('ButtonBorder'); // the border Shape
117 if (shape instanceof go.Shape) {
118 var diagram = e.diagram;
119 var oldskip = diagram.skipsUndoManager;
120 diagram.skipsUndoManager = true;
121 var brush = btn['_buttonFillPressed'];
122 btn['_buttonFillOver'] = shape.fill;
123 shape.fill = brush;
124 brush = btn['_buttonStrokePressed'];
125 btn['_buttonStrokeOver'] = shape.stroke;
126 shape.stroke = brush;
127 diagram.skipsUndoManager = oldskip;
128 }
129 };
130 button.actionUp = function (e, btn) {
131 if (!btn.isEnabledObject())
132 return;
133 if (!(btn instanceof go.Panel))
134 return;
135 if (btn['_buttonFillPressed'] === null)
136 return;
137 if (e.button !== 0)
138 return;
139 var shape = btn.findObject('ButtonBorder'); // the border Shape
140 if (shape instanceof go.Shape) {
141 var diagram = e.diagram;
142 var oldskip = diagram.skipsUndoManager;
143 diagram.skipsUndoManager = true;
144 if (overButton(e, btn)) {
145 shape.fill = btn['_buttonFillOver'];
146 shape.stroke = btn['_buttonStrokeOver'];
147 }
148 else {
149 shape.fill = btn['_buttonFillNormal'];
150 shape.stroke = btn['_buttonStrokeNormal'];
151 }
152 diagram.skipsUndoManager = oldskip;
153 }
154 };
155 button.actionCancel = function (e, btn) {
156 if (!btn.isEnabledObject())
157 return;
158 if (!(btn instanceof go.Panel))
159 return;
160 if (btn['_buttonFillPressed'] === null)
161 return;
162 var shape = btn.findObject('ButtonBorder'); // the border Shape
163 if (shape instanceof go.Shape) {
164 var diagram = e.diagram;
165 var oldskip = diagram.skipsUndoManager;
166 diagram.skipsUndoManager = true;
167 if (overButton(e, btn)) {
168 shape.fill = btn['_buttonFillOver'];
169 shape.stroke = btn['_buttonStrokeOver'];
170 }
171 else {
172 shape.fill = btn['_buttonFillNormal'];
173 shape.stroke = btn['_buttonStrokeNormal'];
174 }
175 diagram.skipsUndoManager = oldskip;
176 }
177 };
178 button.actionMove = function (e, btn) {
179 if (!btn.isEnabledObject())
180 return;
181 if (!(btn instanceof go.Panel))
182 return;
183 if (btn['_buttonFillPressed'] === null)
184 return;
185 var diagram = e.diagram;
186 if (diagram.firstInput.button !== 0)
187 return;
188 diagram.currentTool.standardMouseOver();
189 if (overButton(e, btn)) {
190 var shape = btn.findObject('ButtonBorder');
191 if (shape instanceof go.Shape) {
192 var oldskip = diagram.skipsUndoManager;
193 diagram.skipsUndoManager = true;
194 var brush = btn['_buttonFillPressed'];
195 if (shape.fill !== brush)
196 shape.fill = brush;
197 brush = btn['_buttonStrokePressed'];
198 if (shape.stroke !== brush)
199 shape.stroke = brush;
200 diagram.skipsUndoManager = oldskip;
201 }
202 }
203 };
204 var overButton = function (e, btn) {
205 var over = e.diagram.findObjectAt(e.documentPoint, function (x) {
206 while (x.panel !== null) {
207 if (x.isActionable)
208 return x;
209 x = x.panel;
210 }
211 return x;
212 }, function (x) { return x === btn; });
213 return over !== null;
214 };
215 return button;
216 });
217 // This is a complete Button that you can have in a Node template
218 // to allow the user to collapse/expand the subtree beginning at that Node.
219 // Typical usage within a Node template:
220 // $('TreeExpanderButton')
221 go.GraphObject.defineBuilder('TreeExpanderButton', function (args) {
222 var button = /** @type {Panel} */ (go.GraphObject.make('Button', {
223 '_treeExpandedFigure': 'MinusLine',
224 '_treeCollapsedFigure': 'PlusLine'
225 }, go.GraphObject.make(go.Shape, // the icon
226 {
227 name: 'ButtonIcon',
228 figure: 'MinusLine',
229 stroke: '#424242',
230 strokeWidth: 2,
231 desiredSize: new go.Size(8, 8)
232 },
233 // bind the Shape.figure to the Node.isTreeExpanded value using this converter:
234 new go.Binding('figure', 'isTreeExpanded', function (exp, shape) {
235 var but = shape.panel;
236 return exp ? but['_treeExpandedFigure'] : but['_treeCollapsedFigure'];
237 }).ofObject()),
238 // assume initially not visible because there are no links coming out
239 { visible: false },
240 // bind the button visibility to whether it's not a leaf node
241 new go.Binding('visible', 'isTreeLeaf', function (leaf) { return !leaf; }).ofObject()));
242 // tree expand/collapse behavior
243 button.click = function (e, btn) {
244 var node = btn.part;
245 if (node instanceof go.Adornment)
246 node = node.adornedPart;
247 if (!(node instanceof go.Node))
248 return;
249 var diagram = node.diagram;
250 if (diagram === null)
251 return;
252 var cmd = diagram.commandHandler;
253 if (node.isTreeExpanded) {
254 if (!cmd.canCollapseTree(node))
255 return;
256 }
257 else {
258 if (!cmd.canExpandTree(node))
259 return;
260 }
261 e.handled = true;
262 if (node.isTreeExpanded) {
263 cmd.collapseTree(node);
264 }
265 else {
266 cmd.expandTree(node);
267 }
268 };
269 return button;
270 });
271 // This is a complete Button that you can have in a Group template
272 // to allow the user to collapse/expand the subgraph that the Group holds.
273 // Typical usage within a Group template:
274 // $('SubGraphExpanderButton')
275 go.GraphObject.defineBuilder('SubGraphExpanderButton', function (args) {
276 var button = /** @type {Panel} */ (go.GraphObject.make('Button', {
277 '_subGraphExpandedFigure': 'MinusLine',
278 '_subGraphCollapsedFigure': 'PlusLine'
279 }, go.GraphObject.make(go.Shape, // the icon
280 {
281 name: 'ButtonIcon',
282 figure: 'MinusLine',
283 stroke: '#424242',
284 strokeWidth: 2,
285 desiredSize: new go.Size(8, 8)
286 },
287 // bind the Shape.figure to the Group.isSubGraphExpanded value using this converter:
288 new go.Binding('figure', 'isSubGraphExpanded', function (exp, shape) {
289 var but = shape.panel;
290 return exp ? but['_subGraphExpandedFigure'] : but['_subGraphCollapsedFigure'];
291 }).ofObject())));
292 // subgraph expand/collapse behavior
293 button.click = function (e, btn) {
294 var group = btn.part;
295 if (group instanceof go.Adornment)
296 group = group.adornedPart;
297 if (!(group instanceof go.Group))
298 return;
299 var diagram = group.diagram;
300 if (diagram === null)
301 return;
302 var cmd = diagram.commandHandler;
303 if (group.isSubGraphExpanded) {
304 if (!cmd.canCollapseSubGraph(group))
305 return;
306 }
307 else {
308 if (!cmd.canExpandSubGraph(group))
309 return;
310 }
311 e.handled = true;
312 if (group.isSubGraphExpanded) {
313 cmd.collapseSubGraph(group);
314 }
315 else {
316 cmd.expandSubGraph(group);
317 }
318 };
319 return button;
320 });
321 // This is just an "Auto" Adornment that can hold some contents within a light gray, shadowed box.
322 // Typical usage:
323 // toolTip:
324 // $("ToolTip",
325 // $(go.TextBlock, . . .)
326 // )
327 go.GraphObject.defineBuilder('ToolTip', function (args) {
328 var ad = go.GraphObject.make(go.Adornment, 'Auto', {
329 isShadowed: true,
330 shadowColor: 'rgba(0, 0, 0, .4)',
331 shadowOffset: new go.Point(0, 3),
332 shadowBlur: 5
333 }, go.GraphObject.make(go.Shape, {
334 name: 'Border',
335 figure: 'RoundedRectangle',
336 parameter1: 1,
337 parameter2: 1,
338 fill: '#F5F5F5',
339 stroke: '#F0F0F0',
340 spot1: new go.Spot(0, 0, 4, 6),
341 spot2: new go.Spot(1, 1, -4, -4)
342 }));
343 return ad;
344 });
345 // This is just a "Vertical" Adornment that can hold some "ContextMenuButton"s.
346 // Typical usage:
347 // contextMenu:
348 // $("ContextMenu",
349 // $("ContextMenuButton",
350 // $(go.TextBlock, . . .),
351 // { click: . . .}
352 // ),
353 // $("ContextMenuButton", . . .)
354 // )
355 go.GraphObject.defineBuilder('ContextMenu', function (args) {
356 var ad = go.GraphObject.make(go.Adornment, 'Vertical', {
357 background: '#F5F5F5',
358 isShadowed: true,
359 shadowColor: 'rgba(0, 0, 0, .4)',
360 shadowOffset: new go.Point(0, 3),
361 shadowBlur: 5
362 },
363 // don't set the background if the ContextMenu is adorning something and there's a Placeholder
364 new go.Binding('background', '', function (obj) {
365 var part = obj.adornedPart;
366 if (part !== null && obj.placeholder !== null)
367 return null;
368 return '#F5F5F5';
369 }));
370 return ad;
371 });
372 // This just holds the 'ButtonBorder' Shape that acts as the border
373 // around the button contents, which must be supplied by the caller.
374 // The button contents are usually a TextBlock or Panel consisting of a Shape and a TextBlock.
375 // Typical usage within an Adornment that is either a GraphObject.contextMenu or a Diagram.contextMenu:
376 // $('ContextMenuButton',
377 // $(go.TextBlock, text),
378 // { click: function(e, obj) { alert('Command for ' + obj.part.adornedPart); } },
379 // new go.Binding('visible', '', function(data) { return ...OK to perform Command...; })
380 // )
381 go.GraphObject.defineBuilder('ContextMenuButton', function (args) {
382 var button = /** @type {Panel} */ (go.GraphObject.make('Button'));
383 button.stretch = go.GraphObject.Horizontal;
384 var border = button.findObject('ButtonBorder');
385 if (border instanceof go.Shape) {
386 border.figure = 'Rectangle';
387 border.spot1 = new go.Spot(0, 0, 2, 3);
388 border.spot2 = new go.Spot(1, 1, -2, -2);
389 }
390 return button;
391 });
392 // This button is used to toggle the visibility of a GraphObject named
393 // by the second argument to GraphObject.make. If the second argument is not present
394 // or if it is not a string, this assumes that the element name is 'COLLAPSIBLE'.
395 // You can only control the visibility of one element in a Part at a time,
396 // although that element might be an arbitrarily complex Panel.
397 // Typical usage:
398 // $(go.Panel, . . .,
399 // $('PanelExpanderButton', 'COLLAPSIBLE'),
400 // . . .,
401 // $(go.Panel, . . .,
402 // { name: 'COLLAPSIBLE' },
403 // . . . stuff to be hidden or shown as the PanelExpanderButton is clicked . . .
404 // ),
405 // . . .
406 // )
407 go.GraphObject.defineBuilder('PanelExpanderButton', function (args) {
408 var eltname = (go.GraphObject.takeBuilderArgument(args, 'COLLAPSIBLE'));
409 var button = (go.GraphObject.make('Button', {
410 '_buttonExpandedFigure': 'M0 0 M0 6 L4 2 8 6 M8 8',
411 '_buttonCollapsedFigure': 'M0 0 M0 2 L4 6 8 2 M8 8',
412 '_buttonFillNormal': 'rgba(0, 0, 0, 0)',
413 '_buttonStrokeNormal': null,
414 '_buttonFillOver': 'rgba(0, 0, 0, .2)',
415 '_buttonStrokeOver': null,
416 '_buttonFillPressed': 'rgba(0, 0, 0, .4)',
417 '_buttonStrokePressed': null
418 }, go.GraphObject.make(go.Shape, { name: 'ButtonIcon', strokeWidth: 2 }, new go.Binding('geometryString', 'visible', function (vis) { return vis ? button['_buttonExpandedFigure'] : button['_buttonCollapsedFigure']; }).ofObject(eltname))));
419 var border = button.findObject('ButtonBorder');
420 if (border instanceof go.Shape) {
421 border.stroke = null;
422 border.fill = 'rgba(0, 0, 0, 0)';
423 }
424 button.click = function (e, btn) {
425 if (!(btn instanceof go.Panel))
426 return;
427 var diagram = btn.diagram;
428 if (diagram === null)
429 return;
430 if (diagram.isReadOnly)
431 return;
432 var elt = btn.findTemplateBinder();
433 if (elt === null)
434 elt = btn.part;
435 if (elt !== null) {
436 var pan = elt.findObject(eltname);
437 if (pan !== null) {
438 e.handled = true;
439 diagram.startTransaction('Collapse/Expand Panel');
440 pan.visible = !pan.visible;
441 diagram.commitTransaction('Collapse/Expand Panel');
442 }
443 }
444 };
445 return button;
446 });
447 // Define a common checkbox button; the first argument is the name of the data property
448 // to which the state of this checkbox is data bound. If the first argument is not a string,
449 // it raises an error. If no data binding of the checked state is desired,
450 // pass an empty string as the first argument.
451 // Examples:
452 // $('CheckBoxButton', 'dataPropertyName', ...)
453 // or:
454 // $('CheckBoxButton', '', { '_doClick': function(e, obj) { alert('clicked!'); } })
455 go.GraphObject.defineBuilder('CheckBoxButton', function (args) {
456 // process the one required string argument for this kind of button
457 var propname = /** @type {string} */ (go.GraphObject.takeBuilderArgument(args));
458 var button = /** @type {Panel} */ (go.GraphObject.make('Button', { desiredSize: new go.Size(14, 14) }, go.GraphObject.make(go.Shape, {
459 name: 'ButtonIcon',
460 geometryString: 'M0 0 M0 8.85 L4.9 13.75 16.2 2.45 M16.2 16.2',
461 strokeWidth: 2,
462 stretch: go.GraphObject.Fill,
463 geometryStretch: go.GraphObject.Uniform,
464 visible: false // visible set to false: not checked, unless data.PROPNAME is true
465 },
466 // create a data Binding only if PROPNAME is supplied and not the empty string
467 (propname !== '' ? new go.Binding('visible', propname).makeTwoWay() : []))));
468 button.click = function (e, btn) {
469 var diagram = e.diagram;
470 if (diagram === null || diagram.isReadOnly)
471 return;
472 if (propname !== '' && diagram.model.isReadOnly)
473 return;
474 e.handled = true;
475 var shape = btn.findObject('ButtonIcon');
476 diagram.startTransaction('checkbox');
477 if (shape !== null)
478 shape.visible = !shape.visible; // this toggles data.checked due to TwoWay Binding
479 // support extra side-effects without clobbering the click event handler:
480 if (typeof btn['_doClick'] === 'function')
481 btn['_doClick'](e, btn);
482 diagram.commitTransaction('checkbox');
483 };
484 return button;
485 });
486 // This defines a whole check-box -- including both a 'CheckBoxButton' and whatever you want as the check box label.
487 // Note that mouseEnter/mouseLeave/click events apply to everything in the panel, not just in the 'CheckBoxButton'.
488 // Examples:
489 // $('CheckBox', 'aBooleanDataProperty', $(go.TextBlock, 'the checkbox label'))
490 // or
491 // $('CheckBox', 'someProperty', $(go.TextBlock, 'A choice'),
492 // { '_doClick': function(e, obj) { ... perform extra side-effects ... } })
493 go.GraphObject.defineBuilder('CheckBox', function (args) {
494 // process the one required string argument for this kind of button
495 var propname = /** @type {string} */ (go.GraphObject.takeBuilderArgument(args));
496 var button = /** @type {Panel} */ (go.GraphObject.make('CheckBoxButton', propname, // bound to this data property
497 {
498 name: 'Button',
499 isActionable: false,
500 margin: new go.Margin(0, 1, 0, 0)
501 }));
502 var box = /** @type {Panel} */ (go.GraphObject.make(go.Panel, 'Horizontal', button, {
503 isActionable: true,
504 cursor: button.cursor,
505 margin: 1,
506 // transfer CheckBoxButton properties over to this new CheckBox panel
507 '_buttonFillNormal': button['_buttonFillNormal'],
508 '_buttonStrokeNormal': button['_buttonStrokeNormal'],
509 '_buttonFillOver': button['_buttonFillOver'],
510 '_buttonStrokeOver': button['_buttonStrokeOver'],
511 '_buttonFillPressed': button['_buttonFillPressed'],
512 '_buttonStrokePressed': button['_buttonStrokePressed'],
513 '_buttonFillDisabled': button['_buttonFillDisabled'],
514 mouseEnter: button.mouseEnter,
515 mouseLeave: button.mouseLeave,
516 actionDown: button.actionDown,
517 actionUp: button.actionUp,
518 actionCancel: button.actionCancel,
519 actionMove: button.actionMove,
520 click: button.click,
521 // also save original Button behavior, for potential use in a Panel.click event handler
522 '_buttonClick': button.click
523 }));
524 // avoid potentially conflicting event handlers on the 'CheckBoxButton'
525 button.mouseEnter = null;
526 button.mouseLeave = null;
527 button.actionDown = null;
528 button.actionUp = null;
529 button.actionCancel = null;
530 button.actionMove = null;
531 button.click = null;
532 return box;
533 });
534});