@processmaker/modeler 1.39.7 → 1.39.9

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,8 +1,8 @@
1
1
  {
2
2
  "name": "@processmaker/modeler",
3
- "version": "1.39.7",
3
+ "version": "1.39.9",
4
4
  "scripts": {
5
- "serve": "vue-cli-service serve",
5
+ "serve": "vue-cli-service serve --mode development",
6
6
  "test:unit": "vue-cli-service test:unit",
7
7
  "lint": "vue-cli-service lint --no-fix",
8
8
  "build-bundle": "vue-cli-service build --target lib --name modeler ./src/components/nodes/index.js",
@@ -56,6 +56,7 @@
56
56
  "jointjs": "^3.1.1",
57
57
  "js-yaml-loader": "^1.2.2",
58
58
  "lodash": "^4.17.21",
59
+ "lodash-contrib": "^4.1200.1",
59
60
  "luxon": "^1.21.1",
60
61
  "mocha-junit-reporter": "^2.0.0",
61
62
  "mustache": "^3.2.1",
@@ -68,14 +69,16 @@
68
69
  "vue-monaco": "^1.2.1",
69
70
  "vue-popperjs": "^2.3.0",
70
71
  "vue-upload-component": "^2.8.20",
71
- "vuex": "^3.5.1"
72
+ "vuex": "^3.5.1",
73
+ "y-websocket": "^1.5.0",
74
+ "yjs": "^13.6.7"
72
75
  },
73
76
  "devDependencies": {
74
77
  "@babel/core": "^7.12.16",
75
78
  "@babel/eslint-parser": "^7.12.16",
76
79
  "@cypress/code-coverage": "^3.11.0",
77
80
  "@panter/vue-i18next": "^0.15.2",
78
- "@processmaker/processmaker-bpmn-moddle": "0.14.0",
81
+ "@processmaker/processmaker-bpmn-moddle": "0.14.1",
79
82
  "@types/jest": "^24.9.1",
80
83
  "@vue/babel-preset-app": "^5.0.4",
81
84
  "@vue/cli-plugin-babel": "~5.0.0",
@@ -58,6 +58,14 @@ export default class NodeIdGenerator {
58
58
  this.refreshLastIdCounter();
59
59
  return NodeIdGenerator.#counter;
60
60
  }
61
+ getDefinitionNumber() {
62
+ return NodeIdGenerator.#counter;
63
+ }
64
+
65
+ updateCounters() {
66
+ NodeIdGenerator.#counter++;
67
+ NodeIdGenerator.#diagramCounter++;
68
+ }
61
69
 
62
70
  refreshLastDiagramIdCounter() {
63
71
  let lastIdCounter = this.matchIds(new RegExp(`^${NodeIdGenerator.prefix}(\\d+)_di$`), this.definitions.diagrams);
@@ -152,6 +152,7 @@
152
152
  @remove-nodes="removeNodes"
153
153
  :processNode="processNode"
154
154
  @save-state="pushToUndoStack"
155
+ :isMultiplayer="isMultiplayer"
155
156
  />
156
157
  </b-row>
157
158
 
@@ -173,6 +174,7 @@ import { dia } from 'jointjs';
173
174
  import boundaryEventConfig from '../nodes/boundaryEvent';
174
175
  import BpmnModdle from 'bpmn-moddle';
175
176
  import ExplorerRail from '../rails/explorer-rail/explorer';
177
+ import { isJSON } from 'lodash-contrib';
176
178
  import pull from 'lodash/pull';
177
179
  import remove from 'lodash/remove';
178
180
  import store from '@/store';
@@ -327,12 +329,13 @@ export default {
327
329
  players: [],
328
330
  showInspectorButton: true,
329
331
  inspectorButtonRight: 65,
332
+ multiplayer: null,
333
+ isMultiplayer: false,
330
334
  };
331
335
  },
332
336
  watch: {
333
337
  isRendering() {
334
338
  const loadingMessage = 'Loading process, please be patient.';
335
-
336
339
  if (this.isRendering) {
337
340
  window.ProcessMaker.alert(loadingMessage, 'warning');
338
341
  document.body.style.cursor = 'wait !important';
@@ -917,7 +920,7 @@ export default {
917
920
 
918
921
  this.removeUnsupportedElementAttributes(definition);
919
922
 
920
- const config = definition.config ? JSON.parse(definition.config) : {};
923
+ const config = definition.config && isJSON(definition.config) ? JSON.parse(definition.config) : {};
921
924
  const type = config?.processKey || parser(definition, this.moddle);
922
925
 
923
926
  const unnamedElements = ['bpmn:TextAnnotation', 'bpmn:Association', 'bpmn:DataOutputAssociation', 'bpmn:DataInputAssociation'];
@@ -1024,7 +1027,21 @@ export default {
1024
1027
  control,
1025
1028
  });
1026
1029
  },
1027
- async handleDrop({ clientX, clientY, control, nodeThatWillBeReplaced }) {
1030
+ handleDrop(data) {
1031
+ const { clientX, clientY, control} = data;
1032
+ if (this.isMultiplayer) {
1033
+ window.ProcessMaker.EventBus.$emit('multiplayer-addNode', {
1034
+ clientX,
1035
+ clientY,
1036
+ control,
1037
+ id: `node_${this.nodeIdGenerator.getDefinitionNumber()}`,
1038
+ });
1039
+ } else {
1040
+ this.handleDropProcedure(data);
1041
+ }
1042
+ },
1043
+ async handleDropProcedure(data, selected=true) {
1044
+ const { clientX, clientY, control, nodeThatWillBeReplaced, id } = data;
1028
1045
  this.validateDropTarget({ clientX, clientY, control });
1029
1046
  if (!this.allowDrop) {
1030
1047
  return;
@@ -1043,9 +1060,11 @@ export default {
1043
1060
  if (newNode.isBpmnType('bpmn:BoundaryEvent')) {
1044
1061
  this.setShapeCenterUnderCursor(diagram);
1045
1062
  }
1046
-
1047
- this.highlightNode(newNode);
1048
- await this.addNode(newNode);
1063
+ if (selected) {
1064
+ this.highlightNode(newNode);
1065
+ }
1066
+
1067
+ await this.addNode(newNode, id, selected);
1049
1068
  if (!nodeThatWillBeReplaced) {
1050
1069
  return;
1051
1070
  }
@@ -1062,6 +1081,7 @@ export default {
1062
1081
 
1063
1082
  return newNode;
1064
1083
  },
1084
+
1065
1085
  setShapeCenterUnderCursor(diagram) {
1066
1086
  diagram.bounds.x -= (diagram.bounds.width / 2);
1067
1087
  diagram.bounds.y -= (diagram.bounds.height / 2);
@@ -1073,14 +1093,14 @@ export default {
1073
1093
  const view = newNodeComponent.shapeView;
1074
1094
  await this.$refs.selector.selectElement(view);
1075
1095
  },
1076
- async addNode(node) {
1096
+ async addNode(node, id = null, selected = true) {
1077
1097
  if (!node.pool) {
1078
1098
  node.pool = this.poolTarget;
1079
1099
  }
1080
1100
 
1081
1101
  const targetProcess = node.getTargetProcess(this.processes, this.processNode);
1082
1102
  addNodeToProcess(node, targetProcess);
1083
- node.setIds(this.nodeIdGenerator);
1103
+ node.setIds(this.nodeIdGenerator, id);
1084
1104
 
1085
1105
  this.planeElements.push(node.diagram);
1086
1106
  store.commit('addNode', node);
@@ -1098,9 +1118,11 @@ export default {
1098
1118
  ].includes(node.type)) {
1099
1119
  return;
1100
1120
  }
1101
-
1102
- // Select the node after it has been added to the store (does not apply to flows)
1103
- this.selectNewNode(node);
1121
+ if (selected) {
1122
+ // Select the node after it has been added to the store (does not apply to flows)
1123
+ this.selectNewNode(node);
1124
+ }
1125
+
1104
1126
 
1105
1127
  return new Promise(resolve => {
1106
1128
  setTimeout(() => {
@@ -1123,7 +1145,14 @@ export default {
1123
1145
  this.poolTarget = null;
1124
1146
  });
1125
1147
  },
1126
- async removeNode(node, { removeRelationships = true } = {}) {
1148
+ async removeNode(node, options) {
1149
+ if (this.isMultiplayer) {
1150
+ window.ProcessMaker.EventBus.$emit('multiplayer-removeNode', node);
1151
+ } else {
1152
+ this.removeNodeProcedure(node, options);
1153
+ }
1154
+ },
1155
+ async removeNodeProcedure(node, { removeRelationships = true } = {}) {
1127
1156
  if (!node) {
1128
1157
  // already removed
1129
1158
  return;
@@ -1171,7 +1200,7 @@ export default {
1171
1200
  this.performSingleUndoRedoTransaction(async() => {
1172
1201
  await this.paperManager.performAtomicAction(async() => {
1173
1202
  const { x: clientX, y: clientY } = this.paper.localToClientPoint(node.diagram.bounds);
1174
- const newNode = await this.handleDrop({
1203
+ const newNode = await this.handleDropProcedure({
1175
1204
  clientX, clientY,
1176
1205
  control: { type: typeToReplaceWith },
1177
1206
  nodeThatWillBeReplaced: node,
@@ -1367,7 +1396,7 @@ export default {
1367
1396
  if (this.isSelecting) {
1368
1397
  this.$refs.selector.endSelection(this.paperManager.paper);
1369
1398
  } else {
1370
- this.$refs.selector.stopDrag(event);
1399
+ this.$refs.selector.stopDrag();
1371
1400
  }
1372
1401
  }
1373
1402
  window.ProcessMaker.EventBus.$emit('custom-pointerclick', event);
@@ -1375,6 +1404,9 @@ export default {
1375
1404
  this.dragStart = null;
1376
1405
  this.isSelecting = false;
1377
1406
  },
1407
+ enableMultiplayer() {
1408
+ this.isMultiplayer = true;
1409
+ },
1378
1410
  },
1379
1411
  created() {
1380
1412
  if (runningInCypressTest()) {
@@ -1540,6 +1572,12 @@ export default {
1540
1572
  loadXML: async(xml) => {
1541
1573
  await this.loadXML(xml);
1542
1574
  await undoRedoStore.dispatch('pushState', xml);
1575
+ if (this.isMultiplayer) {
1576
+ window.ProcessMaker.EventBus.$emit('multiplayer-start', {
1577
+ modeler: this,
1578
+ callback: this.enableMultiplayer,
1579
+ });
1580
+ }
1543
1581
  },
1544
1582
  addWarnings: warnings => this.$emit('warnings', warnings),
1545
1583
  addBreadcrumbs: breadcrumbs => this.breadcrumbData.push(breadcrumbs),
@@ -48,6 +48,7 @@ export default {
48
48
  paperManager: Object,
49
49
  useModelGeometry: Boolean,
50
50
  processNode: Object,
51
+ isMultiplayer: Boolean,
51
52
  },
52
53
  data() {
53
54
  return {
@@ -576,6 +577,24 @@ export default {
576
577
  await this.paperManager.awaitScheduledUpdates();
577
578
  this.overPoolStopDrag();
578
579
  this.updateSelectionBox();
580
+ if (this.isMultiplayer) {
581
+ window.ProcessMaker.EventBus.$emit('multiplayer-updateNodes', this.getProperties());
582
+ }
583
+
584
+
585
+ },
586
+ getProperties() {
587
+ const changed = [];
588
+ this.selected.forEach(function(item) {
589
+ changed.push({
590
+ id: item.model.component.node.definition.id,
591
+ properties: {
592
+ clientX: item.model.get('position').x,
593
+ clientY: item.model.get('position').y,
594
+ },
595
+ });
596
+ });
597
+ return changed;
579
598
  },
580
599
  /**
581
600
  * Selector will update the waypoints of the related flows
@@ -70,9 +70,8 @@ export default class Node {
70
70
  this.definition.id = id;
71
71
  }
72
72
 
73
- setIds(nodeIdGenerator) {
74
- const [nodeId, diagramId] = nodeIdGenerator.generate();
75
-
73
+ setIds(nodeIdGenerator, id) {
74
+ const [nodeId, diagramId] = id ? [ id, id + '_di'] : nodeIdGenerator.generate();
76
75
  if (!this.id) {
77
76
  this.id = nodeId;
78
77
  }
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <b-avatar-group class="container">
3
3
  <template v-for="item in players" >
4
- <Avatar :badgeBackgroundColor="item.color" :imgSrc= "item.imgSrc" :userName="item.name" :key="item.key"/>
4
+ <Avatar :badgeBackgroundColor="item.color" :imgSrc="item.imgSrc" :userName="item.name" :key="item.key"/>
5
5
  </template>
6
6
  </b-avatar-group>
7
7
  </template>
@@ -0,0 +1,139 @@
1
+ import * as Y from 'yjs';
2
+ import { WebsocketProvider } from 'y-websocket';
3
+ import { getNodeIdGenerator } from '../NodeIdGenerator';
4
+ import Room from './room';
5
+ export default class Multiplayer {
6
+ ydoc = null;
7
+ yarray = null;
8
+ modeler = null;
9
+ #nodeIdGenerator = null;
10
+ room = null;
11
+ deletedItem = null;
12
+ constructor(modeler) {
13
+ // define document
14
+ this.ydoc = new Y.Doc();
15
+ this.modeler = modeler;
16
+ }
17
+ init() {
18
+ this.#nodeIdGenerator = getNodeIdGenerator(this.modeler.definitions);
19
+
20
+ this.room = new Room(`room-${window.ProcessMaker.modeler.process.id}`);
21
+ const wsProvider = new WebsocketProvider(process.env.VUE_APP_WEBSOCKET_PROVIDER, this.room.getRoom(), this.ydoc);
22
+ wsProvider.on('status', () => {
23
+ // todo status handler
24
+ });
25
+ // array of numbers which produce a sum
26
+ this.yarray = this.ydoc.getArray('modeler');
27
+ // observe changes of the diagram
28
+ this.yarray.observe(event => {
29
+ event.changes.delta.forEach((value) => {
30
+ if (value.insert) {
31
+ value.insert.forEach((value) => {
32
+ this.createShape(value.toJSON());
33
+ this.#nodeIdGenerator.updateCounters();
34
+ });
35
+ }
36
+ });
37
+ // remove nodes observer
38
+ if (event.changes.deleted && event.changes.deleted.size > 0) {
39
+ this.removeShape();
40
+ }
41
+ });
42
+ this.yarray.observeDeep(ymapEventArray => {
43
+ ymapEventArray.forEach((ymap) => {
44
+ const ymapNested = ymap.target ;
45
+ const newProperties = {};
46
+ ymap.changes.keys.forEach((change, key) => {
47
+ if (change.action === 'add') {
48
+ // TODO add new properties
49
+ } else if (change.action === 'update') {
50
+ newProperties[key] = ymapNested.get(key);
51
+ } else if (change.action === 'delete') {
52
+ // TODO delete propertiees
53
+ }
54
+ });
55
+ if (Object.keys(newProperties).length > 0 ) {
56
+ newProperties['id'] = ymapNested.get('id');
57
+ this.updateShapes(newProperties);
58
+ }
59
+ });
60
+ });
61
+ window.ProcessMaker.EventBus.$on('multiplayer-addNode', ( data ) => {
62
+ this.addNode(data);
63
+ });
64
+ window.ProcessMaker.EventBus.$on('multiplayer-removeNode', ( data ) => {
65
+ this.removeNode(data);
66
+ });
67
+ window.ProcessMaker.EventBus.$on('multiplayer-updateNodes', ( data ) => {
68
+ this.updateNodes(data);
69
+ });
70
+ }
71
+ addNode(data) {
72
+ const ymapNested = new Y.Map();
73
+ this.doTransact(ymapNested, data);
74
+ this.yarray.push([ymapNested]);
75
+ }
76
+ createShape(value) {
77
+ this.modeler.handleDropProcedure(value, false);
78
+ }
79
+ removeNode(data) {
80
+ const index = this.getIndex(data.definition.id);
81
+ this.yarray.delete(index, 1); // delete one element
82
+ }
83
+ getIndex(id) {
84
+ let index = -1;
85
+ for (const value of this.yarray) {
86
+ index ++;
87
+ if (value.get('id') === id) {
88
+ break ;
89
+ }
90
+ }
91
+ return index;
92
+ }
93
+ removeShape() {
94
+ const nodes = this.getRemovedNodes(this.modeler.nodes, this.yarray.toArray());
95
+ nodes.forEach((value) => {
96
+ this.modeler.removeNodeProcedure(value, true);
97
+ });
98
+ }
99
+ getRemovedNodes(array1, array2) {
100
+ return array1.filter(object1 => {
101
+ return !array2.some(object2 => {
102
+ return object1.definition.id === object2.get('id');
103
+ });
104
+ });
105
+ }
106
+ updateNodes(data) {
107
+ data.forEach((value) => {
108
+ const index = this.getIndex(value.id);
109
+ const nodeToUpdate = this.yarray.get(index);
110
+ this.doTransact(nodeToUpdate, value.properties);
111
+ });
112
+ }
113
+ doTransact(ymapNested, data) {
114
+ this.ydoc.transact(() => {
115
+ for (const key in data) {
116
+ if (Object.prototype.hasOwnProperty.call(data, key)) {
117
+ ymapNested.set(key, data[key]);
118
+ }
119
+ }
120
+ });
121
+ }
122
+ updateShapes(data) {
123
+ const { paper } = this.modeler;
124
+ const element = this.getJointElement(paper.model, data.id);
125
+ // Update the element's position attribute
126
+ element.set('position', { x:data.clientX, y:data.clientY });
127
+ // Trigger a rendering of the element on the paper
128
+ paper.findViewByModel(element).update();
129
+ }
130
+ getJointElement(graph, targetValue) {
131
+ const cells = graph.getCells();
132
+ for (const cell of cells) {
133
+ if (cell.component.id === targetValue) {
134
+ return cell;
135
+ }
136
+ }
137
+ return null; // Return null if no matching element is found
138
+ }
139
+ }
@@ -0,0 +1,9 @@
1
+ export default class Room {
2
+ #room ='';
3
+ constructor(name) {
4
+ this.#room = name;
5
+ }
6
+ getRoom() {
7
+ return this.#room;
8
+ }
9
+ }
@@ -5,7 +5,7 @@ import './extensions/twitterConnector';
5
5
  import './extensions/testCustomConnector';
6
6
  import './extensions/customMarker';
7
7
  import registerNodes from '@/setup/registerNodes';
8
-
8
+ import Multiplayer from '../multiplayer/multiplayer';
9
9
  const blank = `
10
10
  <?xml version="1.0" encoding="UTF-8"?>
11
11
  <bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="Definitions_03dabax" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="2.0.3">
@@ -20,3 +20,9 @@ window.ProcessMaker.EventBus.$on('modeler-init', registerNodes);
20
20
  window.ProcessMaker.EventBus.$on('modeler-start', ({ loadXML }) => {
21
21
  loadXML(blank);
22
22
  });
23
+ window.ProcessMaker.EventBus.$on('multiplayer-start', (params) => {
24
+ const multiplayer = new Multiplayer(params.modeler);
25
+ multiplayer.init();
26
+ params.callback();
27
+ });
28
+