@processmaker/modeler 1.39.17 → 1.39.18

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.
@@ -2,27 +2,6 @@ import { io } from 'socket.io-client';
2
2
  import * as Y from 'yjs';
3
3
  import { getNodeIdGenerator } from '../NodeIdGenerator';
4
4
  import Room from './room';
5
- import MessageFlow from '@/components/nodes/genericFlow/MessageFlow';
6
- import SequenceFlow from '@/components/nodes/genericFlow/SequenceFlow';
7
- import DataOutputAssociation from '@/components/nodes/genericFlow/DataOutputAssociation';
8
- const BpmnFlows = [
9
- {
10
- type: 'processmaker-modeler-text-annotation',
11
- factory: DataOutputAssociation,
12
- },
13
- {
14
- type: 'processmaker-modeler-sequence-flow',
15
- factory: SequenceFlow,
16
- },
17
- {
18
- type: 'processmaker-modeler-message-flow',
19
- factory: MessageFlow,
20
- },
21
- {
22
- type: 'processmaker-modeler-data-input-association',
23
- factory: DataOutputAssociation,
24
- },
25
- ];
26
5
  export default class Multiplayer {
27
6
  clientIO = null;
28
7
  yDoc = null;
@@ -44,7 +23,8 @@ export default class Multiplayer {
44
23
  // Get the node id generator
45
24
  this.#nodeIdGenerator = getNodeIdGenerator(this.modeler.definitions);
46
25
  // Get the room name from the process id
47
- this.room = new Room(`room-${window.ProcessMaker.modeler.process.id}`);
26
+ const processId = window.ProcessMaker.modeler.process.uuid ?? window.ProcessMaker.modeler.process.id;
27
+ this.room = new Room(`room-${processId}`);
48
28
 
49
29
  // Connect to websocket server
50
30
  this.clientIO = io(window.ProcessMaker.multiplayer.host, { transports: ['websocket', 'polling']});
@@ -53,8 +33,8 @@ export default class Multiplayer {
53
33
  // Join the room
54
34
  this.clientIO.emit('joinRoom', {
55
35
  roomName: this.room.getRoom(),
56
- clientName: window.ProcessMaker.user.fullName,
57
- clientAvatar: window.ProcessMaker.user.avatar,
36
+ clientName: window.ProcessMaker.user?.fullName,
37
+ clientAvatar: window.ProcessMaker.user?.avatar,
58
38
  });
59
39
  });
60
40
 
@@ -140,12 +120,13 @@ export default class Multiplayer {
140
120
  window.ProcessMaker.EventBus.$on('multiplayer-addFlow', ( data ) => {
141
121
  this.addFlow(data);
142
122
  });
123
+
124
+ window.ProcessMaker.EventBus.$on('multiplayer-addLanes', ( lanes ) => {
125
+ this.addLaneNodes(lanes);
126
+ });
143
127
  }
144
128
  addNode(data) {
145
- // Add the new element to the process
146
- this.createShape(data);
147
129
  // Add the new element to the shared array
148
- // this.yArray.push([data]);
149
130
  const yMapNested = new Y.Map();
150
131
  this.doTransact(yMapNested, data);
151
132
  this.yArray.push([yMapNested]);
@@ -154,47 +135,45 @@ export default class Multiplayer {
154
135
  // Send the update to the web socket server
155
136
  this.clientIO.emit('createElement', stateUpdate);
156
137
  }
157
- createShape(value) {
158
- this.modeler.handleDropProcedure(value, false);
138
+ createShape(value){
139
+ if (this.modeler.nodeRegistry[value.type] && this.modeler.nodeRegistry[value.type].multiplayerClient) {
140
+ this.modeler.nodeRegistry[value.type].multiplayerClient(this.modeler, value);
141
+ } else {
142
+ this.modeler.addRemoteNode(value);
143
+ }
159
144
  this.#nodeIdGenerator.updateCounters();
145
+
160
146
  }
161
147
  createRemoteShape(changes) {
162
- const flows = [
163
- 'processmaker-modeler-sequence-flow',
164
- 'processmaker-modeler-text-annotation',
165
- 'processmaker-modeler-message-flow',
166
- 'processmaker-modeler-data-input-association',
167
- ];
168
148
  return new Promise(resolve => {
169
149
  changes.map((data) => {
170
- if (flows.includes(data.type)) {
171
- this.createFlow(data);
172
- } else {
173
- this.createShape(data);
174
- }
150
+ this.createShape(data);
175
151
  });
176
152
  resolve();
177
153
  });
178
154
  }
179
155
  removeNode(data) {
180
156
  const index = this.getIndex(data.definition.id);
181
- this.removeShape(data);
182
- this.yArray.delete(index, 1); // delete one element
183
-
184
- // Encode the state as an update and send it to the server
185
- const stateUpdate = Y.encodeStateAsUpdate(this.yDoc);
186
- // Send the update to the web socket server
187
- this.clientIO.emit('removeElement', stateUpdate);
157
+ if (index >= 0) {
158
+ this.removeShape(data);
159
+ this.yArray.delete(index, 1); // delete one element
160
+ // Encode the state as an update and send it to the server
161
+ const stateUpdate = Y.encodeStateAsUpdate(this.yDoc);
162
+ // Send the update to the web socket server
163
+ this.clientIO.emit('removeElement', stateUpdate);
164
+ }
188
165
  }
189
166
  getIndex(id) {
190
167
  let index = -1;
168
+ let found = false;
191
169
  for (const value of this.yArray) {
192
170
  index ++;
193
171
  if (value.get('id') === id) {
172
+ found = true;
194
173
  break ;
195
174
  }
196
175
  }
197
- return index;
176
+ return found ? index : -1;
198
177
  }
199
178
  getNodeById(nodeId) {
200
179
  const node = this.modeler.nodes.find((element) => element.definition && element.definition.id === nodeId);
@@ -215,7 +194,8 @@ export default class Multiplayer {
215
194
  data.forEach((value) => {
216
195
  const index = this.getIndex(value.id);
217
196
  const nodeToUpdate = this.yArray.get(index);
218
- this.doTransact(nodeToUpdate, value.properties);
197
+ const updateData = value.poolId ? { ...value.properties, ...{ poolId: value.poolId } } : value.properties;
198
+ this.doTransact(nodeToUpdate, updateData);
219
199
  });
220
200
  }
221
201
  replaceNode(nodeData, newControl) {
@@ -224,13 +204,11 @@ export default class Multiplayer {
224
204
  const nodeToUpdate = this.yArray.get(index);
225
205
  // Update the node id in the nodeData
226
206
  nodeData.id = `node_${this.#nodeIdGenerator.getDefinitionNumber()}`;
227
- // Replace the node in the process
228
- this.modeler.replaceNodeProcedure(nodeData, true);
229
207
  // Update the node id generator
230
208
  this.#nodeIdGenerator.updateCounters();
231
209
  // Update the node in the shared array
232
210
  this.yDoc.transact(() => {
233
- nodeToUpdate.set('control', newControl);
211
+ nodeToUpdate.set('type', newControl);
234
212
  nodeToUpdate.set('id', nodeData.id);
235
213
  });
236
214
 
@@ -240,13 +218,14 @@ export default class Multiplayer {
240
218
  this.clientIO.emit('updateElement', { updateDoc: stateUpdate, isReplaced: true });
241
219
  }
242
220
  replaceShape(updatedNode) {
221
+ const { x: clientX, y: clientY } = this.modeler.paper.localToClientPoint(updatedNode);
243
222
  // Get the node to update
244
223
  const node = this.getNodeById(updatedNode.oldNodeId);
245
224
  // Update the node id in the nodeData
246
225
  const nodeData = {
247
- clientX: updatedNode.clientX,
248
- clientY: updatedNode.clientY,
249
- control: { type: updatedNode.control.type },
226
+ clientX,
227
+ clientY,
228
+ control: { type: updatedNode.type },
250
229
  nodeThatWillBeReplaced: node,
251
230
  id: updatedNode.id,
252
231
  };
@@ -269,23 +248,27 @@ export default class Multiplayer {
269
248
  // Send the update to the web socket server
270
249
  this.clientIO.emit('updateElement', { updateDoc: stateUpdate, isReplaced: false });
271
250
  }
272
- updateShapes(data) {
251
+ async updateShapes(data) {
273
252
  const { paper } = this.modeler;
274
- const element = this.getJointElement(paper.model, data.id);
253
+ const element = this.modeler.getElementByNodeId(data.id);
254
+ const newPool = this.modeler.getElementByNodeId(data.poolId);
275
255
  // Update the element's position attribute
276
- element.set('position', { x:data.clientX, y:data.clientY });
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 });
277
262
  // Trigger a rendering of the element on the paper
278
- paper.findViewByModel(element).update();
279
- }
280
- getJointElement(graph, targetValue) {
281
- const cells = graph.getCells();
282
- for (const cell of cells) {
283
- if (cell.component.id === targetValue) {
284
- return cell;
285
- }
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);
286
269
  }
287
- return null; // Return null if no matching element is found
288
270
  }
271
+
289
272
  addFlow(data) {
290
273
  const yMapNested = new Y.Map();
291
274
  this.doTransact(yMapNested, data);
@@ -296,20 +279,49 @@ export default class Multiplayer {
296
279
  this.clientIO.emit('createElement', stateUpdate);
297
280
  this.#nodeIdGenerator.updateCounters();
298
281
  }
299
- createFlow(data){
300
- const { paper } = this.modeler;
301
- const sourceElem = this.getJointElement(paper.model, data.sourceRefId);
302
- const targetElem = this.getJointElement(paper.model, data.targetRefId);
303
- if (sourceElem && targetElem) {
304
- const bpmnFlow = BpmnFlows.find(FlowClass => {
305
- return FlowClass.type === data.type;
306
- });
307
- const flow = new bpmnFlow.factory(this.modeler.nodeRegistry, this.modeler.moddle, this.modeler.paper);
308
- const actualFlow = flow.makeFlowNode(sourceElem, targetElem, data.waypoint);
309
- // add Nodes
310
- this.modeler.addNode(actualFlow, data.id);
311
- this.#nodeIdGenerator.updateCounters();
312
- }
313
282
 
283
+ addLaneNodes(lanes) {
284
+ const pool = this.getPool(lanes);
285
+ window.ProcessMaker.EventBus.$emit('multiplayer-updateNodes', [{
286
+ id: pool.component.node.definition.id,
287
+ properties: {
288
+ x: pool.component.node.diagram.bounds.x,
289
+ y: pool.component.node.diagram.bounds.y,
290
+ height: pool.component.node.diagram.bounds.height,
291
+ width: pool.component.node.diagram.bounds.width,
292
+ isAddingLaneAbove: pool.isAddingLaneAbove,
293
+ },
294
+ }]);
295
+ this.yDoc.transact(() => {
296
+ lanes.forEach((lane) => {
297
+ const yMapNested = new Y.Map();
298
+ const data = this.prepareLaneData(lane);
299
+ this.doTransact(yMapNested, data);
300
+ this.yArray.push([yMapNested]);
301
+ const stateUpdate = Y.encodeStateAsUpdate(this.yDoc);
302
+ this.clientIO.emit('createElement', stateUpdate);
303
+ });
304
+ });
305
+ }
306
+ prepareLaneData(lane) {
307
+ const data = {
308
+ type: lane.type,
309
+ id: lane.definition.id,
310
+ name: lane.definition.name,
311
+ x: lane.diagram.bounds.x,
312
+ y: lane.diagram.bounds.y,
313
+ width: lane.diagram.bounds.width,
314
+ height: lane.diagram.bounds.height,
315
+ poolId: lane.pool.component.node.definition.id,
316
+ laneSetId: lane.pool.component.laneSet.id,
317
+ };
318
+ return data;
314
319
  }
320
+ getPool(lanes) {
321
+ if (lanes && lanes.length > 0) {
322
+ return lanes[0].pool;
323
+ }
324
+ return false;
325
+ }
326
+
315
327
  }
@@ -114,7 +114,7 @@ export default new Vuex.Store({
114
114
  },
115
115
  actions: {
116
116
  getUserPinnedObjects({ commit }) {
117
- if (!window.ProcessMaker.user) {
117
+ if (window.ProcessMaker?.user?.id === 'standalone') {
118
118
  // For standalone version of Modeler
119
119
  const pinnedNodes = localStorage.pinnedNodes ? JSON.parse(localStorage.pinnedNodes) : [] ;
120
120
  pinnedNodes.forEach(node => {
@@ -137,7 +137,7 @@ export default new Vuex.Store({
137
137
  addUserPinnedObject({ commit, state }, pinnedNode) {
138
138
  commit('setPinnedNodes', pinnedNode);
139
139
  const pinnedNodes = state.pinnedNodeTypes;
140
- if (!window.ProcessMaker.user) {
140
+ if (window.ProcessMaker?.user?.id === 'standalone') {
141
141
  // For standalone version of Modeler
142
142
  localStorage.pinnedNodes = JSON.stringify(pinnedNodes);
143
143
  return;
@@ -152,7 +152,7 @@ export default new Vuex.Store({
152
152
  removeUserPinnedObject({ commit, state }, nodeToUnpin) {
153
153
  commit('setUnpinNode', nodeToUnpin);
154
154
  const pinnedNodes = state.pinnedNodeTypes;
155
- if (!window.ProcessMaker.user) {
155
+ if (window.ProcessMaker?.user?.id === 'standalone') {
156
156
  // For standalone version of Modeler
157
157
  localStorage.pinnedNodes = JSON.stringify(pinnedNodes);
158
158
  return;
@@ -4,6 +4,7 @@ import MockAdapter from 'axios-mock-adapter';
4
4
  import mockProcesses from './mockProcesses.json';
5
5
  import mockSignals from './mockSignals.json';
6
6
  import mockProcessSvg from './mockProcessSvg';
7
+ import { faker } from '@faker-js/faker';
7
8
 
8
9
  axios.defaults.baseURL = 'https://bpm4.local.processmaker.com/api/1.0/';
9
10
  axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
@@ -57,5 +58,10 @@ window.ProcessMaker = {
57
58
  id: 1,
58
59
  },
59
60
  },
61
+ user: {
62
+ id: 'standalone',
63
+ fullName: faker.person.fullName(),
64
+ avatar: null,
65
+ },
60
66
 
61
67
  };