@processmaker/modeler 1.39.18 → 1.39.19

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.39.18",
3
+ "version": "1.39.19",
4
4
  "scripts": {
5
5
  "serve": "vue-cli-service serve --mode development",
6
6
  "test:unit": "vue-cli-service test:unit",
@@ -189,7 +189,7 @@ import getValidationProperties from '@/targetValidationUtils';
189
189
  import { id as laneId } from '@/components/nodes/poolLane/config';
190
190
  import { id as processId } from '@/components/inspectors/process';
191
191
  import { id as sequenceFlowId } from '../nodes/sequenceFlow';
192
- import { id as associationId } from '../nodes/association';
192
+ import { id as associationId } from '../nodes/association/associationConfig';
193
193
  import { id as messageFlowId } from '../nodes/messageFlow/config';
194
194
  import { id as dataOutputAssociationFlowId } from '../nodes/dataOutputAssociation/config';
195
195
  import { id as dataInputAssociationFlowId } from '../nodes/dataInputAssociation/config';
@@ -332,6 +332,13 @@ export default {
332
332
  previewConfigs: [],
333
333
  multiplayer: null,
334
334
  isMultiplayer: false,
335
+ flowTypes: [
336
+ 'processmaker-modeler-sequence-flow',
337
+ 'processmaker-modeler-message-flow',
338
+ 'processmaker-modeler-data-input-association',
339
+ 'processmaker-modeler-data-output-association',
340
+ 'processmaker-modeler-association',
341
+ ],
335
342
  };
336
343
  },
337
344
  watch: {
@@ -1052,7 +1059,7 @@ export default {
1052
1059
  this.validateBpmnDiagram();
1053
1060
  }
1054
1061
  },
1055
-
1062
+
1056
1063
  async handleDrop(data) {
1057
1064
  const { clientX, clientY, control, nodeThatWillBeReplaced } = data;
1058
1065
  this.validateDropTarget({ clientX, clientY, control });
@@ -1112,17 +1119,15 @@ export default {
1112
1119
  'processmaker-modeler-sequence-flow',
1113
1120
  'processmaker-modeler-association',
1114
1121
  'processmaker-modeler-data-input-association',
1115
- 'processmaker-modeler-data-input-association',
1116
- ];
1117
- const flowTypes = [
1118
- 'processmaker-modeler-sequence-flow',
1119
- 'processmaker-modeler-message-flow',
1122
+ 'processmaker-modeler-data-output-association',
1120
1123
  ];
1124
+
1121
1125
  if (!this.isMultiplayer) {
1122
1126
  return;
1123
1127
  }
1128
+
1124
1129
  if (!fromClient) {
1125
- if (!blackList.includes(node.type) && !flowTypes.includes(node.type)) {
1130
+ if (!blackList.includes(node.type) && !this.flowTypes.includes(node.type)) {
1126
1131
  const defaultData = {
1127
1132
  x: node.diagram.bounds.x,
1128
1133
  y: node.diagram.bounds.y,
@@ -1137,14 +1142,24 @@ export default {
1137
1142
  }
1138
1143
  window.ProcessMaker.EventBus.$emit('multiplayer-addNode', defaultData);
1139
1144
  }
1140
- if (flowTypes.includes(node.type)) {
1141
- window.ProcessMaker.EventBus.$emit('multiplayer-addFlow', {
1142
- id: node.definition.id,
1143
- type: node.type,
1144
- sourceRefId: node.definition.sourceRef.id,
1145
- targetRefId: node.definition.targetRef.id,
1146
- waypoint: node.diagram.waypoint,
1147
- });
1145
+ if (this.flowTypes.includes(node.type)) {
1146
+ let sourceRefId = node.definition.sourceRef?.id;
1147
+ let targetRefId = node.definition.targetRef?.id;
1148
+
1149
+ if (node.type === 'processmaker-modeler-data-input-association') {
1150
+ sourceRefId = Array.isArray(node.definition.sourceRef) && node.definition.sourceRef[0]?.id;
1151
+ targetRefId = node.definition.targetRef?.$parent?.$parent.get('id');
1152
+ }
1153
+
1154
+ if (sourceRefId && targetRefId) {
1155
+ window.ProcessMaker.EventBus.$emit('multiplayer-addFlow', {
1156
+ id: node.definition.id,
1157
+ type: node.type,
1158
+ sourceRefId,
1159
+ targetRefId,
1160
+ waypoint: node.diagram.waypoint,
1161
+ });
1162
+ }
1148
1163
  }
1149
1164
  }
1150
1165
  },
@@ -1338,9 +1353,10 @@ export default {
1338
1353
  await this.paperManager.performAtomicAction(async() => {
1339
1354
  await this.highlightNode(null);
1340
1355
  await this.$nextTick();
1341
- await this.addNode(actualFlow);
1342
1356
  await store.commit('removeNode', genericFlow);
1343
1357
  await this.$nextTick();
1358
+ await this.addNode(actualFlow, genericFlow.definition.id);
1359
+ await this.$nextTick();
1344
1360
  await this.highlightNode(targetNode);
1345
1361
  });
1346
1362
  });
@@ -31,7 +31,7 @@ import { id as poolId } from '@/components/nodes/pool/config';
31
31
  import { id as laneId } from '@/components/nodes/poolLane/config';
32
32
  import { id as genericFlowId } from '@/components/nodes/genericFlow/config';
33
33
  import { id as sequenceFlowId } from '@/components/nodes/sequenceFlow';
34
- import { id as associationId } from '@/components/nodes/association';
34
+ import { id as associationId } from '@/components/nodes/association/associationConfig';
35
35
  import { id as messageFlowId } from '@/components/nodes/messageFlow/config';
36
36
  import { id as dataOutputAssociationFlowId } from '@/components/nodes/dataOutputAssociation/config';
37
37
  import { id as dataInputAssociationFlowId } from '@/components/nodes/dataInputAssociation/config';
@@ -577,7 +577,7 @@ export default {
577
577
  await this.paperManager.awaitScheduledUpdates();
578
578
  this.overPoolStopDrag();
579
579
  this.updateSelectionBox();
580
- if (this.isMultiplayer) {
580
+ if (this.isMultiplayer) {
581
581
  window.ProcessMaker.EventBus.$emit('multiplayer-updateNodes', this.getProperties(this.selected));
582
582
  }
583
583
  },
@@ -0,0 +1,27 @@
1
+ import Node from '@/components/nodes/node';
2
+ import * as associationConfig from './associationConfig';
3
+
4
+ export class AssociationFlow {
5
+ constructor(nodeRegistry, moddle, paper) {
6
+ this.nodeRegistry = nodeRegistry;
7
+ this.moddle = moddle;
8
+ this.paper = paper;
9
+ }
10
+
11
+ makeFlowNode(sourceShape, targetShape, waypoint) {
12
+ const diagram = associationConfig.diagram(this.moddle);
13
+ const associationFlow = associationConfig.definition(this.moddle);
14
+
15
+ associationFlow.set('sourceRef', sourceShape.component.node.definition);
16
+ associationFlow.set('targetRef', targetShape.component.node.definition);
17
+
18
+ diagram.waypoint = waypoint.map(point => this.moddle.create('dc:Point', point));
19
+
20
+ const node = new Node(
21
+ associationConfig.id,
22
+ associationFlow,
23
+ diagram,
24
+ );
25
+ return node;
26
+ }
27
+ }
@@ -1 +1,16 @@
1
+ export const id = 'processmaker-modeler-association';
2
+
3
+ export const bpmnType = 'bpmn:Association';
4
+
1
5
  export const direction = { none: 'None', one: 'One', both: 'Both' };
6
+
7
+ export function definition(moddle) {
8
+ return moddle.create(bpmnType, {
9
+ associationDirection: direction.none,
10
+ targetRef: { x: undefined, y: undefined },
11
+ });
12
+ }
13
+
14
+ export function diagram(moddle) {
15
+ return moddle.create('bpmndi:BPMNEdge');
16
+ }
@@ -1,22 +1,16 @@
1
+ import { getNodeIdGenerator } from '@/NodeIdGenerator';
1
2
  import component from './association.vue';
2
- import { direction } from './associationConfig';
3
+ import { id, bpmnType, direction, definition, diagram } from './associationConfig';
3
4
  import idConfigSettings from '@/components/inspectors/idConfigSettings';
4
-
5
- export const id = 'processmaker-modeler-association';
5
+ import { AssociationFlow } from './AssociationFlow';
6
6
 
7
7
  export default {
8
8
  id,
9
9
  component,
10
- bpmnType: 'bpmn:Association',
10
+ bpmnType,
11
11
  control: false,
12
- definition(moddle) {
13
- return moddle.create('bpmn:Association', {
14
- associationDirection: `${direction.none}`,
15
- });
16
- },
17
- diagram(moddle) {
18
- return moddle.create('bpmndi:BPMNEdge');
19
- },
12
+ definition,
13
+ diagram,
20
14
  inspectorConfig: [
21
15
  {
22
16
  name: 'Data Association',
@@ -53,4 +47,17 @@ export default {
53
47
  ],
54
48
  },
55
49
  ],
50
+ async multiplayerClient(modeler, data) {
51
+ const { paper } = modeler;
52
+ const sourceElem = modeler.getElementByNodeId(data.sourceRefId);
53
+ const targetElem = modeler.getElementByNodeId(data.targetRefId);
54
+ if (sourceElem && targetElem) {
55
+ const flow = new AssociationFlow(modeler.nodeRegistry, modeler.moddle, paper);
56
+ const actualFlow = flow.makeFlowNode(sourceElem, targetElem, data.waypoint);
57
+ // add Nodes
58
+ modeler.addNode(actualFlow, data.id, true);
59
+ const nodeIdIterator = getNodeIdGenerator(modeler.definitions);
60
+ nodeIdIterator.updateCounters();
61
+ }
62
+ },
56
63
  };
@@ -0,0 +1,30 @@
1
+ import Node from '@/components/nodes/node';
2
+ import * as associationConfig from './config';
3
+ import { getOrFindDataInput } from '@/components/crown/utils';
4
+
5
+ export class DataInputAssociation {
6
+ constructor(nodeRegistry, moddle, paper) {
7
+ this.nodeRegistry = nodeRegistry;
8
+ this.moddle = moddle;
9
+ this.paper = paper;
10
+ }
11
+
12
+ makeFlowNode(sourceShape, targetShape, waypoint) {
13
+ const diagram = associationConfig.diagram(this.moddle);
14
+ const associationFlow = associationConfig.definition(this.moddle);
15
+
16
+ // When saving the BPMN, if this is not an array the sourceRef is not stored
17
+ const dataInput = getOrFindDataInput(this.moddle, targetShape.component.node, sourceShape.component.node.definition);
18
+ associationFlow.set('targetRef', dataInput);
19
+ associationFlow.set('sourceRef', [sourceShape.component.node.definition]);
20
+
21
+ diagram.waypoint = waypoint.map(point => this.moddle.create('dc:Point', point));
22
+
23
+ const node = new Node(
24
+ associationConfig.id,
25
+ associationFlow,
26
+ diagram,
27
+ );
28
+ return node;
29
+ }
30
+ }
@@ -1,6 +1,8 @@
1
1
  import component from './dataInputAssociation.vue';
2
2
  import idConfigSettings from '@/components/inspectors/idConfigSettings';
3
3
  import * as config from './config';
4
+ import { DataInputAssociation } from './DataInputAssociation';
5
+ import { getNodeIdGenerator } from '@/NodeIdGenerator';
4
6
 
5
7
  export default {
6
8
  ...config,
@@ -29,4 +31,19 @@ export default {
29
31
  ],
30
32
  },
31
33
  ],
34
+ async multiplayerClient(modeler, data) {
35
+ const { paper } = modeler;
36
+ const sourceElem = modeler.getElementByNodeId(data.sourceRefId);
37
+ const targetElem = modeler.getElementByNodeId(data.targetRefId);
38
+ if (sourceElem && targetElem) {
39
+ const flow = new DataInputAssociation(modeler.nodeRegistry, modeler.moddle, paper);
40
+ const actualFlow = flow.makeFlowNode(sourceElem, targetElem, data.waypoint);
41
+
42
+ targetElem.component.node.definition.set('dataInputAssociations', [actualFlow.definition]);
43
+ // add Nodes
44
+ modeler.addNode(actualFlow, data.id, true);
45
+ const nodeIdIterator = getNodeIdGenerator(modeler.definitions);
46
+ nodeIdIterator.updateCounters();
47
+ }
48
+ },
32
49
  };
@@ -1,6 +1,8 @@
1
1
  import component from './dataOutputAssociation.vue';
2
2
  import idConfigSettings from '@/components/inspectors/idConfigSettings';
3
3
  import * as config from './config';
4
+ import { getNodeIdGenerator } from '@/NodeIdGenerator';
5
+ import DataOutputAssociation from '../genericFlow/DataOutputAssociation';
4
6
 
5
7
  export default {
6
8
  ...config,
@@ -29,4 +31,17 @@ export default {
29
31
  ],
30
32
  },
31
33
  ],
34
+ async multiplayerClient(modeler, data) {
35
+ const { paper } = modeler;
36
+ const sourceElem = modeler.getElementByNodeId(data.sourceRefId);
37
+ const targetElem = modeler.getElementByNodeId(data.targetRefId);
38
+ if (sourceElem && targetElem) {
39
+ const flow = new DataOutputAssociation(modeler.nodeRegistry, modeler.moddle, paper);
40
+ const actualFlow = flow.makeFlowNode(sourceElem, targetElem, data.waypoint);
41
+ // add Nodes
42
+ modeler.addNode(actualFlow, data.id, true);
43
+ const nodeIdIterator = getNodeIdGenerator(modeler.definitions);
44
+ nodeIdIterator.updateCounters();
45
+ }
46
+ },
32
47
  };
@@ -55,7 +55,7 @@ export default class DataOutputAssociation extends DataAssociation {
55
55
  const sourceIsDataObject = sourceNode.definition.$type === 'bpmn:DataObjectReference';
56
56
  const targetIsDataStore = targetNode.definition.$type === 'bpmn:DataStoreReference';
57
57
  const targetIsDataObject = targetNode.definition.$type === 'bpmn:DataObjectReference';
58
-
58
+
59
59
  if (sourceIsDataStore && dataStoreValidTargets.includes(targetType)) {
60
60
  return true;
61
61
  }
@@ -109,6 +109,7 @@ export default {
109
109
  const flow = new bpmnFlow.factory(this.nodeRegistry, this.moddle, this.paper);
110
110
  const genericLink = this.shape.findView(this.paper);
111
111
  const waypoint = [genericLink.sourceAnchor.toJSON(), genericLink.targetAnchor.toJSON()];
112
+
112
113
  this.$emit('replace-generic-flow', {
113
114
  actualFlow: flow.makeFlowNode(this.sourceShape, this.target, waypoint),
114
115
  genericFlow: this.node,
@@ -1,6 +1,6 @@
1
1
  import { id as laneId } from '../components/nodes/poolLane/config';
2
2
  import { id as sequenceFlowId } from '../components/nodes/sequenceFlow';
3
- import { id as associationId } from '../components/nodes/association';
3
+ import { id as associationId } from '../components/nodes/association/associationConfig';
4
4
  import { id as messageFlowId } from '../components/nodes/messageFlow/config';
5
5
  import { id as dataOutputAssociationFlowId } from '../components/nodes/dataOutputAssociation/config';
6
6
  import { id as dataInputAssociationFlowId } from '../components/nodes/dataInputAssociation/config';
@@ -23,6 +23,7 @@ export default {
23
23
  props: ['highlighted', 'paper', 'paperManager', 'isCompleted', 'isIdle'],
24
24
  data() {
25
25
  return {
26
+ linkView: null,
26
27
  sourceShape: null,
27
28
  target: null,
28
29
  listeningToMouseup: false,
@@ -163,6 +164,31 @@ export default {
163
164
  this.updateWaypoints();
164
165
  await this.$nextTick();
165
166
 
167
+ if (this.$parent.isMultiplayer && this.linkView) {
168
+ // update waypoints in multiplayer mode
169
+ const nodeType = this.linkView.model.component.node.type;
170
+ const sourceRefId = this.linkView.sourceView.model.component.node.definition.id;
171
+ const targetRefId = this.linkView.targetView.model.component.node.definition.id;
172
+
173
+ const changes = [
174
+ {
175
+ id: this.linkView.model.component.node.definition.id,
176
+ properties: {
177
+ type: nodeType,
178
+ waypoint: [
179
+ this.linkView.sourceAnchor.toJSON(),
180
+ ...this.shape.vertices(),
181
+ this.linkView.targetAnchor.toJSON(),
182
+ ],
183
+ sourceRefId,
184
+ targetRefId,
185
+ },
186
+ },
187
+ ];
188
+
189
+ window.ProcessMaker.EventBus.$emit('multiplayer-updateNodes', changes);
190
+ }
191
+
166
192
  this.listeningToMouseleave = true;
167
193
  this.$emit('save-state');
168
194
  }
@@ -174,14 +200,15 @@ export default {
174
200
  * @param {Object} options
175
201
  */
176
202
  async onChangeTargets(link, vertices, options){
177
- if (options && options.ui) {
203
+ if (options?.ui) {
178
204
  await this.$nextTick();
179
205
  await this.waitForUpdateWaypoints();
206
+ this.listeningToMouseleave = false;
180
207
  await this.storeWaypoints();
181
208
  }
182
209
  },
183
210
  async onChangeVertices(link, vertices, options){
184
- if (options && options.ui) {
211
+ if (options?.ui) {
185
212
  this.updateWaypoints();
186
213
  await this.$nextTick();
187
214
  this.listeningToMouseleave = false;
@@ -189,9 +216,9 @@ export default {
189
216
  }
190
217
  },
191
218
  updateWaypoints() {
192
- const linkView = this.shape.findView(this.paper);
193
- const start = linkView.sourceAnchor;
194
- const end = linkView.targetAnchor;
219
+ this.linkView = this.shape.findView(this.paper);
220
+ const start = this.linkView.sourceAnchor;
221
+ const end = this.linkView.targetAnchor;
195
222
 
196
223
  this.node.diagram.waypoint = [start,
197
224
  ...this.shape.vertices(),
@@ -239,6 +266,11 @@ export default {
239
266
  if (this.updateDefinitionLinks) {
240
267
  this.updateDefinitionLinks();
241
268
  }
269
+
270
+ if (this.linkView && ['processmaker-modeler-association', 'processmaker-modeler-data-input-association'].includes(this.shape.component.node.type)) {
271
+ this.$parent.multiplayerHook(this.shape.component.node, false);
272
+ }
273
+
242
274
  this.$emit('save-state');
243
275
  });
244
276
 
@@ -1,7 +1,9 @@
1
1
  import { io } from 'socket.io-client';
2
2
  import * as Y from 'yjs';
3
3
  import { getNodeIdGenerator } from '../NodeIdGenerator';
4
+ import { getDefaultAnchorPoint } from '@/portsUtils';
4
5
  import Room from './room';
6
+
5
7
  export default class Multiplayer {
6
8
  clientIO = null;
7
9
  yDoc = null;
@@ -142,12 +144,12 @@ export default class Multiplayer {
142
144
  this.modeler.addRemoteNode(value);
143
145
  }
144
146
  this.#nodeIdGenerator.updateCounters();
145
-
147
+
146
148
  }
147
149
  createRemoteShape(changes) {
148
150
  return new Promise(resolve => {
149
151
  changes.map((data) => {
150
- this.createShape(data);
152
+ this.createShape(data);
151
153
  });
152
154
  resolve();
153
155
  });
@@ -252,23 +254,55 @@ export default class Multiplayer {
252
254
  const { paper } = this.modeler;
253
255
  const element = this.modeler.getElementByNodeId(data.id);
254
256
  const newPool = this.modeler.getElementByNodeId(data.poolId);
255
- // Update the element's position attribute
256
- element.resize(
257
- /* Add labelWidth to ensure elements don't overlap with the pool label */
258
- data.width,
259
- data.height,
260
- );
261
- element.set('position', { x: data.x, y: data.y });
262
- // Trigger a rendering of the element on the paper
263
- await paper.findViewByModel(element).update();
264
- // validate if the parent pool was updated
265
- await element.component.$nextTick();
266
- await this.modeler.paperManager.awaitScheduledUpdates();
267
- if (newPool && element.component.node.pool && element.component.node.pool.component.id !== data.poolId) {
268
- element.component.node.pool.component.moveElementRemote(element, newPool);
257
+
258
+ if (this.modeler.flowTypes.includes(data.type)) {
259
+ // Update the element's waypoints
260
+ // Get the source and target elements
261
+ const sourceElem = this.modeler.getElementByNodeId(data.sourceRefId);
262
+ const targetElem = this.modeler.getElementByNodeId(data.targetRefId);
263
+
264
+ const { waypoint } = data;
265
+ const startWaypoint = waypoint.shift();
266
+ const endWaypoint = waypoint.pop();
267
+
268
+ // Update the element's waypoints
269
+ const newWaypoint = waypoint.map(point => this.modeler.moddle.create('dc:Point', point));
270
+ element.set('vertices', newWaypoint);
271
+
272
+ // Update the element's source anchor
273
+ element.source(sourceElem, {
274
+ anchor: () => {
275
+ return getDefaultAnchorPoint(this.getConnectionPoint(sourceElem, startWaypoint), sourceElem.findView(paper));
276
+ },
277
+ connectionPoint: { name: 'boundary' },
278
+ });
279
+
280
+ // Update the element's target anchor
281
+ element.target(targetElem, {
282
+ anchor: () => {
283
+ return getDefaultAnchorPoint(this.getConnectionPoint(targetElem, endWaypoint), targetElem.findView(paper));
284
+ },
285
+ connectionPoint: { name: 'boundary' },
286
+ });
287
+ } else {
288
+ // Update the element's position attribute
289
+ element.resize(
290
+ /* Add labelWidth to ensure elements don't overlap with the pool label */
291
+ data.width,
292
+ data.height,
293
+ );
294
+ element.set('position', { x: data.x, y: data.y });
295
+ // Trigger a rendering of the element on the paper
296
+ await paper.findViewByModel(element).update();
297
+ // validate if the parent pool was updated
298
+ await element.component.$nextTick();
299
+ await this.modeler.paperManager.awaitScheduledUpdates();
300
+ if (newPool && element.component.node.pool && element.component.node.pool.component.id !== data.poolId) {
301
+ element.component.node.pool.component.moveElementRemote(element, newPool);
302
+ }
269
303
  }
270
304
  }
271
-
305
+
272
306
  addFlow(data) {
273
307
  const yMapNested = new Y.Map();
274
308
  this.doTransact(yMapNested, data);
@@ -320,8 +354,21 @@ export default class Multiplayer {
320
354
  getPool(lanes) {
321
355
  if (lanes && lanes.length > 0) {
322
356
  return lanes[0].pool;
323
- }
357
+ }
324
358
  return false;
325
359
  }
360
+ getConnectionPoint(element, newPosition) {
361
+ const { x: elemX, y: elemY } = element.position();
362
+ const connectionOffset = {
363
+ x: newPosition.x - elemX,
364
+ y: newPosition.y - elemY,
365
+ };
326
366
 
367
+ const { x, y } = element.position();
368
+ const { width, height } = element.size();
369
+
370
+ return connectionOffset
371
+ ? { x: x + connectionOffset.x, y: y + connectionOffset.y }
372
+ : { x: x + (width / 2), y: y + (height / 2) };
373
+ }
327
374
  }