bpmn-js 18.13.0 → 18.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,8 @@ import {
4
4
  is
5
5
  } from '../../util/ModelUtil';
6
6
 
7
+ import { collectElementsAnnotations } from '../../util/AnnotationUtil';
8
+
7
9
  import {
8
10
  forEach,
9
11
  isArray,
@@ -170,6 +172,21 @@ export default function BpmnCopyPaste(bpmnFactory, eventBus, moddleCopy) {
170
172
  }
171
173
  });
172
174
 
175
+ eventBus.on('copyPaste.createTree', function(context) {
176
+ var element = context.element,
177
+ children = context.children;
178
+
179
+ if (!is(element, 'bpmn:SubProcess')) {
180
+ return;
181
+ }
182
+
183
+ // add TextAnnotations to copy the closure,
184
+ // since by default they are children of a global process, not subprocess
185
+ forEach(collectElementsAnnotations(children), (entry) => {
186
+ children.push(entry.annotation);
187
+ });
188
+ });
189
+
173
190
  // resolve references
174
191
 
175
192
  eventBus.on('copyPaste.pasteElement', LOW_PRIORITY, function(context) {
@@ -467,17 +467,34 @@ function getBoundaryEventTargetLayout(attachOrientation, targetOrientation, atta
467
467
  }
468
468
  }
469
469
 
470
- // attached to either top-right, top-left, bottom-right or bottom-left corner
471
- // and orientation is same on the counter-axis
470
+ // attached to either top-right, top-left, bottom-right or bottom-left corner,
471
+ // or strictly above/below or left/right of the target. In the corner case,
472
+ // the orientation is compared on the counter-axis to decide the layout.
473
+
474
+ var verticalAttachOrientation = getVerticalOrientation(attachOrientation),
475
+ horizontalAttachOrientation = getHorizontalOrientation(attachOrientation),
476
+ verticalTargetOrientation = getVerticalOrientation(targetOrientation),
477
+ horizontalTargetOrientation = getHorizontalOrientation(targetOrientation);
478
+
479
+ // If the target is strictly above/below (no horizontal orientation)
480
+ if (verticalTargetOrientation && !horizontalTargetOrientation) {
481
+ return 'v';
482
+ }
483
+
484
+ // If the target is strictly left/right (no vertical orientation)
485
+ if (horizontalTargetOrientation && !verticalTargetOrientation) {
486
+ return 'h';
487
+ }
488
+
472
489
 
473
490
  if (isHorizontal) {
474
- if (isSame(getVerticalOrientation(attachOrientation), getVerticalOrientation(targetOrientation))) {
491
+ if (isSame(verticalAttachOrientation, verticalTargetOrientation)) {
475
492
  return 'h';
476
493
  } else {
477
494
  return 'v';
478
495
  }
479
496
  } else {
480
- if (isSame(getHorizontalOrientation(attachOrientation), getHorizontalOrientation(targetOrientation))) {
497
+ if (isSame(horizontalAttachOrientation, horizontalTargetOrientation)) {
481
498
  return 'v';
482
499
  } else {
483
500
  return 'h';
@@ -2,15 +2,20 @@ import inherits from 'inherits-browser';
2
2
 
3
3
  import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
4
4
 
5
- import { find } from 'min-dash';
5
+ import { find, forEach } from 'min-dash';
6
6
 
7
7
  import { isExpanded } from '../../../util/DiUtil';
8
8
 
9
+ import {
10
+ collectElementsAnnotations,
11
+ getElementAnnotations
12
+ } from '../../../util/AnnotationUtil';
13
+
9
14
  import { getBusinessObject, getDi, is } from '../../../util/ModelUtil';
10
15
 
11
16
  import { getMid } from 'diagram-js/lib/layout/LayoutUtil';
12
17
 
13
- import { getBBox } from 'diagram-js/lib/util/Elements';
18
+ import { getBBox, selfAndChildren } from 'diagram-js/lib/util/Elements';
14
19
 
15
20
  import {
16
21
  getPlaneIdFromShape,
@@ -41,7 +46,6 @@ var DEFAULT_POSITION = {
41
46
  y: 160
42
47
  };
43
48
 
44
-
45
49
  /**
46
50
  * Creates bpmndi:BPMNPlane elements and canvas planes when collapsed subprocesses are created.
47
51
  *
@@ -107,17 +111,26 @@ export default function SubProcessPlaneBehavior(
107
111
  }, true);
108
112
 
109
113
 
110
- this.postExecuted('shape.create', function(context) {
111
- var shape = context.shape,
112
- rootElement = context.newRootElement;
114
+ this.postExecuted('elements.create', function(context) {
115
+ var elements = context.elements;
113
116
 
114
- if (!rootElement || !shape.children) {
115
- return;
116
- }
117
+ forEach(elements, function(element) {
118
+ if (!isCollapsedSubProcess(element)) {
119
+ return;
120
+ }
121
+
122
+ var rootElement = elementRegistry.get(getPlaneIdFromShape(element));
123
+
124
+ if (!rootElement || !element.children || !element.children.length) {
125
+ return;
126
+ }
117
127
 
118
- self._showRecursively(shape.children);
128
+ var children = getSubProcessChildren(element);
119
129
 
120
- self._moveChildrenToShape(shape, rootElement);
130
+ self._showRecursively(children);
131
+
132
+ self._moveChildrenToShape(children, rootElement);
133
+ });
121
134
  }, true);
122
135
 
123
136
 
@@ -131,6 +144,20 @@ export default function SubProcessPlaneBehavior(
131
144
  }, true);
132
145
 
133
146
 
147
+ // remove annotations connected to elements inside the subprocess
148
+ this.preExecute('shape.delete', function(context) {
149
+ var shape = context.shape;
150
+
151
+ if (!is(shape, 'bpmn:SubProcess') || !isExpanded(shape)) {
152
+ return;
153
+ }
154
+
155
+ forEach(collectElementsAnnotations([ shape ]), (entry) => {
156
+ modeling.removeShape(entry.annotation);
157
+ });
158
+ }, true);
159
+
160
+
134
161
  this.preExecuted('shape.delete', function(context) {
135
162
  var shape = context.shape;
136
163
  if (!isCollapsedSubProcess(shape)) {
@@ -336,11 +363,27 @@ export default function SubProcessPlaneBehavior(
336
363
 
337
364
  if (!isExpanded(shape)) {
338
365
 
339
- // collapsed
340
- self._moveChildrenToShape(shape, rootElement);
366
+ // if an annotation is attached to both the sub-process and elements inside it,
367
+ // remove the inner associations, keeping only connections at the sub-process level,
368
+ // since we can't keep both after collapsing
369
+ self._disconnectSharedAnnotations(shape);
370
+
371
+ var children = getSubProcessChildren(shape);
372
+
373
+ self._moveChildrenToShape(children, rootElement);
341
374
 
342
375
  } else {
343
- self._moveChildrenToShape(rootElement, shape);
376
+ self._moveChildrenToShape(rootElement.children.slice(), shape);
377
+
378
+ // annotations live at process level by design;
379
+ // move them back from the sub-process to the process root
380
+ forEach(collectElementsAnnotations(shape.children), (entry) => {
381
+ modeling.moveShape(entry.annotation, { x: 0, y: 0 }, shape.parent);
382
+
383
+ forEach(entry.associations, (association) => {
384
+ modeling.moveConnection(association, { x: 0, y: 0 }, shape.parent);
385
+ });
386
+ });
344
387
  }
345
388
  }, true);
346
389
 
@@ -410,33 +453,21 @@ export default function SubProcessPlaneBehavior(
410
453
  inherits(SubProcessPlaneBehavior, CommandInterceptor);
411
454
 
412
455
  /**
413
- * Moves the child elements from source to target.
456
+ * Moves the given elements to the target.
414
457
  *
415
458
  * If the target is a plane, the children are moved to the top left corner.
416
459
  * Otherwise, the center of the target is used.
417
460
  *
418
- * @param {Root} source
461
+ * @param {Element[]} children
419
462
  * @param {Root} target
420
463
  */
421
- SubProcessPlaneBehavior.prototype._moveChildrenToShape = function(source, target) {
464
+ SubProcessPlaneBehavior.prototype._moveChildrenToShape = function(children, target) {
422
465
  var modeling = this._modeling;
423
466
 
424
- var children = source.children;
425
- var offset;
426
-
427
- if (!children) {
467
+ if (!children.length) {
428
468
  return;
429
469
  }
430
470
 
431
- // add external labels that weren't children of sub process
432
- children = children.concat(children.reduce(function(labels, child) {
433
- if (child.label && child.label.parent !== source) {
434
- return labels.concat(child.label);
435
- }
436
-
437
- return labels;
438
- }, []));
439
-
440
471
  // only change plane if there are no visible children, but don't move them
441
472
  var visibleChildren = children.filter(function(child) {
442
473
  return !child.hidden;
@@ -448,6 +479,7 @@ SubProcessPlaneBehavior.prototype._moveChildrenToShape = function(source, target
448
479
  }
449
480
 
450
481
  var childrenBounds = getBBox(visibleChildren);
482
+ var offset;
451
483
 
452
484
  // target is a plane
453
485
  if (!target.x) {
@@ -473,6 +505,32 @@ SubProcessPlaneBehavior.prototype._moveChildrenToShape = function(source, target
473
505
  modeling.moveElements(children, offset, target, { autoResize: false });
474
506
  };
475
507
 
508
+ /**
509
+ * Remove all associations between inner elements and annotations that are
510
+ * also connected to the sub-process itself.
511
+ *
512
+ * @param {Element} shape
513
+ */
514
+ SubProcessPlaneBehavior.prototype._disconnectSharedAnnotations = function(shape) {
515
+ var modeling = this._modeling;
516
+
517
+ var sharedAnnotations = new Set(
518
+ getElementAnnotations(shape).map((entry) => entry.annotation)
519
+ );
520
+
521
+ if (!sharedAnnotations.size) {
522
+ return;
523
+ }
524
+
525
+ forEach(collectElementsAnnotations(shape.children), (entry) => {
526
+ if (sharedAnnotations.has(entry.annotation)) {
527
+ forEach(entry.associations, (association) => {
528
+ modeling.removeConnection(association);
529
+ });
530
+ }
531
+ });
532
+ };
533
+
476
534
  /**
477
535
  * Sets `hidden` property on all children of the given shape.
478
536
  *
@@ -586,3 +644,55 @@ SubProcessPlaneBehavior.$inject = [
586
644
  'bpmnjs',
587
645
  'elementRegistry'
588
646
  ];
647
+
648
+ /**
649
+ * Collect text annotations and their associations connected to the given elements.
650
+ *
651
+ * @param {Element[]} elements
652
+ *
653
+ * @return {Element[]}
654
+ */
655
+ function collectAnnotationElements(elements) {
656
+ var result = [];
657
+
658
+ forEach(collectElementsAnnotations(elements), (entry) => {
659
+ result.push(entry.annotation);
660
+ result.push.apply(result, entry.associations);
661
+ });
662
+
663
+ return result;
664
+ }
665
+
666
+ /**
667
+ * Collect all children of a collapsed subprocess that need to be moved
668
+ * to or from its plane, including annotations and external labels that
669
+ * live at the process level by design(ref: https://github.com/camunda/camunda-modeler/issues/5163#issuecomment-3946571271)
670
+ *
671
+ * @param {Element} element
672
+ *
673
+ * @return {Element[]}
674
+ */
675
+ function getSubProcessChildren(element) {
676
+ return element.children.slice()
677
+ .concat(collectAnnotationElements(element.children))
678
+ .concat(getExternalLabels(element));
679
+ }
680
+
681
+ /**
682
+ * Returns external labels of the given element's children and their descendants
683
+ * that are not direct children of the element (i.e. parented elsewhere).
684
+ *
685
+ * @param {Element} element
686
+ *
687
+ * @return {Element[]}
688
+ */
689
+ function getExternalLabels(element) {
690
+ return selfAndChildren(element.children || [], true, -1)
691
+ .reduce(function(labels, child) {
692
+ if (child.label && child.label.parent !== element) {
693
+ labels.push(child.label);
694
+ }
695
+ return labels;
696
+ }, []);
697
+ }
698
+
@@ -12,6 +12,8 @@ import { selfAndAllChildren } from 'diagram-js/lib/util/Elements';
12
12
 
13
13
  import { isExpanded } from '../../../util/DiUtil';
14
14
 
15
+ import { is } from '../../../util/ModelUtil';
16
+
15
17
  /**
16
18
  * @typedef {import('diagram-js/lib/core/EventBus').default} EventBus
17
19
  * @typedef {import('../Modeling').default} Modeling
@@ -34,7 +36,6 @@ export default function ToggleCollapseConnectionBehaviour(
34
36
 
35
37
  this.postExecuted('shape.toggleCollapse', 1500, function(context) {
36
38
 
37
- // var shape = context.shape;
38
39
  var shape = context.shape;
39
40
 
40
41
  // only change connections when collapsing
@@ -65,6 +66,12 @@ export default function ToggleCollapseConnectionBehaviour(
65
66
  return;
66
67
  }
67
68
 
69
+ // don't reconnect TextAnnotation connections,
70
+ // since they should stay connected and moved with its element to the subprocess plane
71
+ if (is(c, 'bpmn:Association') && (is(c.source, 'bpmn:TextAnnotation') || is(c.target, 'bpmn:TextAnnotation'))) {
72
+ return;
73
+ }
74
+
68
75
  if (incoming) {
69
76
  modeling.reconnectEnd(c, shape, getMid(shape));
70
77
  } else {
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Get text annotations connected to the given element.
3
+ *
4
+ * @param element
5
+ *
6
+ * @return
7
+ */
8
+ export function getElementAnnotations(element: Element): {
9
+ annotation: Element;
10
+ association: Element;
11
+ }[];
12
+
13
+ /**
14
+ * Recursively collect text annotations connected to the given elements and their descendants.
15
+ * De-duplicates by annotation, collecting all associations per annotation.
16
+ *
17
+ * @param elements
18
+ *
19
+ * @return
20
+ */
21
+ export function collectElementsAnnotations(elements: Element[]): {
22
+ annotation: Element;
23
+ associations: Element[];
24
+ }[];
25
+
26
+ type Element = import("../model/Types").Element;
@@ -0,0 +1,55 @@
1
+ import { forEach } from 'min-dash';
2
+ import { selfAndChildren } from 'diagram-js/lib/util/Elements';
3
+ import { is } from './ModelUtil';
4
+
5
+ /**
6
+ * @typedef { import('../model/Types').Element } Element
7
+ */
8
+
9
+ /**
10
+ * Get text annotations connected to the given element.
11
+ *
12
+ * @param {Element} element
13
+ *
14
+ * @return { { annotation: Element, association: Element }[] }
15
+ */
16
+ export function getElementAnnotations(element) {
17
+ let result = [];
18
+
19
+ forEach(element.incoming, (connection) => {
20
+ if (is(connection, 'bpmn:Association') && is(connection.source, 'bpmn:TextAnnotation')) {
21
+ result.push({ annotation: connection.source, association: connection });
22
+ }
23
+ });
24
+
25
+ forEach(element.outgoing, (connection) => {
26
+ if (is(connection, 'bpmn:Association') && is(connection.target, 'bpmn:TextAnnotation')) {
27
+ result.push({ annotation: connection.target, association: connection });
28
+ }
29
+ });
30
+
31
+ return result;
32
+ }
33
+
34
+ /**
35
+ * Recursively collect text annotations connected to the given elements and their descendants.
36
+ * De-duplicates by annotation, collecting all associations per annotation.
37
+ *
38
+ * @param {Element[]} elements
39
+ *
40
+ * @return { { annotation: Element, associations: Element[] }[] }
41
+ */
42
+ export function collectElementsAnnotations(elements) {
43
+ const result = new Map();
44
+
45
+ forEach(selfAndChildren(elements, true, -1), (element) => {
46
+ forEach(getElementAnnotations(element), (entry) => {
47
+ if (!result.has(entry.annotation)) {
48
+ result.set(entry.annotation, { annotation: entry.annotation, associations: [] });
49
+ }
50
+ result.get(entry.annotation).associations.push(entry.association);
51
+ });
52
+ });
53
+
54
+ return [ ...result.values() ];
55
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bpmn-js",
3
- "version": "18.13.0",
3
+ "version": "18.13.2",
4
4
  "description": "A bpmn 2.0 toolkit and web modeler",
5
5
  "main": "lib/index.js",
6
6
  "files": [
@@ -69,7 +69,7 @@
69
69
  "@rollup/plugin-json": "^6.1.0",
70
70
  "@rollup/plugin-node-resolve": "^16.0.3",
71
71
  "@rollup/plugin-replace": "^6.0.3",
72
- "@rollup/plugin-terser": "^0.4.4",
72
+ "@rollup/plugin-terser": "^1.0.0",
73
73
  "babel-loader": "^10.0.0",
74
74
  "babel-plugin-istanbul": "^7.0.1",
75
75
  "bio-dts": "^0.11.0",
@@ -111,7 +111,7 @@
111
111
  },
112
112
  "dependencies": {
113
113
  "bpmn-moddle": "^10.0.0",
114
- "diagram-js": "^15.9.0",
114
+ "diagram-js": "^15.10.0",
115
115
  "diagram-js-direct-editing": "^3.3.0",
116
116
  "ids": "^3.0.0",
117
117
  "inherits-browser": "^0.1.0",