@processmaker/modeler 1.27.0 → 1.29.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 (32) hide show
  1. package/dist/modeler.common.js +1519 -822
  2. package/dist/modeler.common.js.map +1 -1
  3. package/dist/modeler.umd.js +1519 -822
  4. package/dist/modeler.umd.js.map +1 -1
  5. package/dist/modeler.umd.min.js +3 -3
  6. package/dist/modeler.umd.min.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/NodeIdGenerator.js +55 -22
  9. package/src/NodeInspector.js +2 -2
  10. package/src/components/crown/crownButtons/{duplicateButton.vue → cloneButton.vue} +10 -9
  11. package/src/components/crown/crownConfig/crownConfig.vue +3 -4
  12. package/src/components/crown/crownMultiselect/crownMultiselect.vue +20 -7
  13. package/src/components/crown/utils.js +12 -1
  14. package/src/components/hotkeys/copyPaste.js +7 -4
  15. package/src/components/hotkeys/main.js +6 -1
  16. package/src/components/inspectors/DocumentationFormTextArea.vue +6 -2
  17. package/src/components/inspectors/LoopCharacteristics.vue +5 -2
  18. package/src/components/inspectors/process.js +5 -1
  19. package/src/components/modeler/Modeler.vue +92 -99
  20. package/src/components/modeler/Selection.vue +40 -29
  21. package/src/components/nodes/association/index.js +3 -0
  22. package/src/components/nodes/dataInputAssociation/dataInputAssociation.vue +36 -26
  23. package/src/components/nodes/genericFlow/DataOutputAssociation.js +54 -2
  24. package/src/components/nodes/genericFlow/genericFlow.vue +0 -17
  25. package/src/components/nodes/node.js +81 -8
  26. package/src/components/nodes/pool/poolEventHandlers.js +0 -9
  27. package/src/components/toolbar/ToolBar.vue +21 -9
  28. package/src/components/toolbar/toolbar.scss +2 -1
  29. package/src/mixins/cloneSelection.js +153 -0
  30. package/src/mixins/linkConfig.js +4 -1
  31. package/src/store.js +7 -11
  32. package/src/undoRedoStore.js +4 -19
@@ -11,6 +11,7 @@
11
11
  @toggle-panels-compressed="panelsCompressed = !panelsCompressed"
12
12
  @toggle-mini-map-open="miniMapOpen = $event"
13
13
  @saveBpmn="saveBpmn"
14
+ @publishTemplate="publishTemplate"
14
15
  @close="close"
15
16
  @save-state="pushToUndoStack"
16
17
  @clearSelection="clearSelection"
@@ -63,7 +64,7 @@
63
64
  :parent-height="parentHeight"
64
65
  :canvas-drag-position="canvasDragPosition"
65
66
  :compressed="panelsCompressed && noElementsSelected"
66
- @shape-resize="shapeResize"
67
+ @shape-resize="shapeResize(false)"
67
68
  />
68
69
 
69
70
  <component
@@ -105,8 +106,8 @@
105
106
  @copy-element="copyElement"
106
107
  @copy-selection="copyElement"
107
108
  @paste-element="pasteElements"
108
- @duplicate-element="duplicateElement"
109
- @duplicate-selection="duplicateSelection"
109
+ @clone-element="cloneElement"
110
+ @clone-selection="cloneSelection"
110
111
  @default-flow="toggleDefaultFlow"
111
112
  @shape-resize="shapeResize"
112
113
  />
@@ -116,7 +117,7 @@
116
117
  :graph="graph"
117
118
  :paperManager="paperManager"
118
119
  :useModelGeometry="false"
119
- @duplicate-selection="duplicateSelection"
120
+ @clone-selection="cloneSelection"
120
121
  @remove-nodes="removeNodes"
121
122
  :processNode="processNode"
122
123
  @save-state="pushToUndoStack"
@@ -139,12 +140,13 @@ import InspectorPanel from '@/components/inspectors/InspectorPanel';
139
140
  import undoRedoStore from '@/undoRedoStore';
140
141
  import { Linter } from 'bpmnlint';
141
142
  import linterConfig from '../../../.bpmnlintrc';
142
- import NodeIdGenerator from '../../NodeIdGenerator';
143
+ import { getNodeIdGenerator } from '../../NodeIdGenerator';
143
144
  import Process from '../inspectors/process';
144
145
  import runningInCypressTest from '@/runningInCypressTest';
145
146
  import getValidationProperties from '@/targetValidationUtils';
146
147
  import MiniPaper from '@/components/miniPaper/MiniPaper';
147
148
  import { id as laneId } from '@/components/nodes/poolLane/config';
149
+ import { id as processId } from '@/components/inspectors/process';
148
150
  import { id as sequenceFlowId } from '../nodes/sequenceFlow';
149
151
  import { id as associationId } from '../nodes/association';
150
152
  import { id as messageFlowId } from '../nodes/messageFlow/config';
@@ -167,6 +169,7 @@ import { removeNodeFlows, removeNodeMessageFlows, removeNodeAssociations, remove
167
169
  import { getInvalidNodes } from '@/components/modeler/modelerUtils';
168
170
  import { NodeMigrator } from '@/components/modeler/NodeMigrator';
169
171
  import addLoopCharacteristics from '@/setup/addLoopCharacteristics';
172
+ import cloneSelection from '../../mixins/cloneSelection';
170
173
 
171
174
  import ProcessmakerModelerGenericFlow from '@/components/nodes/genericFlow/genericFlow';
172
175
 
@@ -190,9 +193,11 @@ export default {
190
193
  },
191
194
  },
192
195
  },
193
- mixins: [hotkeys],
196
+ mixins: [hotkeys, cloneSelection],
194
197
  data() {
195
198
  return {
199
+ pasteInProgress: false,
200
+ cloneInProgress: false,
196
201
  internalClipboard: [],
197
202
  tooltipTarget: null,
198
203
 
@@ -303,10 +308,10 @@ export default {
303
308
  isAppleOS() {
304
309
  return typeof navigator !== 'undefined' && /Mac|iPad|iPhone/.test(navigator.platform);
305
310
  },
306
- async shapeResize() {
311
+ async shapeResize(clearIfEmpty=true) {
307
312
  await this.$nextTick();
308
313
  await this.paperManager.awaitScheduledUpdates();
309
- this.$refs.selector.updateSelectionBox(true);
314
+ this.$refs.selector.updateSelectionBox(true, clearIfEmpty);
310
315
  },
311
316
  toggleDefaultFlow(flow) {
312
317
  const source = flow.definition.sourceRef;
@@ -315,7 +320,7 @@ export default {
315
320
  }
316
321
  source.set('default', flow);
317
322
  },
318
- duplicateElement(node, copyCount) {
323
+ cloneElement(node, copyCount) {
319
324
  const clonedNode = node.clone(this.nodeRegistry, this.moddle, this.$t);
320
325
  const yOffset = (node.diagram.bounds.height + 30) * copyCount;
321
326
 
@@ -329,90 +334,61 @@ export default {
329
334
  dataOutputAssociationFlowId,
330
335
  dataInputAssociationFlowId,
331
336
  genericFlowId,
337
+ processId,
332
338
  ];
333
339
  if (this.highlightedNodes.length === 1 && flows.includes(this.highlightedNodes[0].type)) return;
334
- store.commit('setCopiedElements', this.cloneSelection());
340
+ store.commit('setCopiedElements', this.cloneNodesSelection());
335
341
  this.$bvToast.toast(this.$t('Object(s) have been copied'), { noCloseButton:true, variant: 'success', solid: true, toaster: 'b-toaster-top-center' });
336
342
  },
343
+ publishTemplate() {
344
+ this.$emit('publishTemplate');
345
+ },
337
346
  async pasteElements() {
338
- if (this.copiedElements) {
339
- await this.addClonedNodes(this.copiedElements);
340
- this.$refs.selector.selectElements(this.findViewElementsFromNodes(this.copiedElements));
341
- store.commit('setCopiedElements', this.cloneSelection());
347
+ if (this.copiedElements.length > 0 && !this.pasteInProgress) {
348
+ this.pasteInProgress = true;
349
+ try {
350
+ await this.addClonedNodes(this.copiedElements);
351
+ await this.$nextTick();
352
+ await this.paperManager.awaitScheduledUpdates();
353
+ await this.$refs.selector.selectElements(this.findViewElementsFromNodes(this.copiedElements), true);
354
+ await this.$nextTick();
355
+ await store.commit('setCopiedElements', this.cloneNodesSelection());
356
+ this.scrollToSelection();
357
+ } finally {
358
+ this.pasteInProgress = false;
359
+ await this.pushToUndoStack();
360
+ }
342
361
  }
343
362
  },
344
- cloneSelection() {
345
- let clonedNodes = [], clonedFlows = [], originalFlows = [];
346
- const nodes = this.highlightedNodes;
347
- const selector = this.$refs.selector.$el;
348
- const { height: sheight } = selector.getBoundingClientRect();
349
- if (typeof selector.getBoundingClientRect === 'function') {
350
- // get selector height
351
- nodes.forEach(node => {
352
- // Add flows described in the definitions property
353
- if (node.definition.incoming || node.definition.outgoing) {
354
- // Since both incoming and outgoing reference the same flow, any of them is copied
355
- let flowsToCopy = [...(node.definition.incoming || node.definition.outgoing)];
356
- // Check if flow is already in array before pushing
357
- flowsToCopy.forEach(flow => {
358
- if (!originalFlows.some(el => el.id === flow.id)) {
359
- originalFlows.push(flow);
360
- }
361
- });
362
- }
363
-
364
- // Check node type to clone
365
- if ([
366
- sequenceFlowId,
367
- laneId,
368
- associationId,
369
- messageFlowId,
370
- dataOutputAssociationFlowId,
371
- dataInputAssociationFlowId,
372
- genericFlowId,
373
- ].includes(node.type)) {
374
- // Add offset for all waypoints on cloned flow
375
- const clonedFlow = node.cloneFlow(this.nodeRegistry, this.moddle, this.$t);
376
- clonedFlow.setIds(this.nodeIdGenerator);
377
- clonedFlows.push(clonedFlow);
378
- clonedNodes.push(clonedFlow);
379
- } else {
380
- // Clone node and calculate offset
381
- const clonedNode = node.clone(this.nodeRegistry, this.moddle, this.$t);
382
- const yOffset = sheight;
383
- clonedNode.diagram.bounds.y += yOffset;
384
- // Set cloned node id
385
- clonedNode.setIds(this.nodeIdGenerator);
386
- clonedNodes.push(clonedNode);
387
- }
388
- });
363
+ async cloneSelection() {
364
+ const clonedNodes = this.cloneNodesSelection();
365
+ if (clonedNodes && clonedNodes.length === 0) {
366
+ return;
367
+ }
368
+ try {
369
+ this.cloneInProgress = true;
370
+ this.$refs.selector.clearSelection();
371
+ await this.addClonedNodes(clonedNodes);
372
+ await this.$nextTick();
373
+ await this.paperManager.awaitScheduledUpdates();
374
+ await this.$refs.selector.selectElements(this.findViewElementsFromNodes(clonedNodes));
375
+ this.scrollToSelection();
376
+ } finally {
377
+ this.cloneInProgress = false;
378
+ await this.pushToUndoStack();
389
379
  }
390
- // Connect flows
391
- clonedFlows.forEach(flow => {
392
- // Look up the original flow
393
- const flowClonedFrom = { definition: originalFlows.find(el => el.id === flow.definition.cloneOf) };
394
- // Get the id's of the sourceRef and targetRef of original flow
395
- const src = flowClonedFrom.definition.sourceRef;
396
- const target = flowClonedFrom.definition.targetRef;
397
- const srcClone = clonedNodes.find(node => node.definition.cloneOf === src.id);
398
- const targetClone = clonedNodes.find(node => node.definition.cloneOf === target.id);
399
- // Reference the elements to the flow that connects them
400
- flow.definition.sourceRef = srcClone.definition;
401
- flow.definition.targetRef = targetClone.definition;
402
- // Reference the flow to the elements that are connected by it
403
- srcClone.definition.outgoing ? srcClone.definition.outgoing.push(flow.definition) : srcClone.definition.outgoing = [flow.definition];
404
- targetClone.definition.incoming ? targetClone.definition.incoming.push(flow.definition) : targetClone.definition.incoming = [flow.definition];
405
- // Translate flow waypoints to where they should be
406
- flow.diagram.waypoint.forEach(point => {
407
- point.y += sheight;
408
- });
409
- });
410
- return clonedNodes;
411
380
  },
412
- async duplicateSelection() {
413
- const clonedNodes = this.cloneSelection();
414
- await this.addClonedNodes(clonedNodes);
415
- this.$refs.selector.selectElements(this.findViewElementsFromNodes(clonedNodes));
381
+ scrollToSelection() {
382
+ const containerRect = this.$refs['paper-container'].getBoundingClientRect();
383
+ const selector = this.$refs.selector;
384
+ const selectorRect = selector.$el.getBoundingClientRect();
385
+ // Scroll to the cloned elements only when they are not visible on the screen.
386
+ if (selectorRect.right > containerRect.right || selectorRect.bottom > containerRect.bottom || selectorRect.left < containerRect.left || selectorRect.top < containerRect.top) {
387
+ const currentPosition = this.paper.translate();
388
+ const newTy = currentPosition.ty - (selectorRect.top - containerRect.top - selectorRect.height);
389
+ this.paper.translate(currentPosition.tx, newTy);
390
+ selector.updateSelectionBox(true);
391
+ }
416
392
  },
417
393
  findViewElementsFromNodes(nodes) {
418
394
  return nodes.map(node => {
@@ -476,9 +452,12 @@ export default {
476
452
  }
477
453
  },
478
454
  async pushToUndoStack() {
455
+ if (this.pasteInProgress || this.cloneInProgress) {
456
+ return;
457
+ }
479
458
  try {
480
459
  const xml = await this.getXmlFromDiagram();
481
- undoRedoStore.dispatch('pushState', xml);
460
+ await undoRedoStore.dispatch('pushState', xml);
482
461
  window.ProcessMaker.EventBus.$emit('modeler-change');
483
462
  } catch (invalidXml) {
484
463
  // eslint-disable-next-line no-console
@@ -829,12 +808,20 @@ export default {
829
808
  this.isRendering = false;
830
809
  this.$emit('parsed');
831
810
  },
832
- async loadXML(xml = this.currentXML) {
811
+ async loadXML(xml = null) {
812
+ let emitChangeEvent = false;
813
+ if (xml === null) {
814
+ xml = this.currentXML;
815
+ emitChangeEvent = true;
816
+ }
833
817
  this.definitions = await this.xmlManager.getDefinitionsFromXml(xml);
834
818
  this.xmlManager.definitions = this.definitions;
835
- this.nodeIdGenerator = new NodeIdGenerator(this.definitions);
819
+ this.nodeIdGenerator = getNodeIdGenerator(this.definitions);
836
820
  store.commit('clearNodes');
837
- this.renderPaper();
821
+ await this.renderPaper();
822
+ if (emitChangeEvent) {
823
+ window.ProcessMaker.EventBus.$emit('modeler-change');
824
+ }
838
825
  },
839
826
  getBoundaryEvents(process) {
840
827
  return process.get('flowElements').filter(({ $type }) => $type === 'bpmn:BoundaryEvent');
@@ -970,11 +957,14 @@ export default {
970
957
  store.commit('addNode', node);
971
958
  this.poolTarget = null;
972
959
  });
973
-
974
- await this.pushToUndoStack();
975
960
  },
976
961
  async removeNode(node, { removeRelationships = true } = {}) {
962
+ if (!node) {
963
+ // already removed
964
+ return;
965
+ }
977
966
  if (removeRelationships) {
967
+
978
968
  removeNodeFlows(node, this);
979
969
  removeNodeMessageFlows(node, this);
980
970
  removeNodeAssociations(node, this);
@@ -989,10 +979,10 @@ export default {
989
979
  store.commit('highlightNode', this.processNode);
990
980
  this.$refs.selector.clearSelection();
991
981
  await this.$nextTick();
992
- this.pushToUndoStack();
982
+ await this.pushToUndoStack();
993
983
  },
994
984
  async removeNodes() {
995
- this.performSingleUndoRedoTransaction(async() => {
985
+ await this.performSingleUndoRedoTransaction(async() => {
996
986
  await this.paperManager.performAtomicAction(async() => {
997
987
  const waitPromises = [];
998
988
  this.highlightedNodes.forEach((node) =>
@@ -1035,7 +1025,7 @@ export default {
1035
1025
  undoRedoStore.commit('disableSavingState');
1036
1026
  await cb();
1037
1027
  undoRedoStore.commit('enableSavingState');
1038
- this.pushToUndoStack();
1028
+ await this.pushToUndoStack();
1039
1029
  },
1040
1030
  removeNodesFromLane(node) {
1041
1031
  const containingLane = node.pool && node.pool.component.laneSet &&
@@ -1274,14 +1264,17 @@ export default {
1274
1264
  this.pointerUpHandler(event, cellView);
1275
1265
  }, this);
1276
1266
 
1267
+ this.$el.addEventListener('mouseenter', () => {
1268
+ store.commit('setClientLeftPaper', false);
1269
+ });
1270
+
1277
1271
  this.$el.addEventListener('mousemove', event => {
1278
- const { clientX, clientY } = event;
1279
1272
  this.pointerMoveHandler(event);
1280
- store.commit('setClientMousePosition', { clientX, clientY });
1281
1273
  });
1282
1274
 
1283
1275
  this.$el.addEventListener('mouseleave', () => {
1284
- store.commit('clientLeftPaper');
1276
+ this.paperManager.removeEventHandler('blank:pointermove');
1277
+ store.commit('setClientLeftPaper', true);
1285
1278
  });
1286
1279
 
1287
1280
  this.paperManager.addEventHandler('cell:pointerclick', (cellView, evt, x, y) => {
@@ -1311,9 +1304,9 @@ export default {
1311
1304
 
1312
1305
  /* Register custom nodes */
1313
1306
  window.ProcessMaker.EventBus.$emit('modeler-start', {
1314
- loadXML: xml => {
1315
- this.loadXML(xml);
1316
- undoRedoStore.dispatch('pushState', xml);
1307
+ loadXML: async(xml) => {
1308
+ await this.loadXML(xml);
1309
+ await undoRedoStore.dispatch('pushState', xml);
1317
1310
  },
1318
1311
  addWarnings: warnings => this.$emit('warnings', warnings),
1319
1312
  addBreadcrumbs: breadcrumbs => this.breadcrumbData.push(breadcrumbs),
@@ -16,6 +16,7 @@
16
16
  :plane-elements="$parent.planeElements"
17
17
  :is-rendering="$parent.isRendering"
18
18
  :dropdown-data="[]"
19
+ :has-pools="hasPoolsOrLanesSelected"
19
20
  v-on="$listeners"
20
21
  />
21
22
  </div>
@@ -28,14 +29,13 @@ import CrownMultiselect from '@/components/crown/crownMultiselect/crownMultisele
28
29
  import { id as poolId } from '@/components/nodes/pool/config';
29
30
  import { id as laneId } from '@/components/nodes/poolLane/config';
30
31
  import { id as genericFlowId } from '@/components/nodes/genericFlow/config';
32
+ import { id as sequenceFlowId } from '@/components/nodes/sequenceFlow';
33
+ import { id as associationId } from '@/components/nodes/association';
34
+ import { id as messageFlowId } from '@/components/nodes/messageFlow/config';
35
+ import { id as dataOutputAssociationFlowId } from '@/components/nodes/dataOutputAssociation/config';
36
+ import { id as dataInputAssociationFlowId } from '@/components/nodes/dataInputAssociation/config';
31
37
  import { labelWidth, poolPadding } from '../nodes/pool/poolSizes';
32
- const boundaryElements = [
33
- 'processmaker-modeler-boundary-timer-event',
34
- 'processmaker-modeler-boundary-error-event',
35
- 'processmaker-modeler-boundary-signal-event',
36
- 'processmaker-modeler-boundary-conditional-event',
37
- 'processmaker-modeler-boundary-message-event',
38
- ];
38
+
39
39
  export default {
40
40
  name: 'Selection',
41
41
  components: {
@@ -85,6 +85,14 @@ export default {
85
85
  this.paperManager.paper.on('scale:changed ', this.updateSelectionBox);
86
86
  this.paperManager.paper.on('translate:changed ', this.translateChanged);
87
87
  },
88
+ computed: {
89
+ hasPoolsOrLanesSelected() {
90
+ return this.selected.some((view) => {
91
+ return view.model.component.node.type === poolId ||
92
+ view.model.component.node.type === laneId;
93
+ });
94
+ },
95
+ },
88
96
  watch: {
89
97
  // whenever selected changes
90
98
  selected(newSelected) {
@@ -318,7 +326,7 @@ export default {
318
326
  /**
319
327
  * Update the selection Box
320
328
  */
321
- updateSelectionBox(force=false) {
329
+ updateSelectionBox(force=false, clearIfEmpty=true) {
322
330
  if (force || this.isSelecting && this.style) {
323
331
  if (this.selected.length > 0) {
324
332
  const box = this.getSelectionVertex(this.selected, false, true);
@@ -330,7 +338,7 @@ export default {
330
338
  // Set the dimensions of the element
331
339
  this.style.width = `${box.maxX - box.minX}px`;
332
340
  this.style.height = `${box.maxY - box.minY}px`;
333
- } else {
341
+ } else if (clearIfEmpty) {
334
342
  this.clearSelection();
335
343
  }
336
344
  }
@@ -339,6 +347,13 @@ export default {
339
347
  * Filter the selected elements
340
348
  */
341
349
  filterSelected() {
350
+ const flowTypes = [
351
+ sequenceFlowId,
352
+ dataOutputAssociationFlowId,
353
+ dataInputAssociationFlowId,
354
+ associationId,
355
+ messageFlowId,
356
+ ];
342
357
  // Get the selected pools IDs
343
358
  const selectedPoolsIds = this.selected
344
359
  .filter(shape => shape.model.component)
@@ -349,26 +364,15 @@ export default {
349
364
  if (shape.model.component && shape.model.component.node.pool) {
350
365
  return shape.model.component.node.pool && !selectedPoolsIds.includes(shape.model.component.node.pool.component.node.id);
351
366
  }
367
+ // remove from selection the selected flows that belongs to a selected pools
368
+ if (shape.model.component && flowTypes.includes(shape.model.component.node.type)) {
369
+ const parent = shape.model.getParentCell();
370
+ if (parent.component && parent.component.node.pool) {
371
+ return !selectedPoolsIds.includes(parent.component.node.pool.component.node.id);
372
+ }
373
+ }
352
374
  return true;
353
375
  });
354
- // A boundary event could only be selected alone
355
- const firstSelectedBoundary = this.selected.find(shape => {
356
- return shape.model.component &&
357
- boundaryElements.includes(shape.model.component.node.type);
358
- });
359
- const firstSelectedElement = this.selected[0];
360
- if (firstSelectedBoundary) {
361
- this.selected = this.selected.filter(shape => {
362
- if (firstSelectedElement === firstSelectedBoundary) {
363
- // boundary event selected alone
364
- return shape.model.component &&
365
- shape === firstSelectedBoundary;
366
- }
367
- // do not allow to select a boundary event with another element
368
- return shape.model.component &&
369
- !boundaryElements.includes(shape.model.component.node.type);
370
- });
371
- }
372
376
  },
373
377
  /**
374
378
  * Pan paper handler
@@ -475,6 +479,9 @@ export default {
475
479
  this.$emit('save-state');
476
480
  this.dragging = false;
477
481
  this.stopForceMove = false;
482
+ // Readjusts the selection box, taking into consideration elements
483
+ // that are anchored and did not move, such as boundary events.
484
+ this.updateSelectionBox();
478
485
  },
479
486
  /**
480
487
  * Translate the Selected shapes adding some custom validations
@@ -636,7 +643,7 @@ export default {
636
643
  /**
637
644
  * Rollback drag an element outside it's pool parent
638
645
  */
639
- rollbackSelection(){
646
+ async rollbackSelection(){
640
647
  const deltaX = this.initialPosition.left - this.left;
641
648
  const deltaY = this.initialPosition.top - this.top;
642
649
  this.style.left = `${this.initialPosition.left}px`;
@@ -645,14 +652,18 @@ export default {
645
652
  const shapesToNotTranslate = [
646
653
  'PoolLane',
647
654
  'standard.Link',
655
+ 'processmaker.components.nodes.boundaryEvent.Shape',
648
656
  ];
649
657
  this.selected.filter(shape => !shapesToNotTranslate.includes(shape.model.get('type')))
650
658
  .forEach(shape => {
651
659
  shape.model.translate(deltaX/scale.sx, deltaY/scale.sy);
652
660
  });
653
661
  this.isOutOfThePool = false;
654
- store.commit('allowSavingElementPosition');
662
+ await store.commit('allowSavingElementPosition');
655
663
  this.paperManager.setStateValid();
664
+ await this.$nextTick();
665
+ await this.paperManager.awaitScheduledUpdates();
666
+ this.updateSelectionBox(true);
656
667
  },
657
668
  /**
658
669
  * Expand and fit the pool container
@@ -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) {