@processmaker/modeler 1.27.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@processmaker/modeler",
3
- "version": "1.27.0",
3
+ "version": "1.28.0",
4
4
  "scripts": {
5
5
  "serve": "vue-cli-service serve",
6
6
  "open-cypress": "TZ=UTC cypress open",
@@ -1,11 +1,13 @@
1
1
  export default class NodeIdGenerator {
2
2
  static prefix = 'node_';
3
3
 
4
- #counter = 1;
5
- #diagramCounter = 1;
4
+ static #counter = 1;
5
+ static #diagramCounter = 1;
6
6
 
7
7
  constructor(definitions) {
8
8
  this.definitions = definitions;
9
+ this.refreshLastIdCounter();
10
+ this.refreshLastDiagramIdCounter();
9
11
  }
10
12
 
11
13
  findById(id, root = this.definitions.rootElements, walked = []) {
@@ -17,45 +19,76 @@ export default class NodeIdGenerator {
17
19
  } else if (root instanceof Object && root.$type) {
18
20
  walked.push(root);
19
21
  if (root.id === id) return root;
20
- Object.getOwnPropertyNames(root).find(key => found = !(root[key] instanceof Function) && this.findById(id, root[key], walked));
22
+ Object.getOwnPropertyNames(root).find(key => found = !(root[key] instanceof Function) && (key.substring(0, 1) !== '$') && this.findById(id, root[key], walked));
21
23
  }
22
24
  return found;
23
25
  }
24
26
 
25
- generate() {
26
- let definitionId = this.#generateDefinitionId();
27
- let diagramId = this.#generateDiagramId();
28
-
29
- while (!this.#isDefinitionIdUnique(definitionId)) {
30
- definitionId = this.#generateDefinitionId();
27
+ matchIds(idRegex, root, walked = [], lastIdCounter = 0) {
28
+ if (walked.indexOf(root) > -1) return lastIdCounter;
29
+ if (root instanceof Array) {
30
+ walked.push(root);
31
+ root.forEach(item => lastIdCounter = this.matchIds(idRegex, item, walked, lastIdCounter));
32
+ } else if (root instanceof Object) {
33
+ walked.push(root);
34
+ if (root.id) {
35
+ const match = String(root.id).match(idRegex);
36
+ const idCounter = match ? parseInt(match[1]) : 0;
37
+ if (idCounter > lastIdCounter) {
38
+ lastIdCounter = idCounter;
39
+ }
40
+ }
41
+ Object.getOwnPropertyNames(root).forEach(key => {
42
+ if (!(root[key] instanceof Function) && (key.substring(0, 1) !== '$')) {
43
+ lastIdCounter = this.matchIds(idRegex, root[key], walked, lastIdCounter);
44
+ }
45
+ });
31
46
  }
47
+ return lastIdCounter;
48
+ }
32
49
 
33
- while (!this.#isDiagramIdUnique(diagramId)) {
34
- diagramId = this.#generateDiagramId();
35
- }
50
+ refreshLastIdCounter() {
51
+ let lastIdCounter = this.matchIds(new RegExp(`^${NodeIdGenerator.prefix}(\\d+)$`), this.definitions.rootElements);
52
+ NodeIdGenerator.#counter = lastIdCounter + 1;
53
+ }
36
54
 
55
+ getCounter() {
56
+ this.refreshLastIdCounter();
57
+ return NodeIdGenerator.#counter;
58
+ }
59
+
60
+ refreshLastDiagramIdCounter() {
61
+ let lastIdCounter = this.matchIds(new RegExp(`^${NodeIdGenerator.prefix}(\\d+)_di$`), this.definitions.diagrams);
62
+ NodeIdGenerator.#diagramCounter = lastIdCounter + 1;
63
+ }
64
+
65
+ generate() {
66
+ let definitionId = this.#generateDefinitionId();
67
+ let diagramId = this.#generateDiagramId();
37
68
  return [definitionId, diagramId];
38
69
  }
39
70
 
40
71
  #generateDefinitionId = () => {
41
- const id = NodeIdGenerator.prefix + this.#counter;
42
- this.#counter++;
72
+ const id = NodeIdGenerator.prefix + NodeIdGenerator.#counter;
73
+ NodeIdGenerator.#counter++;
43
74
 
44
75
  return id;
45
76
  };
46
77
 
47
78
  #generateDiagramId = () => {
48
- const id = NodeIdGenerator.prefix + this.#diagramCounter + '_di';
49
- this.#diagramCounter++;
79
+ const id = NodeIdGenerator.prefix + NodeIdGenerator.#diagramCounter + '_di';
80
+ NodeIdGenerator.#diagramCounter++;
50
81
 
51
82
  return id;
52
83
  };
84
+ }
53
85
 
54
- #isDefinitionIdUnique = id => {
55
- return !this.findById(id) && !this.findById(id, this.definitions.diagrams);
56
- };
86
+ // Singleton instance
87
+ let singleton = null;
57
88
 
58
- #isDiagramIdUnique = id => {
59
- return !this.findById(id) && !this.findById(id, this.definitions.diagrams);
60
- };
89
+ export function getNodeIdGenerator(definitions) {
90
+ if (!singleton) {
91
+ singleton = new NodeIdGenerator(definitions);
92
+ }
93
+ return singleton;
61
94
  }
@@ -1,4 +1,4 @@
1
- import NodeIdGenerator from './NodeIdGenerator';
1
+ import { getNodeIdGenerator } from './NodeIdGenerator';
2
2
  import omit from 'lodash/omit';
3
3
 
4
4
  export default class NodeInspector {
@@ -6,7 +6,7 @@ export default class NodeInspector {
6
6
  constructor(definitions, options = {}) {
7
7
  this.index = window.NODE_INSPECTOR_FIRST_INDEX || new Date().getTime();
8
8
  this.definitions = definitions;
9
- this.nodeIdGenerator = new NodeIdGenerator(this.definitions);
9
+ this.nodeIdGenerator = getNodeIdGenerator(this.definitions);
10
10
  this.options = Object.assign({
11
11
  prefix: this.nodeIdGenerator.generate()[0],
12
12
  }, options);
@@ -140,7 +140,6 @@ export default {
140
140
  handler() {
141
141
  this.setNodeColor();
142
142
  },
143
- deep: true,
144
143
  },
145
144
  highlightedShapes(shapes, prevShapes) {
146
145
  if (isEqual(shapes, prevShapes)) {
@@ -114,6 +114,7 @@ export function getOrFindDataInput(moddle, task, sourceNode) {
114
114
  inputSets: [],
115
115
  outputSets: [],
116
116
  });
117
+ task.definition.ioSpecification.$parent = task.definition;
117
118
  }
118
119
  // Check if dataInput exists
119
120
  if (!task.definition.ioSpecification.dataInputs) {
@@ -126,6 +127,7 @@ export function getOrFindDataInput(moddle, task, sourceNode) {
126
127
  isCollection: 'false',
127
128
  name: sourceNode.name,
128
129
  }));
130
+ task.definition.ioSpecification.dataInputs[task.definition.ioSpecification.dataInputs.length - 1].$parent = task.definition.ioSpecification;
129
131
  task.definition.ioSpecification.set('dataInputs', task.definition.ioSpecification.dataInputs);
130
132
  }
131
133
  dataInput = task.definition.ioSpecification.dataInputs.find(input => input.id === dataInputId);
@@ -164,13 +166,22 @@ export function getOrFindDataInput(moddle, task, sourceNode) {
164
166
  }
165
167
  inputSet = task.definition.ioSpecification.inputSets[0];
166
168
  // Check if dataInputRef exists
167
- const dataInputRef = inputSet.dataInputRefs.find(ref => ref.id === dataInputId);
169
+ const dataInputRef = inputSet.get('dataInputRefs').find(ref => ref.id === dataInputId);
168
170
  if (!dataInputRef) {
169
171
  inputSet.dataInputRefs.push(dataInput);
170
172
  }
171
173
  return dataInput;
172
174
  }
173
175
 
176
+ export function findIOSpecificationOwner(ioSpec, modeler) {
177
+ const owner = ioSpec.$parent;
178
+ if (!owner) {
179
+ return modeler.nodes.find(node => node.definition.ioSpecification === ioSpec ||
180
+ node.definition.ioSpecification?.id === ioSpec.id
181
+ )?.definition;
182
+ }
183
+ return owner;
184
+ }
174
185
 
175
186
  export function removeDataInput(task, sourceNode) {
176
187
  if (sourceNode.$type !== 'bpmn:DataObjectReference' && sourceNode.$type !== 'bpmn:DataStoreReference') {
@@ -1,13 +1,16 @@
1
1
  export default {
2
2
  methods: {
3
3
  copyPasteHandler(event, options) {
4
- const isCopy = event.key === 'c';
5
- const isPaste = event.key === 'v';
4
+ const node = event.target.nodeName.toLowerCase();
5
+ const isBody = node === 'body';
6
+ const key = event.key.toLowerCase();
7
+ const isCopy = key === 'c';
8
+ const isPaste = key === 'v';
6
9
 
7
- if (isCopy && options.mod) {
10
+ if (isBody && isCopy && options.mod) {
8
11
  this.copy(event);
9
12
  }
10
- if (isPaste && options.mod) {
13
+ if (isBody && isPaste && options.mod) {
11
14
  this.paste(event);
12
15
  }
13
16
  },
@@ -5,6 +5,11 @@ import moveShapeByKeypress from './moveWithArrowKeys';
5
5
 
6
6
  export default {
7
7
  mixins: [ZoomInOut, CopyPaste],
8
+ computed: {
9
+ clientLeftPaper() {
10
+ return store.getters.clientLeftPaper;
11
+ },
12
+ },
8
13
  mounted() {
9
14
  document.addEventListener('keydown', this.keydownListener);
10
15
  document.addEventListener('keyup', this.keyupListener);
@@ -45,7 +50,7 @@ export default {
45
50
  const scale = this.paperManager.scale;
46
51
  this.canvasDragPosition = { x: x * scale.sx, y: y * scale.sy };
47
52
  }
48
- if (this.canvasDragPosition) {
53
+ if (this.canvasDragPosition && !this.clientLeftPaper) {
49
54
  this.paperManager.translate(
50
55
  event.offsetX - this.canvasDragPosition.x,
51
56
  event.offsetY - this.canvasDragPosition.y
@@ -115,6 +115,9 @@ export default {
115
115
  },
116
116
  loopMaximum: 0,
117
117
  loopCondition: null,
118
+ ioSpecification: {
119
+ dataInputs: [],
120
+ },
118
121
  },
119
122
  loopType: null,
120
123
  multiType: null,
@@ -218,7 +221,7 @@ export default {
218
221
  },
219
222
  getLoopDataOutputRef() {
220
223
  if (!this.local.loopCharacteristics || !this.local.loopCharacteristics.loopDataOutputRef) return null;
221
- return this.local.ioSpecification.dataOutputs[0].name;
224
+ return this.local.ioSpecification?.dataOutputs[0].name;
222
225
  },
223
226
  setLoopDataOutputRef(value) {
224
227
  if (!this.local.ioSpecification) {
@@ -252,7 +255,7 @@ export default {
252
255
  },
253
256
  getLoopDataInputRef() {
254
257
  if (!this.local.loopCharacteristics || !this.local.loopCharacteristics.loopDataInputRef) return null;
255
- return this.local.ioSpecification.dataInputs[0].name;
258
+ return this.local.ioSpecification?.dataInputs[0].name;
256
259
  },
257
260
  setLoopDataInputRef(value) {
258
261
  const dataDef = {
@@ -1,6 +1,6 @@
1
1
  import idConfigSettings from './idConfigSettings';
2
2
 
3
- export default {
3
+ const process = {
4
4
  id: 'processmaker-modeler-process',
5
5
  bpmnType: 'bpmn:Process',
6
6
  control: false,
@@ -38,3 +38,7 @@ export default {
38
38
  },
39
39
  ],
40
40
  };
41
+
42
+ export const id = process.id;
43
+
44
+ export default process;
@@ -139,12 +139,13 @@ import InspectorPanel from '@/components/inspectors/InspectorPanel';
139
139
  import undoRedoStore from '@/undoRedoStore';
140
140
  import { Linter } from 'bpmnlint';
141
141
  import linterConfig from '../../../.bpmnlintrc';
142
- import NodeIdGenerator from '../../NodeIdGenerator';
142
+ import { getNodeIdGenerator } from '../../NodeIdGenerator';
143
143
  import Process from '../inspectors/process';
144
144
  import runningInCypressTest from '@/runningInCypressTest';
145
145
  import getValidationProperties from '@/targetValidationUtils';
146
146
  import MiniPaper from '@/components/miniPaper/MiniPaper';
147
147
  import { id as laneId } from '@/components/nodes/poolLane/config';
148
+ import { id as processId } from '@/components/inspectors/process';
148
149
  import { id as sequenceFlowId } from '../nodes/sequenceFlow';
149
150
  import { id as associationId } from '../nodes/association';
150
151
  import { id as messageFlowId } from '../nodes/messageFlow/config';
@@ -167,6 +168,7 @@ import { removeNodeFlows, removeNodeMessageFlows, removeNodeAssociations, remove
167
168
  import { getInvalidNodes } from '@/components/modeler/modelerUtils';
168
169
  import { NodeMigrator } from '@/components/modeler/NodeMigrator';
169
170
  import addLoopCharacteristics from '@/setup/addLoopCharacteristics';
171
+ import cloneSelection from '../../mixins/cloneSelection';
170
172
 
171
173
  import ProcessmakerModelerGenericFlow from '@/components/nodes/genericFlow/genericFlow';
172
174
 
@@ -190,9 +192,10 @@ export default {
190
192
  },
191
193
  },
192
194
  },
193
- mixins: [hotkeys],
195
+ mixins: [hotkeys, cloneSelection],
194
196
  data() {
195
197
  return {
198
+ pasteInProgress: false,
196
199
  internalClipboard: [],
197
200
  tooltipTarget: null,
198
201
 
@@ -329,90 +332,51 @@ export default {
329
332
  dataOutputAssociationFlowId,
330
333
  dataInputAssociationFlowId,
331
334
  genericFlowId,
335
+ processId,
332
336
  ];
333
337
  if (this.highlightedNodes.length === 1 && flows.includes(this.highlightedNodes[0].type)) return;
334
338
  store.commit('setCopiedElements', this.cloneSelection());
335
339
  this.$bvToast.toast(this.$t('Object(s) have been copied'), { noCloseButton:true, variant: 'success', solid: true, toaster: 'b-toaster-top-center' });
336
340
  },
337
341
  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());
342
+ if (this.copiedElements && !this.pasteInProgress) {
343
+ this.pasteInProgress = true;
344
+ try {
345
+ await this.addClonedNodes(this.copiedElements);
346
+ await this.$nextTick();
347
+ await this.paperManager.awaitScheduledUpdates();
348
+ await this.$refs.selector.selectElements(this.findViewElementsFromNodes(this.copiedElements), true);
349
+ await this.$nextTick();
350
+ await store.commit('setCopiedElements', this.cloneSelection());
351
+ this.scrollToSelection();
352
+ } finally {
353
+ this.pasteInProgress = false;
354
+ }
342
355
  }
343
356
  },
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
- });
389
- }
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
- },
412
357
  async duplicateSelection() {
413
358
  const clonedNodes = this.cloneSelection();
359
+ if (clonedNodes && clonedNodes.length === 0) {
360
+ return;
361
+ }
362
+ this.$refs.selector.clearSelection();
414
363
  await this.addClonedNodes(clonedNodes);
415
- this.$refs.selector.selectElements(this.findViewElementsFromNodes(clonedNodes));
364
+ await this.$nextTick();
365
+ await this.paperManager.awaitScheduledUpdates();
366
+ await this.$refs.selector.selectElements(this.findViewElementsFromNodes(clonedNodes));
367
+ this.scrollToSelection();
368
+ },
369
+ scrollToSelection() {
370
+ const containerRect = this.$refs['paper-container'].getBoundingClientRect();
371
+ const selector = this.$refs.selector;
372
+ const selectorRect = selector.$el.getBoundingClientRect();
373
+ // Scroll to the cloned elements only when they are not visible on the screen.
374
+ if (selectorRect.right > containerRect.right || selectorRect.bottom > containerRect.bottom || selectorRect.left < containerRect.left || selectorRect.top < containerRect.top) {
375
+ const currentPosition = this.paper.translate();
376
+ const newTy = currentPosition.ty - (selectorRect.top - containerRect.top - selectorRect.height);
377
+ this.paper.translate(currentPosition.tx, newTy);
378
+ selector.updateSelectionBox(true);
379
+ }
416
380
  },
417
381
  findViewElementsFromNodes(nodes) {
418
382
  return nodes.map(node => {
@@ -832,7 +796,7 @@ export default {
832
796
  async loadXML(xml = this.currentXML) {
833
797
  this.definitions = await this.xmlManager.getDefinitionsFromXml(xml);
834
798
  this.xmlManager.definitions = this.definitions;
835
- this.nodeIdGenerator = new NodeIdGenerator(this.definitions);
799
+ this.nodeIdGenerator = getNodeIdGenerator(this.definitions);
836
800
  store.commit('clearNodes');
837
801
  this.renderPaper();
838
802
  },
@@ -974,7 +938,12 @@ export default {
974
938
  await this.pushToUndoStack();
975
939
  },
976
940
  async removeNode(node, { removeRelationships = true } = {}) {
941
+ if (!node) {
942
+ // already removed
943
+ return;
944
+ }
977
945
  if (removeRelationships) {
946
+
978
947
  removeNodeFlows(node, this);
979
948
  removeNodeMessageFlows(node, this);
980
949
  removeNodeAssociations(node, this);
@@ -1274,14 +1243,17 @@ export default {
1274
1243
  this.pointerUpHandler(event, cellView);
1275
1244
  }, this);
1276
1245
 
1246
+ this.$el.addEventListener('mouseenter', () => {
1247
+ store.commit('setClientLeftPaper', false);
1248
+ });
1249
+
1277
1250
  this.$el.addEventListener('mousemove', event => {
1278
- const { clientX, clientY } = event;
1279
1251
  this.pointerMoveHandler(event);
1280
- store.commit('setClientMousePosition', { clientX, clientY });
1281
1252
  });
1282
1253
 
1283
1254
  this.$el.addEventListener('mouseleave', () => {
1284
- store.commit('clientLeftPaper');
1255
+ this.paperManager.removeEventHandler('blank:pointermove');
1256
+ store.commit('setClientLeftPaper', true);
1285
1257
  });
1286
1258
 
1287
1259
  this.paperManager.addEventHandler('cell:pointerclick', (cellView, evt, x, y) => {
@@ -29,13 +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
- 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
- ];
32
+
39
33
  export default {
40
34
  name: 'Selection',
41
35
  components: {
@@ -351,24 +345,6 @@ export default {
351
345
  }
352
346
  return true;
353
347
  });
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
348
  },
373
349
  /**
374
350
  * Pan paper handler
@@ -475,6 +451,9 @@ export default {
475
451
  this.$emit('save-state');
476
452
  this.dragging = false;
477
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();
478
457
  },
479
458
  /**
480
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
  },