bpmn-js 16.2.0 → 16.3.0

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.
@@ -0,0 +1,216 @@
1
+ import inherits from 'inherits-browser';
2
+
3
+ import { getBusinessObject, is } from '../../../util/ModelUtil';
4
+
5
+ import CommandInterceptor from 'diagram-js/lib/command/CommandInterceptor';
6
+ import { hasEventDefinition, isEventSubProcess } from '../../../util/DiUtil';
7
+
8
+ /**
9
+ * @typedef {import('diagram-js/lib/core/EventBus').default} EventBus
10
+ * @typedef {import('../lib/features/modeling/Modeling').default} Modeling
11
+ */
12
+
13
+ /**
14
+ *
15
+ *
16
+ * @param {import('diagram-js/lib/core/EventBus').default} eventBus
17
+ * @param {import('../Modeling').default} modeling
18
+ * @param {import('../../rules/BpmnRules').default} bpmnRules
19
+ */
20
+ export default function CompensateBoundaryEventBehavior(eventBus, modeling, bpmnRules) {
21
+
22
+ CommandInterceptor.call(this, eventBus);
23
+
24
+ this.preExecute('shape.replace', handleReplacement, true);
25
+ this.postExecuted('shape.replace', handleReplacementPostExecuted, true);
26
+ this.preExecute('connection.create', handleNewConnection, true);
27
+ this.postExecuted('connection.delete', handleConnectionRemoval, true);
28
+ this.postExecuted('connection.reconnect', handleReconnection, true);
29
+ this.postExecuted('element.updateProperties', handlePropertiesUpdate, true);
30
+
31
+ /**
32
+ * Given a connection from boundary event is removed, remove the forCompensation property.
33
+ */
34
+ function handleConnectionRemoval(context) {
35
+ const source = context.source,
36
+ target = context.target;
37
+
38
+ if (isCompensationBoundaryEvent(source) && isForCompensation(target)) {
39
+ removeIsForCompensationProperty(target);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Add forCompensation property and make sure only a single compensation activity is connected.
45
+ */
46
+ function handleNewConnection(context) {
47
+ const source = context.source,
48
+ target = context.target;
49
+
50
+ if (isCompensationBoundaryEvent(source) && canBeForCompensation(target)) {
51
+ addIsForCompensationProperty(target);
52
+ removeExistingAssociation(source);
53
+ }
54
+ }
55
+
56
+ function handleReconnection(context) {
57
+ const newTarget = context.newTarget,
58
+ oldSource = context.oldSource,
59
+ oldTarget = context.oldTarget;
60
+
61
+ // target changes
62
+ if (oldTarget !== newTarget) {
63
+ const source = oldSource;
64
+
65
+ // oldTarget perspective
66
+ if (isForCompensation(oldTarget)) {
67
+ removeIsForCompensationProperty(oldTarget);
68
+ }
69
+
70
+ // newTarget perspective
71
+ if (isCompensationBoundaryEvent(source) && canBeForCompensation(newTarget)) {
72
+ addIsForCompensationProperty(newTarget);
73
+ }
74
+ }
75
+ }
76
+
77
+ function handlePropertiesUpdate(context) {
78
+ const { element } = context;
79
+
80
+ if (isForCompensation(element)) {
81
+ removeIllegalConnections(element);
82
+ removeAttachments(element);
83
+ } else if (canBeForCompensation(element)) {
84
+ removeIncomingCompensationAssociations(element);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * When replacing a boundary event, make sure the compensation activity is connected,
90
+ * and remove the potential candidates for connection replacement to have a single compensation activity.
91
+ */
92
+ function handleReplacement(context) {
93
+ const { targetElement } = context.hints || {},
94
+ { oldShape } = context;
95
+
96
+ // from compensate boundary event
97
+ if (isCompensationBoundaryEvent(context.oldShape) &&
98
+ targetElement.eventDefinitionType !== 'bpmn:CompensateEventDefinition' ||
99
+ targetElement.type !== 'bpmn:BoundaryEvent'
100
+ ) {
101
+ const targetConnection = oldShape.outgoing.find(
102
+ ({ target }) => isForCompensation(target)
103
+ );
104
+
105
+ if (targetConnection && targetConnection.target) {
106
+ context._connectionTarget = targetConnection.target;
107
+ }
108
+ }
109
+
110
+ // to compensate boundary event
111
+ else if (
112
+ !isCompensationBoundaryEvent(context.oldShape) &&
113
+ targetElement.eventDefinitionType === 'bpmn:CompensateEventDefinition' &&
114
+ targetElement.type === 'bpmn:BoundaryEvent'
115
+ ) {
116
+ const targetConnection = oldShape.outgoing.find(
117
+ ({ target }) => canBeForCompensation(target)
118
+ );
119
+
120
+ if (targetConnection && targetConnection.target) {
121
+ context._connectionTarget = targetConnection.target;
122
+ }
123
+
124
+ removeOutgoingSequenceFlows(oldShape);
125
+ }
126
+ }
127
+
128
+ function handleReplacementPostExecuted(context) {
129
+ const { _connectionTarget: target, newShape } = context;
130
+
131
+ if (target) {
132
+ modeling.connect(newShape, target);
133
+ }
134
+ }
135
+
136
+ function addIsForCompensationProperty(target) {
137
+ modeling.updateProperties(target, { isForCompensation: true });
138
+ }
139
+
140
+ function removeIsForCompensationProperty(target) {
141
+ modeling.updateProperties(target, { isForCompensation: undefined });
142
+ }
143
+
144
+ function removeIllegalConnections(element) {
145
+
146
+ for (const connection of element.incoming) {
147
+ if (!bpmnRules.canConnect(connection.source, element)) {
148
+ modeling.removeConnection(connection);
149
+ }
150
+ }
151
+
152
+ for (const connection of element.outgoing) {
153
+ if (!bpmnRules.canConnect(element, connection.target)) {
154
+ modeling.removeConnection(connection);
155
+ }
156
+ }
157
+ }
158
+
159
+ function removeExistingAssociation(boundaryEvent) {
160
+ const associations = boundaryEvent.outgoing.filter(connection => is(connection, 'bpmn:Association'));
161
+ const associationsToRemove = associations.filter(association => isForCompensation(association.target));
162
+
163
+ // remove existing associations
164
+ associationsToRemove.forEach(association => modeling.removeConnection(association));
165
+ }
166
+
167
+ function removeAttachments(element) {
168
+ const attachments = element.attachers.slice();
169
+
170
+ if (!attachments.length) {
171
+ return;
172
+ }
173
+
174
+ modeling.removeElements(attachments);
175
+ }
176
+
177
+ function removeIncomingCompensationAssociations(element) {
178
+ const compensationAssociations = element.incoming.filter(
179
+ connection => isCompensationBoundaryEvent(connection.source)
180
+ );
181
+
182
+ modeling.removeElements(compensationAssociations);
183
+ }
184
+
185
+ function removeOutgoingSequenceFlows(element) {
186
+ const sequenceFlows = element.outgoing.filter(
187
+ connection => is(connection, 'bpmn:SequenceFlow')
188
+ );
189
+
190
+ modeling.removeElements(sequenceFlows);
191
+ }
192
+ }
193
+
194
+ inherits(CompensateBoundaryEventBehavior, CommandInterceptor);
195
+
196
+ CompensateBoundaryEventBehavior.$inject = [
197
+ 'eventBus',
198
+ 'modeling',
199
+ 'bpmnRules'
200
+ ];
201
+
202
+ // helpers //////////
203
+
204
+ function isForCompensation(element) {
205
+ const bo = getBusinessObject(element);
206
+ return bo && bo.get('isForCompensation');
207
+ }
208
+
209
+ function isCompensationBoundaryEvent(element) {
210
+ return element && is(element, 'bpmn:BoundaryEvent') &&
211
+ hasEventDefinition(element, 'bpmn:CompensateEventDefinition');
212
+ }
213
+
214
+ function canBeForCompensation(element) {
215
+ return element && is(element, 'bpmn:Activity') && !isEventSubProcess(element);
216
+ }
@@ -76,11 +76,6 @@ export default function ReplaceConnectionBehavior(eventBus, modeling, bpmnRules,
76
76
  }
77
77
  }
78
78
 
79
- if (is(connection, 'bpmn:Association') && !bpmnRules.canConnectAssociation(source, target)) {
80
- remove = true;
81
- }
82
-
83
-
84
79
  // remove invalid connection,
85
80
  // unless it has been removed already
86
81
  if (remove) {
@@ -114,11 +109,14 @@ export default function ReplaceConnectionBehavior(eventBus, modeling, bpmnRules,
114
109
 
115
110
  replacement = modeling.connect(source, target, {
116
111
  type: allowed.type,
112
+ associationDirection: allowed.associationDirection,
117
113
  waypoints: connection.waypoints.slice()
118
114
  });
119
115
 
120
- // remove old connection
121
- modeling.removeConnection(connection);
116
+ // remove old connection unless it's already removed
117
+ if (connection.parent) {
118
+ modeling.removeConnection(connection);
119
+ }
122
120
 
123
121
  // replace connection in context to reconnect end/start
124
122
  context.connection = replacement;
@@ -3,6 +3,7 @@ import AppendBehavior from './AppendBehavior';
3
3
  import AssociationBehavior from './AssociationBehavior';
4
4
  import AttachEventBehavior from './AttachEventBehavior';
5
5
  import BoundaryEventBehavior from './BoundaryEventBehavior';
6
+ import CompensateBoundaryEventBehavior from './CompensateBoundaryEventBehavior';
6
7
  import CreateBehavior from './CreateBehavior';
7
8
  import CreateDataObjectBehavior from './CreateDataObjectBehavior';
8
9
  import CreateParticipantBehavior from './CreateParticipantBehavior';
@@ -47,6 +48,7 @@ export default {
47
48
  'associationBehavior',
48
49
  'attachEventBehavior',
49
50
  'boundaryEventBehavior',
51
+ 'compensateBoundaryEventBehaviour',
50
52
  'createBehavior',
51
53
  'createDataObjectBehavior',
52
54
  'createParticipantBehavior',
@@ -86,6 +88,7 @@ export default {
86
88
  associationBehavior: [ 'type', AssociationBehavior ],
87
89
  attachEventBehavior: [ 'type', AttachEventBehavior ],
88
90
  boundaryEventBehavior: [ 'type', BoundaryEventBehavior ],
91
+ compensateBoundaryEventBehaviour: [ 'type', CompensateBoundaryEventBehavior ],
89
92
  createBehavior: [ 'type', CreateBehavior ],
90
93
  createDataObjectBehavior: [ 'type', CreateDataObjectBehavior ],
91
94
  createParticipantBehavior: [ 'type', CreateParticipantBehavior ],
@@ -204,6 +204,10 @@ export default function BpmnReplace(
204
204
  return false;
205
205
  }
206
206
 
207
+ if (propertyName === 'isForCompensation') {
208
+ return !isEventSubProcess(newBusinessObject);
209
+ }
210
+
207
211
  return true;
208
212
  });
209
213
 
@@ -7,6 +7,7 @@ export default class BpmnRules extends RuleProvider {
7
7
  canConnectSequenceFlow: typeof canConnectSequenceFlow;
8
8
  canConnectDataAssociation: typeof canConnectDataAssociation;
9
9
  canConnectAssociation: typeof canConnectAssociation;
10
+ canConnectCompensationAssociation: typeof canConnectCompensationAssociation;
10
11
  canMove: typeof canMove;
11
12
  canAttach: typeof canAttach;
12
13
  canReplace: typeof canReplace;
@@ -27,7 +28,7 @@ type Point = import('diagram-js/lib/util/Types').Point;
27
28
  type Rect = import('diagram-js/lib/util/Types').Rect;
28
29
 
29
30
  export type CanConnectResult = {
30
- associationDirection?: string;
31
+ associationDirection?: 'None' | 'One' | 'Both';
31
32
  type: string;
32
33
  } | boolean | null;
33
34
 
@@ -70,6 +71,14 @@ declare function canConnectDataAssociation(source: Element, target: Element): Ca
70
71
  */
71
72
  declare function canConnectAssociation(source: Element, target: Element): CanConnectResult;
72
73
 
74
+ /**
75
+ * @param source
76
+ * @param target
77
+ *
78
+ * @return
79
+ */
80
+ declare function canConnectCompensationAssociation(source: Element, target: Element): boolean;
81
+
73
82
  /**
74
83
  * @param elements
75
84
  * @param target
@@ -50,7 +50,7 @@ import { isConnection } from 'diagram-js/lib/util/ModelUtil';
50
50
  * @typedef {import('diagram-js/lib/util/Types').Rect} Rect
51
51
  *
52
52
  * @typedef { {
53
- * associationDirection?: string;
53
+ * associationDirection?: 'None' | 'One' | 'Both';
54
54
  * type: string;
55
55
  * } | boolean | null } CanConnectResult
56
56
  *
@@ -204,6 +204,8 @@ BpmnRules.prototype.canConnectDataAssociation = canConnectDataAssociation;
204
204
 
205
205
  BpmnRules.prototype.canConnectAssociation = canConnectAssociation;
206
206
 
207
+ BpmnRules.prototype.canConnectCompensationAssociation = canConnectCompensationAssociation;
208
+
207
209
  BpmnRules.prototype.canMove = canMove;
208
210
 
209
211
  BpmnRules.prototype.canAttach = canAttach;
@@ -550,7 +552,7 @@ function canConnect(source, target, connection) {
550
552
  return connectDataAssociation;
551
553
  }
552
554
 
553
- if (isCompensationBoundary(source) && isForCompensation(target)) {
555
+ if (canConnectCompensationAssociation(source, target)) {
554
556
  return {
555
557
  type: 'bpmn:Association',
556
558
  associationDirection: 'One'
@@ -558,9 +560,9 @@ function canConnect(source, target, connection) {
558
560
  }
559
561
 
560
562
  if (canConnectAssociation(source, target)) {
561
-
562
563
  return {
563
- type: 'bpmn:Association'
564
+ type: 'bpmn:Association',
565
+ associationDirection: 'None'
564
566
  };
565
567
  }
566
568
 
@@ -1027,11 +1029,6 @@ function isOneTextAnnotation(source, target) {
1027
1029
  */
1028
1030
  function canConnectAssociation(source, target) {
1029
1031
 
1030
- // compensation boundary events are exception
1031
- if (isCompensationBoundary(source) && isForCompensation(target)) {
1032
- return true;
1033
- }
1034
-
1035
1032
  // don't connect parent <-> child
1036
1033
  if (isParent(target, source) || isParent(source, target)) {
1037
1034
  return false;
@@ -1047,6 +1044,22 @@ function canConnectAssociation(source, target) {
1047
1044
  return !!canConnectDataAssociation(source, target);
1048
1045
  }
1049
1046
 
1047
+ /**
1048
+ * @param {Element} source
1049
+ * @param {Element} target
1050
+ *
1051
+ * @return {boolean}
1052
+ */
1053
+ function canConnectCompensationAssociation(source, target) {
1054
+ return (
1055
+ isSameScope(source, target) &&
1056
+ isCompensationBoundary(source) &&
1057
+ is(target, 'bpmn:Activity') &&
1058
+ !isHostOfElement(target, source) &&
1059
+ !isEventSubProcess(target)
1060
+ );
1061
+ }
1062
+
1050
1063
  /**
1051
1064
  * @param {Element} source
1052
1065
  * @param {Element} target
@@ -1172,3 +1185,7 @@ function canCopy(elements, element) {
1172
1185
  function getRootElement(element) {
1173
1186
  return getParent(element, 'bpmn:Process') || getParent(element, 'bpmn:Collaboration');
1174
1187
  }
1188
+
1189
+ function isHostOfElement(potentialHost, element) {
1190
+ return potentialHost.attachers.includes(element);
1191
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bpmn-js",
3
- "version": "16.2.0",
3
+ "version": "16.3.0",
4
4
  "description": "A bpmn 2.0 toolkit and web modeler",
5
5
  "main": "index.js",
6
6
  "files": [