@processmaker/modeler 1.26.0 → 1.28.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.
Files changed (34) hide show
  1. package/dist/img/clipboard.bcc7796a.svg +1 -0
  2. package/dist/modeler.common.js +1790 -740
  3. package/dist/modeler.common.js.map +1 -1
  4. package/dist/modeler.umd.js +1790 -740
  5. package/dist/modeler.umd.js.map +1 -1
  6. package/dist/modeler.umd.min.js +3 -3
  7. package/dist/modeler.umd.min.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/NodeIdGenerator.js +55 -22
  10. package/src/NodeInspector.js +2 -2
  11. package/src/assets/clipboard.svg +1 -0
  12. package/src/components/crown/crownButtons/copyButton.vue +3 -3
  13. package/src/components/crown/crownButtons/duplicateButton.vue +40 -0
  14. package/src/components/crown/crownConfig/crownConfig.vue +7 -1
  15. package/src/components/crown/crownMultiselect/crownMultiselect.vue +15 -6
  16. package/src/components/crown/utils.js +12 -1
  17. package/src/components/hotkeys/copyPaste.js +26 -0
  18. package/src/components/hotkeys/main.js +9 -2
  19. package/src/components/inspectors/InspectorPanel.vue +1 -0
  20. package/src/components/inspectors/LoopCharacteristics.vue +5 -2
  21. package/src/components/inspectors/process.js +5 -1
  22. package/src/components/modeler/Modeler.vue +104 -4
  23. package/src/components/modeler/Selection.vue +18 -4
  24. package/src/components/nodes/association/index.js +3 -0
  25. package/src/components/nodes/dataInputAssociation/dataInputAssociation.vue +36 -26
  26. package/src/components/nodes/genericFlow/DataOutputAssociation.js +54 -2
  27. package/src/components/nodes/genericFlow/genericFlow.vue +0 -17
  28. package/src/components/nodes/node.js +106 -2
  29. package/src/components/toolbar/ToolBar.vue +17 -3
  30. package/src/components/toolbar/breadcrumb/Breadcrumb.vue +7 -0
  31. package/src/components/toolbar/toolbar.scss +11 -0
  32. package/src/mixins/cloneSelection.js +145 -0
  33. package/src/mixins/linkConfig.js +4 -1
  34. package/src/store.js +11 -0
@@ -29,6 +29,7 @@ import { id as poolId } from '@/components/nodes/pool/config';
29
29
  import { id as laneId } from '@/components/nodes/poolLane/config';
30
30
  import { id as genericFlowId } from '@/components/nodes/genericFlow/config';
31
31
  import { labelWidth, poolPadding } from '../nodes/pool/poolSizes';
32
+
32
33
  export default {
33
34
  name: 'Selection',
34
35
  components: {
@@ -85,6 +86,17 @@ export default {
85
86
  },
86
87
  },
87
88
  methods: {
89
+ async selectElements(elements) {
90
+ await this.$nextTick();
91
+ this.clearSelection();
92
+ this.selected = elements;
93
+ this.showLasso = true;
94
+ this.isSelected = true;
95
+ this.isSelecting = true;
96
+ this.start = null;
97
+ await this.$nextTick();
98
+ this.updateSelectionBox();
99
+ },
88
100
  /**
89
101
  * Select an element dinamically.
90
102
  * Shift key will manage the condition to push to selection
@@ -321,18 +333,17 @@ export default {
321
333
  * Filter the selected elements
322
334
  */
323
335
  filterSelected() {
324
- // remove from selection the selected child nodes in the pool
336
+ // Get the selected pools IDs
325
337
  const selectedPoolsIds = this.selected
326
338
  .filter(shape => shape.model.component)
327
339
  .filter(shape => shape.model.component.node.type === 'processmaker-modeler-pool')
328
340
  .map(shape => shape.model.component.node.id);
341
+ // remove from selection the selected children that belongs to a selected pool
329
342
  this.selected = this.selected.filter(shape => {
330
343
  if (shape.model.component && shape.model.component.node.pool) {
331
344
  return shape.model.component.node.pool && !selectedPoolsIds.includes(shape.model.component.node.pool.component.node.id);
332
345
  }
333
346
  return true;
334
- }).filter(shape => {
335
- return !(shape.model.getParentCell() && shape.model.getParentCell().get('parent'));
336
347
  });
337
348
  },
338
349
  /**
@@ -377,7 +388,7 @@ export default {
377
388
  return shapes && selected.length === shapes.length;
378
389
  },
379
390
  /**
380
- * Start the drag procedure for the selext box
391
+ * Start the drag procedure for the select box
381
392
  * @param {Object} event
382
393
  */
383
394
  startDrag(event) {
@@ -440,6 +451,9 @@ export default {
440
451
  this.$emit('save-state');
441
452
  this.dragging = false;
442
453
  this.stopForceMove = false;
454
+ // Readjusts the selection box, taking into consideration elements
455
+ // that are anchored and did not move, such as boundary events.
456
+ this.updateSelectionBox();
443
457
  },
444
458
  /**
445
459
  * Translate the Selected shapes adding some custom validations
@@ -14,6 +14,9 @@ export default {
14
14
  associationDirection: `${direction.none}`,
15
15
  });
16
16
  },
17
+ diagram(moddle) {
18
+ return moddle.create('bpmndi:BPMNEdge');
19
+ },
17
20
  inspectorConfig: [
18
21
  {
19
22
  name: 'Data Association',
@@ -21,7 +21,7 @@ import linkConfig from '@/mixins/linkConfig';
21
21
  import get from 'lodash/get';
22
22
  import associationHead from '!!url-loader!@/assets/association-head.svg';
23
23
  import CrownConfig from '@/components/crown/crownConfig/crownConfig';
24
- import { getOrFindDataInput, removeDataInput } from '@/components/crown/utils';
24
+ import { getOrFindDataInput, removeDataInput, findIOSpecificationOwner } from '@/components/crown/utils';
25
25
  import { pull } from 'lodash';
26
26
 
27
27
  export default {
@@ -50,20 +50,38 @@ export default {
50
50
  },
51
51
  computed: {
52
52
  isValidConnection() {
53
- const targetType = get(this.target, 'component.node.type');
53
+ const targetType = get(this.target, 'component.node.definition.$type');
54
54
 
55
55
  if (!targetType) {
56
56
  return false;
57
57
  }
58
58
 
59
- /* A data input association can be connected to anything that isn't a data store or object or a start event */
60
- const invalidTarget = this.targetNode.isBpmnType('bpmn:DataObjectReference', 'bpmn:DataStoreReference', 'bpmn:StartEvent');
59
+ const dataStoreValidTargets = [
60
+ 'bpmn:Task',
61
+ 'bpmn:SubProcess',
62
+ 'bpmn:CallActivity',
63
+ 'bpmn:ManualTask',
64
+ 'bpmn:ScriptTask',
65
+ 'bpmn:ServiceTask',
66
+ ];
67
+ const dataObjectValidTargets = [
68
+ 'bpmn:Task',
69
+ 'bpmn:SubProcess',
70
+ 'bpmn:CallActivity',
71
+ 'bpmn:ManualTask',
72
+ 'bpmn:ScriptTask',
73
+ 'bpmn:ServiceTask',
74
+ 'bpmn:IntermediateThrowEvent',
75
+ 'bpmn:EndEvent',
76
+ ];
61
77
 
62
- if (invalidTarget) {
63
- return false;
64
- }
78
+ const sourceIsDataStore = this.sourceNode.definition.$type === 'bpmn:DataStoreReference';
79
+ const sourceIsDataObject = this.sourceNode.definition.$type === 'bpmn:DataObjectReference';
65
80
 
66
- return true;
81
+ if (sourceIsDataStore && dataStoreValidTargets.includes(targetType)) {
82
+ return true;
83
+ }
84
+ return (sourceIsDataObject && dataObjectValidTargets.includes(targetType));
67
85
  },
68
86
  },
69
87
  methods: {
@@ -71,29 +89,16 @@ export default {
71
89
  if (this.node.dataAssociationProps) {
72
90
  return this.node.dataAssociationProps.sourceShape;
73
91
  }
74
-
75
- const taskWithInputAssociation = this.graph.getElements().find(element => {
76
- return element.component && element.component.node.definition.get('dataInputAssociations') &&
77
- element.component.node.definition.get('dataInputAssociations')[0] === this.node.definition;
78
- });
79
-
80
- const dataObjectDefinition = taskWithInputAssociation.component.node.definition.get('dataInputAssociations')[0].sourceRef[0];
81
-
82
- return this.graph.getElements().find(element => {
83
- return element.component && element.component.node.definition === dataObjectDefinition;
84
- });
92
+ const source = this.node.definition.sourceRef[0];
93
+ // find shape
94
+ const shape = this.graph.getElements().find(e=>e.component.node.definition === source);
95
+ return shape;
85
96
  },
86
97
  getTargetRef() {
87
98
  if (this.node.dataAssociationProps) {
88
99
  return this.node.dataAssociationProps.targetCoords;
89
100
  }
90
-
91
- const taskWithInputAssociation = this.graph.getElements().find(element => {
92
- return element.component && element.component.node.definition.get('dataInputAssociations') &&
93
- element.component.node.definition.get('dataInputAssociations')[0] === this.node.definition;
94
- });
95
-
96
- return taskWithInputAssociation.component.node.definition;
101
+ return findIOSpecificationOwner(this.node.definition.targetRef.$parent, this.$parent);
97
102
  },
98
103
  updateRouter() {
99
104
  this.shape.router('normal', { elementPadding: this.elementPadding });
@@ -102,6 +107,7 @@ export default {
102
107
  const targetShape = this.shape.getTargetElement();
103
108
  const dataInput = getOrFindDataInput(this.moddle, targetShape.component.node, this.sourceNode.definition);
104
109
  this.node.definition.set('targetRef', dataInput);
110
+ // @todo Review why this needs to be and array. When saving the BPMN, if this is not an array the sourceRef is not stored
105
111
  this.node.definition.set('sourceRef', [this.sourceNode.definition]);
106
112
  targetShape.component.node.definition.set('dataInputAssociations', [this.node.definition]);
107
113
  },
@@ -129,6 +135,10 @@ export default {
129
135
  this.shape.component = this;
130
136
  },
131
137
  destroyed() {
138
+ // when a association was not completed this.targetNode will be undefined
139
+ if (!this.targetNode) {
140
+ return;
141
+ }
132
142
  removeDataInput(this.targetNode, this.sourceNode.definition);
133
143
  pull(this.targetNode.definition.get('dataInputAssociations'), this.node.definition);
134
144
  },
@@ -12,8 +12,60 @@ export default class DataOutputAssociation extends DataAssociation {
12
12
  return false;
13
13
  }
14
14
 
15
- return DataAssociation.isADataNode(targetNode) &&
16
- DataOutputAssociation.isValidSourceNode(sourceNode);
15
+ const dataStoreValidSources = [
16
+ 'bpmn:Task',
17
+ 'bpmn:SubProcess',
18
+ 'bpmn:CallActivity',
19
+ 'bpmn:ManualTask',
20
+ 'bpmn:ScriptTask',
21
+ 'bpmn:ServiceTask',
22
+ ];
23
+ const dataStoreValidTargets = [
24
+ 'bpmn:Task',
25
+ 'bpmn:SubProcess',
26
+ 'bpmn:CallActivity',
27
+ 'bpmn:ManualTask',
28
+ 'bpmn:ScriptTask',
29
+ 'bpmn:ServiceTask',
30
+ ];
31
+ const dataObjectValidSources = [
32
+ 'bpmn:Task',
33
+ 'bpmn:SubProcess',
34
+ 'bpmn:CallActivity',
35
+ 'bpmn:ManualTask',
36
+ 'bpmn:ScriptTask',
37
+ 'bpmn:ServiceTask',
38
+ 'bpmn:IntermediateCatchEvent',
39
+ 'bpmn:StartEvent',
40
+ ];
41
+ const dataObjectValidTargets = [
42
+ 'bpmn:Task',
43
+ 'bpmn:SubProcess',
44
+ 'bpmn:CallActivity',
45
+ 'bpmn:ManualTask',
46
+ 'bpmn:ScriptTask',
47
+ 'bpmn:ServiceTask',
48
+ 'bpmn:IntermediateThrowEvent',
49
+ 'bpmn:EndEvent',
50
+ ];
51
+
52
+ const sourceType = sourceNode.definition.$type;
53
+ const targetType = targetNode.definition.$type;
54
+ const sourceIsDataStore = sourceNode.definition.$type === 'bpmn:DataStoreReference';
55
+ const sourceIsDataObject = sourceNode.definition.$type === 'bpmn:DataObjectReference';
56
+ const targetIsDataStore = targetNode.definition.$type === 'bpmn:DataStoreReference';
57
+ const targetIsDataObject = targetNode.definition.$type === 'bpmn:DataObjectReference';
58
+
59
+ if (sourceIsDataStore && dataStoreValidTargets.includes(targetType)) {
60
+ return true;
61
+ }
62
+ if (sourceIsDataObject && dataObjectValidTargets.includes(targetType)) {
63
+ return true;
64
+ }
65
+ if (targetIsDataStore && dataStoreValidSources.includes(sourceType)) {
66
+ return true;
67
+ }
68
+ return (targetIsDataObject && dataObjectValidSources.includes(sourceType));
17
69
  }
18
70
 
19
71
  makeFlowNode(sourceShape, targetShape, genericLink) {
@@ -86,23 +86,6 @@ export default {
86
86
  },
87
87
  },
88
88
  watch: {
89
- 'node.definition': {
90
- handler() {
91
- const newNameLabel = this.shapeName;
92
-
93
- if (newNameLabel !== this.nameLabel) {
94
- this.nameLabel = newNameLabel;
95
- }
96
- this.setDefaultMarker(this.isDefaultFlow());
97
- },
98
- deep: true,
99
- },
100
- 'node.definition.sourceRef': {
101
- handler() {
102
- this.setDefaultMarker(this.isDefaultFlow());
103
- },
104
- deep: true,
105
- },
106
89
  },
107
90
  methods: {
108
91
  completeLink() {
@@ -10,7 +10,8 @@ import cloneDeep from 'lodash/cloneDeep';
10
10
 
11
11
  export default class Node {
12
12
  static diagramPropertiesToCopy = ['x', 'y', 'width', 'height'];
13
- static definitionPropertiesToNotCopy = ['$type', 'id'];
13
+ static definitionPropertiesToNotCopy = ['$type', 'id', 'dataOutputAssociations'];
14
+ static flowDefinitionPropertiesToNotCopy = ['$type', 'id', 'sourceRef', 'targetRef'];
14
15
  static eventDefinitionPropertiesToNotCopy = ['errorRef', 'messageRef'];
15
16
 
16
17
  type;
@@ -80,6 +81,70 @@ export default class Node {
80
81
  this.diagram.id = diagramId;
81
82
  this.diagram.bpmnElement = this.definition;
82
83
  }
84
+ if (this.definition.loopCharacteristics && this.definition.loopCharacteristics.$type === 'bpmn:StandardLoopCharacteristics') {
85
+ this.definition.loopCharacteristics.set('id', nodeIdGenerator.generate()[0]);
86
+ if (this.definition.loopCharacteristics.loopCondition) {
87
+ this.definition.loopCharacteristics.get('loopCondition').set('id', nodeIdGenerator.generate()[0]);
88
+ }
89
+ }
90
+ let dataInputRef, dataOutputRef;
91
+ if (this.definition.ioSpecification) {
92
+ this.definition.ioSpecification.set('id', nodeIdGenerator.generate()[0]);
93
+ const taskId = this.definition.id;
94
+ if (this.definition.ioSpecification.get('dataInputs')) {
95
+ this.definition.ioSpecification.get('dataInputs').forEach(dataInput => {
96
+ const id = dataInput.get('id');
97
+ if (id.substring(0, this.cloneOf.length) === this.cloneOf) {
98
+ dataInput.set('id', id.replace(this.cloneOf + '_', taskId + '_'));
99
+ dataInputRef = dataInput;
100
+ } else {
101
+ dataInput.set('id', nodeIdGenerator.generate()[0]);
102
+ }
103
+ });
104
+ }
105
+ if (this.definition.ioSpecification.get('dataOutputs')) {
106
+ this.definition.ioSpecification.get('dataOutputs').forEach(dataOutput => {
107
+ const id = dataOutput.get('id');
108
+ if (id.substring(0, this.cloneOf.length) === this.cloneOf) {
109
+ dataOutput.set('id', id.replace(this.cloneOf + '_', taskId + '_'));
110
+ dataOutputRef = dataOutput;
111
+ } else {
112
+ dataOutput.set('id', nodeIdGenerator.generate()[0]);
113
+ }
114
+ });
115
+ }
116
+ if (this.definition.ioSpecification.get('inputSets')) {
117
+ this.definition.ioSpecification.get('inputSets').forEach(inputSet => {
118
+ inputSet.set('id', nodeIdGenerator.generate()[0]);
119
+ });
120
+ }
121
+ if (this.definition.ioSpecification.get('outputSets')) {
122
+ this.definition.ioSpecification.get('outputSets').forEach(outputSet => {
123
+ outputSet.set('id', nodeIdGenerator.generate()[0]);
124
+ });
125
+ }
126
+ }
127
+ if (this.definition.loopCharacteristics && this.definition.loopCharacteristics.$type === 'bpmn:MultiInstanceLoopCharacteristics') {
128
+ this.definition.loopCharacteristics.set('id', nodeIdGenerator.generate()[0]);
129
+ if (this.definition.loopCharacteristics.loopCardinality) {
130
+ this.definition.loopCharacteristics.get('loopCardinality').set('id', nodeIdGenerator.generate()[0]);
131
+ }
132
+ if (this.definition.loopCharacteristics.loopDataInputRef && dataInputRef) {
133
+ this.definition.loopCharacteristics.set('loopDataInputRef', dataInputRef);
134
+ }
135
+ if (this.definition.loopCharacteristics.loopDataOutputRef && dataOutputRef) {
136
+ this.definition.loopCharacteristics.set('loopDataOutputRef', dataOutputRef);
137
+ }
138
+ if (this.definition.loopCharacteristics.inputDataItem) {
139
+ this.definition.loopCharacteristics.get('inputDataItem').set('id', nodeIdGenerator.generate()[0]);
140
+ }
141
+ if (this.definition.loopCharacteristics.outputDataItem) {
142
+ this.definition.loopCharacteristics.get('outputDataItem').set('id', nodeIdGenerator.generate()[0]);
143
+ }
144
+ if (this.definition.loopCharacteristics.completionCondition) {
145
+ this.definition.loopCharacteristics.get('completionCondition').set('id', nodeIdGenerator.generate()[0]);
146
+ }
147
+ }
83
148
  }
84
149
 
85
150
  clone(nodeRegistry, moddle, $t) {
@@ -89,6 +154,8 @@ export default class Node {
89
154
 
90
155
  clonedNode.id = null;
91
156
  clonedNode.pool = this.pool;
157
+ clonedNode.cloneOf = this.id;
158
+
92
159
  Node.diagramPropertiesToCopy.forEach(prop => clonedNode.diagram.bounds[prop] = this.diagram.bounds[prop]);
93
160
  Object.keys(this.definition).filter(key => !Node.definitionPropertiesToNotCopy.includes(key)).forEach(key => {
94
161
  const definition = this.definition.get(key);
@@ -96,7 +163,7 @@ export default class Node {
96
163
  if (key === 'eventDefinitions') {
97
164
  for (var i in clonedDefinition) {
98
165
  if (definition[i].signalRef && !clonedDefinition[i].signalRef) {
99
- clonedDefinition[i].signalRef = { ...definition[i].signalRef };
166
+ clonedDefinition[i].set('signalRef', { ...definition[i].signalRef });
100
167
  }
101
168
  }
102
169
  }
@@ -112,6 +179,43 @@ export default class Node {
112
179
  return clonedNode;
113
180
  }
114
181
 
182
+ cloneFlow(nodeRegistry, moddle, $t) {
183
+ const definition = nodeRegistry[this.type].definition(moddle, $t);
184
+ const diagram = nodeRegistry[this.type].diagram(moddle);
185
+ const clonedFlow = new this.constructor(this.type, definition, diagram);
186
+
187
+ clonedFlow.id = null;
188
+ clonedFlow.pool = this.pool;
189
+ clonedFlow.cloneOf = this.id;
190
+ clonedFlow.diagram.waypoint = [];
191
+
192
+ this.diagram.waypoint.forEach(point => clonedFlow.diagram.waypoint.push(point));
193
+
194
+ Object.keys(this.definition).filter(key => !Node.flowDefinitionPropertiesToNotCopy.includes(key)).forEach(key => {
195
+ const definition = this.definition.get(key);
196
+ const clonedDefinition = typeof definition === 'object' ? cloneDeep(definition) : definition;
197
+ if (key === 'eventDefinitions') {
198
+ for (var i in clonedDefinition) {
199
+ if (definition[i].signalRef && !clonedDefinition[i].signalRef) {
200
+ clonedDefinition[i].set('signalRef', { ...definition[i].signalRef });
201
+ }
202
+ }
203
+ }
204
+ clonedFlow.definition.set(key, clonedDefinition);
205
+ clonedFlow.definition.set('sourceRef', null);
206
+ clonedFlow.definition.set('targetRef', null);
207
+ });
208
+
209
+ Node.eventDefinitionPropertiesToNotCopy.forEach(
210
+ prop => clonedFlow.definition.eventDefinitions &&
211
+ clonedFlow.definition.eventDefinitions[0] &&
212
+ clonedFlow.definition.eventDefinitions[0].hasOwnProperty(prop) &&
213
+ clonedFlow.definition.eventDefinitions[0].set(prop, null)
214
+ );
215
+
216
+ return clonedFlow;
217
+ }
218
+
115
219
  getTargetProcess(processes, processNode) {
116
220
  return this.pool
117
221
  ? processes.find(({ id }) => id === this.pool.component.node.definition.get('processRef').id)
@@ -101,7 +101,7 @@
101
101
  </span>
102
102
  </div>
103
103
  <a
104
- class="btn btn-sm btn-primary mini-map-btn text-uppercase mx-2"
104
+ class="btn btn-sm btn-primary autosave-btn text-uppercase mx-2"
105
105
  data-test="publish-btn"
106
106
  :title="$t('Publish')"
107
107
  @click="$emit('saveBpmn')"
@@ -109,7 +109,7 @@
109
109
  {{ $t('Publish') }}
110
110
  </a>
111
111
  <a
112
- class="btn btn-sm btn-link toolbar-item mini-map-btn text-black text-uppercase"
112
+ class="btn btn-sm btn-link toolbar-item autosave-btn text-black text-uppercase"
113
113
  data-test="close-btn"
114
114
  :title="$t('Close')"
115
115
  @click="$emit('close')"
@@ -117,9 +117,11 @@
117
117
  {{ $t('Close') }}
118
118
  </a>
119
119
  <EllipsisMenu
120
- @navigate="onNavigate"
121
120
  :actions="ellipsisMenuActions"
122
121
  :divider="false"
122
+ @navigate="onNavigate"
123
+ @show="onShow"
124
+ @hide="onHide"
123
125
  />
124
126
  </template>
125
127
  <b-button
@@ -258,6 +260,18 @@ export default {
258
260
  break;
259
261
  }
260
262
  },
263
+ onShow() {
264
+ const inspectorDiv = document.getElementById('inspector');
265
+ if (inspectorDiv) {
266
+ inspectorDiv.style.zIndex = '1';
267
+ }
268
+ },
269
+ onHide() {
270
+ const inspectorDiv = document.getElementById('inspector');
271
+ if (inspectorDiv) {
272
+ inspectorDiv.style.zIndex = '2';
273
+ }
274
+ },
261
275
  },
262
276
  };
263
277
  </script>
@@ -19,3 +19,10 @@ export default {
19
19
  },
20
20
  };
21
21
  </script>
22
+ <style lang="css" scoped>
23
+ @media screen and (max-width: 1365px) {
24
+ #breadcrumbs {
25
+ width: 290px;
26
+ }
27
+ }
28
+ </style>
@@ -27,3 +27,14 @@ $toolbar-background-color: #fff;
27
27
  .cursor-default {
28
28
  cursor: default !important;
29
29
  }
30
+
31
+ .autosave-btn {
32
+ display: flex;
33
+ justify-content: center;
34
+ align-items: center;
35
+ }
36
+
37
+ .btn-ellipsis {
38
+ border-top-left-radius: 4px !important;
39
+ border-bottom-left-radius: 4px !important;
40
+ }
@@ -0,0 +1,145 @@
1
+ import { id as laneId } from '../components/nodes/poolLane/config';
2
+ import { id as sequenceFlowId } from '../components/nodes/sequenceFlow';
3
+ import { id as associationId } from '../components/nodes/association';
4
+ import { id as messageFlowId } from '../components/nodes/messageFlow/config';
5
+ import { id as dataOutputAssociationFlowId } from '../components/nodes/dataOutputAssociation/config';
6
+ import { id as dataInputAssociationFlowId } from '../components/nodes/dataInputAssociation/config';
7
+ import { id as genericFlowId } from '../components/nodes/genericFlow/config';
8
+ import { getOrFindDataInput, findIOSpecificationOwner } from '../components/crown/utils';
9
+
10
+ export default {
11
+ methods: {
12
+ cloneSelection() {
13
+ let clonedNodes = [], clonedFlows = [], clonedDataInputAssociations = [], clonedDataOutputAssociations = [];
14
+ const nodes = this.highlightedNodes;
15
+ const selector = this.$refs.selector.$el;
16
+ const flowNodeTypes = [
17
+ sequenceFlowId,
18
+ laneId,
19
+ associationId,
20
+ messageFlowId,
21
+ genericFlowId,
22
+ ];
23
+ const dataInputAssociationNodeTypes = [
24
+ dataInputAssociationFlowId,
25
+ ];
26
+ const dataOutputAssociationNodeTypes = [
27
+ dataOutputAssociationFlowId,
28
+ ];
29
+
30
+ if (typeof selector.getBoundingClientRect === 'function') {
31
+ nodes.forEach(node => {
32
+ if (flowNodeTypes.includes(node.type)) {
33
+ const clonedFlow = this.cloneFlowAndSetNewId(node);
34
+ clonedFlows.push(clonedFlow);
35
+ clonedNodes.push(clonedFlow);
36
+ } else if (dataInputAssociationNodeTypes.includes(node.type)) {
37
+ const clonedFlow = this.cloneFlowAndSetNewId(node);
38
+ clonedDataInputAssociations.push(clonedFlow);
39
+ clonedNodes.push(clonedFlow);
40
+ } else if (dataOutputAssociationNodeTypes.includes(node.type)) {
41
+ const clonedFlow = this.cloneFlowAndSetNewId(node);
42
+ clonedDataOutputAssociations.push(clonedFlow);
43
+ clonedNodes.push(clonedFlow);
44
+ } else {
45
+ const clonedElement = this.cloneElementAndCalculateOffset(node);
46
+ clonedNodes.push(clonedElement);
47
+ }
48
+ });
49
+ }
50
+
51
+ this.connectClonedFlows(clonedFlows, clonedNodes);
52
+ this.connectClonedDataInputAssociations(clonedDataInputAssociations, clonedNodes);
53
+ this.connectClonedDataOutputAssociations(clonedDataOutputAssociations, clonedNodes);
54
+
55
+ return clonedNodes;
56
+ },
57
+ // Returns the Flow Element (Task| DataStore| DataObject) that is the target of the association
58
+ getDataInputOutputAssociationTargetRef(association) {
59
+ if (association.targetRef.$type === 'bpmn:DataInput') {
60
+ const ioSpec = association.targetRef.$parent;
61
+ return findIOSpecificationOwner(ioSpec, this);
62
+ }
63
+ if (association.targetRef.$type === 'bpmn:DataInputAssociation') {
64
+ const ioSpec = association.targetRef.$parent;
65
+ return findIOSpecificationOwner(ioSpec, this);
66
+ }
67
+ return association.targetRef;
68
+ },
69
+ cloneFlowAndSetNewId(node) {
70
+ const clonedFlow = node.cloneFlow(this.nodeRegistry, this.moddle, this.$t);
71
+ clonedFlow.setIds(this.nodeIdGenerator);
72
+ return clonedFlow;
73
+ },
74
+ cloneElementAndCalculateOffset(node) {
75
+ const clonedElement = node.clone(this.nodeRegistry, this.moddle, this.$t);
76
+ const { height: selectorHeight } = this.$refs.selector.$el.getBoundingClientRect();
77
+ const { sy } = this.paper.scale();
78
+ const yOffset = selectorHeight / sy;
79
+ clonedElement.diagram.bounds.y += yOffset;
80
+ clonedElement.setIds(this.nodeIdGenerator);
81
+ return clonedElement;
82
+ },
83
+ connectClonedFlows(clonedFlows, clonedNodes) {
84
+ clonedFlows.forEach(clonedFlow => {
85
+ const originalFlow = this.nodes.find(node => node.definition.id === clonedFlow.cloneOf);
86
+ const src = originalFlow.definition.sourceRef;
87
+ const target = originalFlow.definition.targetRef;
88
+ const srcClone = clonedNodes.find(node => node.cloneOf === src.id);
89
+ const targetClone = clonedNodes.find(node => node.cloneOf === target.id);
90
+ if (!srcClone || !targetClone) {
91
+ clonedNodes.splice(clonedNodes.indexOf(clonedFlow), 1);
92
+ return;
93
+ }
94
+ clonedFlow.definition.set('sourceRef', srcClone.definition);
95
+ clonedFlow.definition.set('targetRef', targetClone.definition);
96
+
97
+ if (srcClone.definition.outgoing) {
98
+ srcClone.definition.outgoing.push(clonedFlow.definition);
99
+ } else {
100
+ srcClone.definition.set('outgoing', [clonedFlow.definition]);
101
+ }
102
+
103
+ if (targetClone.definition.incoming) {
104
+ targetClone.definition.incoming.push(clonedFlow.definition);
105
+ } else {
106
+ targetClone.definition.set('incoming', [clonedFlow.definition]);
107
+ }
108
+
109
+ clonedFlow.diagram.waypoint.forEach(point => {
110
+ const { height: selectorHeight } = this.$refs.selector.$el.getBoundingClientRect();
111
+ point.y += selectorHeight;
112
+ });
113
+ });
114
+ },
115
+ connectClonedDataInputAssociations(clonedDataInputAssociations, clonedNodes) {
116
+ clonedDataInputAssociations.forEach(clonedAssociation => {
117
+ const originalAssociation = this.nodes.find(node => node.definition.id === clonedAssociation.cloneOf);
118
+ const src = originalAssociation.definition.sourceRef[0];
119
+ const srcClone = clonedNodes.find(node => node.cloneOf === src.id);
120
+ const originalTargetElement = this.getDataInputOutputAssociationTargetRef(originalAssociation.definition);
121
+ const clonedElement = clonedNodes.find(node => node.cloneOf === originalTargetElement.id);
122
+ const clonedDataInput = getOrFindDataInput(this.moddle, clonedElement, srcClone.definition);
123
+
124
+ clonedAssociation.definition.set('sourceRef', [srcClone.definition]);
125
+ clonedAssociation.definition.set('targetRef', clonedDataInput);
126
+ clonedElement.definition.set('dataInputAssociations', [clonedAssociation.definition]);
127
+ });
128
+ },
129
+ connectClonedDataOutputAssociations(clonedDataOutputAssociations, clonedNodes) {
130
+ clonedDataOutputAssociations.forEach(clonedAssociation => {
131
+ const originalAssociation = this.nodes.find(node => node.definition.id === clonedAssociation.cloneOf);
132
+ const src = originalAssociation.definition.sourceRef || originalAssociation.definition.$parent;
133
+ const target = originalAssociation.definition.targetRef;
134
+ const srcClone = clonedNodes.find(node => node.cloneOf === src.id);
135
+ const targetClone = clonedNodes.find(node => node.cloneOf === target.id);
136
+
137
+ clonedAssociation.definition.set('targetRef', targetClone.definition);
138
+ clonedAssociation.definition.set('sourceRef', srcClone.definition);
139
+
140
+ const existingOutputAssociations = srcClone.definition.get('dataOutputAssociations') || [];
141
+ srcClone.definition.set('dataOutputAssociations', [...existingOutputAssociations, clonedAssociation.definition]);
142
+ });
143
+ },
144
+ },
145
+ };
@@ -78,7 +78,7 @@ export default {
78
78
  });
79
79
  },
80
80
  setEndpoint(shape, endpoint, connectionOffset) {
81
- if (isPoint(shape)) {
81
+ if (shape && isPoint(shape)) {
82
82
  return this.shape[endpoint](shape, {
83
83
  anchor: {
84
84
  name: 'modelCenter',
@@ -253,6 +253,9 @@ export default {
253
253
  ? this.getTargetRef()
254
254
  : this.node.definition.get('targetRef');
255
255
 
256
+ // if flow doesn't have a targetRef such as incomplete node, return
257
+ if (!targetRef) return;
258
+
256
259
  if (targetRef.id) {
257
260
  const targetShape = this.graph.getElements().find(element => {
258
261
  return element.component && element.component.node.definition === targetRef;