@processmaker/modeler 1.39.6 → 1.39.8
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/dist/img/cursor.86e6f3e3.svg +3 -0
- package/dist/modeler.common.js +574 -114
- package/dist/modeler.common.js.map +1 -1
- package/dist/modeler.umd.js +574 -114
- package/dist/modeler.umd.js.map +1 -1
- package/dist/modeler.umd.min.js +4 -4
- package/dist/modeler.umd.min.js.map +1 -1
- package/package.json +4 -2
- package/src/NodeIdGenerator.js +8 -0
- package/src/components/modeler/Modeler.vue +54 -12
- package/src/components/multiplayer/remoteCursor/RemoteCursor.vue +67 -0
- package/src/components/multiplayer/remoteCursor/cursor.svg +3 -0
- package/src/components/nodes/node.js +2 -3
- package/src/components/toolbar/ToolBar.vue +2 -0
- package/src/components/topRail/TopRail.vue +7 -1
- package/src/components/topRail/multiplayerViewUsers/MultiplayerViewUsers.vue +22 -0
- package/src/components/topRail/multiplayerViewUsers/avatar/Avatar.vue +137 -0
- package/src/components/topRail/multiplayerViewUsers/avatar/avatar.scss +6 -0
- package/src/multiplayer/multiplayer.js +48 -0
- package/src/multiplayer/room.js +10 -0
- package/src/setup/globals.js +1 -1
- package/src/setup/initialLoad.js +7 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@processmaker/modeler",
|
|
3
|
-
"version": "1.39.
|
|
3
|
+
"version": "1.39.8",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"serve": "vue-cli-service serve",
|
|
6
6
|
"test:unit": "vue-cli-service test:unit",
|
|
@@ -68,7 +68,9 @@
|
|
|
68
68
|
"vue-monaco": "^1.2.1",
|
|
69
69
|
"vue-popperjs": "^2.3.0",
|
|
70
70
|
"vue-upload-component": "^2.8.20",
|
|
71
|
-
"vuex": "^3.5.1"
|
|
71
|
+
"vuex": "^3.5.1",
|
|
72
|
+
"y-websocket": "^1.5.0",
|
|
73
|
+
"yjs": "^13.6.7"
|
|
72
74
|
},
|
|
73
75
|
"devDependencies": {
|
|
74
76
|
"@babel/core": "^7.12.16",
|
package/src/NodeIdGenerator.js
CHANGED
|
@@ -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);
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
@close="close"
|
|
24
24
|
@save-state="pushToUndoStack"
|
|
25
25
|
@clearSelection="clearSelection"
|
|
26
|
+
:players="players"
|
|
26
27
|
@action="handleToolbarAction"
|
|
27
28
|
/>
|
|
28
29
|
<b-row class="modeler h-100">
|
|
@@ -153,6 +154,15 @@
|
|
|
153
154
|
@save-state="pushToUndoStack"
|
|
154
155
|
/>
|
|
155
156
|
</b-row>
|
|
157
|
+
|
|
158
|
+
<RemoteCursor
|
|
159
|
+
v-for="player in players"
|
|
160
|
+
:cursor-color="player.color"
|
|
161
|
+
:username="player.name"
|
|
162
|
+
:key="player.id"
|
|
163
|
+
:top="player.top"
|
|
164
|
+
:left="player.left"
|
|
165
|
+
/>
|
|
156
166
|
</span>
|
|
157
167
|
</template>
|
|
158
168
|
|
|
@@ -213,7 +223,7 @@ import RailBottom from '@/components/railBottom/RailBottom.vue';
|
|
|
213
223
|
import ProcessmakerModelerGenericFlow from '@/components/nodes/genericFlow/genericFlow';
|
|
214
224
|
|
|
215
225
|
import Selection from './Selection';
|
|
216
|
-
|
|
226
|
+
import RemoteCursor from '@/components/multiplayer/remoteCursor/RemoteCursor.vue';
|
|
217
227
|
|
|
218
228
|
export default {
|
|
219
229
|
components: {
|
|
@@ -225,6 +235,7 @@ export default {
|
|
|
225
235
|
ProcessmakerModelerGenericFlow,
|
|
226
236
|
Selection,
|
|
227
237
|
RailBottom,
|
|
238
|
+
RemoteCursor,
|
|
228
239
|
},
|
|
229
240
|
props: {
|
|
230
241
|
owner: Object,
|
|
@@ -313,8 +324,11 @@ export default {
|
|
|
313
324
|
isSelecting: false,
|
|
314
325
|
isIntoTheSelection: false,
|
|
315
326
|
dragStart: null,
|
|
327
|
+
players: [],
|
|
316
328
|
showInspectorButton: true,
|
|
317
329
|
inspectorButtonRight: 65,
|
|
330
|
+
multiplayer: null,
|
|
331
|
+
isMultiplayer: false,
|
|
318
332
|
};
|
|
319
333
|
},
|
|
320
334
|
watch: {
|
|
@@ -907,7 +921,7 @@ export default {
|
|
|
907
921
|
|
|
908
922
|
const config = definition.config ? JSON.parse(definition.config) : {};
|
|
909
923
|
const type = config?.processKey || parser(definition, this.moddle);
|
|
910
|
-
|
|
924
|
+
|
|
911
925
|
const unnamedElements = ['bpmn:TextAnnotation', 'bpmn:Association', 'bpmn:DataOutputAssociation', 'bpmn:DataInputAssociation'];
|
|
912
926
|
const requireName = unnamedElements.indexOf(bpmnType) === -1;
|
|
913
927
|
if (requireName && !definition.get('name')) {
|
|
@@ -1012,7 +1026,21 @@ export default {
|
|
|
1012
1026
|
control,
|
|
1013
1027
|
});
|
|
1014
1028
|
},
|
|
1015
|
-
|
|
1029
|
+
handleDrop(data) {
|
|
1030
|
+
const { clientX, clientY, control} = data;
|
|
1031
|
+
if (this.isMultiplayer) {
|
|
1032
|
+
window.ProcessMaker.EventBus.$emit('multiplayer-addNode', {
|
|
1033
|
+
clientX,
|
|
1034
|
+
clientY,
|
|
1035
|
+
control,
|
|
1036
|
+
id: `node_${this.nodeIdGenerator.getDefinitionNumber()}`,
|
|
1037
|
+
});
|
|
1038
|
+
} else {
|
|
1039
|
+
this.handleDropProcedure(data);
|
|
1040
|
+
}
|
|
1041
|
+
},
|
|
1042
|
+
async handleDropProcedure(data, selected=true) {
|
|
1043
|
+
const { clientX, clientY, control, nodeThatWillBeReplaced, id } = data;
|
|
1016
1044
|
this.validateDropTarget({ clientX, clientY, control });
|
|
1017
1045
|
if (!this.allowDrop) {
|
|
1018
1046
|
return;
|
|
@@ -1031,9 +1059,11 @@ export default {
|
|
|
1031
1059
|
if (newNode.isBpmnType('bpmn:BoundaryEvent')) {
|
|
1032
1060
|
this.setShapeCenterUnderCursor(diagram);
|
|
1033
1061
|
}
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1062
|
+
if (selected) {
|
|
1063
|
+
this.highlightNode(newNode);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
await this.addNode(newNode, id, selected);
|
|
1037
1067
|
if (!nodeThatWillBeReplaced) {
|
|
1038
1068
|
return;
|
|
1039
1069
|
}
|
|
@@ -1050,6 +1080,7 @@ export default {
|
|
|
1050
1080
|
|
|
1051
1081
|
return newNode;
|
|
1052
1082
|
},
|
|
1083
|
+
|
|
1053
1084
|
setShapeCenterUnderCursor(diagram) {
|
|
1054
1085
|
diagram.bounds.x -= (diagram.bounds.width / 2);
|
|
1055
1086
|
diagram.bounds.y -= (diagram.bounds.height / 2);
|
|
@@ -1061,14 +1092,14 @@ export default {
|
|
|
1061
1092
|
const view = newNodeComponent.shapeView;
|
|
1062
1093
|
await this.$refs.selector.selectElement(view);
|
|
1063
1094
|
},
|
|
1064
|
-
async addNode(node) {
|
|
1095
|
+
async addNode(node, id = null, selected = true) {
|
|
1065
1096
|
if (!node.pool) {
|
|
1066
1097
|
node.pool = this.poolTarget;
|
|
1067
1098
|
}
|
|
1068
1099
|
|
|
1069
1100
|
const targetProcess = node.getTargetProcess(this.processes, this.processNode);
|
|
1070
1101
|
addNodeToProcess(node, targetProcess);
|
|
1071
|
-
node.setIds(this.nodeIdGenerator);
|
|
1102
|
+
node.setIds(this.nodeIdGenerator, id);
|
|
1072
1103
|
|
|
1073
1104
|
this.planeElements.push(node.diagram);
|
|
1074
1105
|
store.commit('addNode', node);
|
|
@@ -1086,9 +1117,11 @@ export default {
|
|
|
1086
1117
|
].includes(node.type)) {
|
|
1087
1118
|
return;
|
|
1088
1119
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1120
|
+
if (selected) {
|
|
1121
|
+
// Select the node after it has been added to the store (does not apply to flows)
|
|
1122
|
+
this.selectNewNode(node);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1092
1125
|
|
|
1093
1126
|
return new Promise(resolve => {
|
|
1094
1127
|
setTimeout(() => {
|
|
@@ -1159,7 +1192,7 @@ export default {
|
|
|
1159
1192
|
this.performSingleUndoRedoTransaction(async() => {
|
|
1160
1193
|
await this.paperManager.performAtomicAction(async() => {
|
|
1161
1194
|
const { x: clientX, y: clientY } = this.paper.localToClientPoint(node.diagram.bounds);
|
|
1162
|
-
const newNode = await this.
|
|
1195
|
+
const newNode = await this.handleDropProcedure({
|
|
1163
1196
|
clientX, clientY,
|
|
1164
1197
|
control: { type: typeToReplaceWith },
|
|
1165
1198
|
nodeThatWillBeReplaced: node,
|
|
@@ -1363,6 +1396,9 @@ export default {
|
|
|
1363
1396
|
this.dragStart = null;
|
|
1364
1397
|
this.isSelecting = false;
|
|
1365
1398
|
},
|
|
1399
|
+
enableMultiplayer() {
|
|
1400
|
+
this.isMultiplayer = true;
|
|
1401
|
+
},
|
|
1366
1402
|
},
|
|
1367
1403
|
created() {
|
|
1368
1404
|
if (runningInCypressTest()) {
|
|
@@ -1528,6 +1564,12 @@ export default {
|
|
|
1528
1564
|
loadXML: async(xml) => {
|
|
1529
1565
|
await this.loadXML(xml);
|
|
1530
1566
|
await undoRedoStore.dispatch('pushState', xml);
|
|
1567
|
+
if (this.isMultiplayer) {
|
|
1568
|
+
window.ProcessMaker.EventBus.$emit('multiplayer-start', {
|
|
1569
|
+
modeler: this,
|
|
1570
|
+
callback: this.enableMultiplayer,
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1531
1573
|
},
|
|
1532
1574
|
addWarnings: warnings => this.$emit('warnings', warnings),
|
|
1533
1575
|
addBreadcrumbs: breadcrumbs => this.breadcrumbData.push(breadcrumbs),
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="remote-cursor" :style="{ left: left + 'px', top: top + 'px' }">
|
|
3
|
+
<inline-svg :src="cursorIcon" :fill="cursorColor" />
|
|
4
|
+
|
|
5
|
+
<div class="remote-username">
|
|
6
|
+
{{ username }}
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script>
|
|
12
|
+
import InlineSvg from 'vue-inline-svg';
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
components: {
|
|
16
|
+
InlineSvg,
|
|
17
|
+
},
|
|
18
|
+
props: {
|
|
19
|
+
cursorColor: {
|
|
20
|
+
type: String,
|
|
21
|
+
default: '#000000',
|
|
22
|
+
},
|
|
23
|
+
username: {
|
|
24
|
+
type: String,
|
|
25
|
+
},
|
|
26
|
+
top: {
|
|
27
|
+
type: Number,
|
|
28
|
+
},
|
|
29
|
+
left: {
|
|
30
|
+
type: Number,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
data() {
|
|
34
|
+
return {
|
|
35
|
+
cursorIcon: require('@/components/multiplayer/remoteCursor/cursor.svg'),
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<style scoped lang="scss">
|
|
42
|
+
.remote {
|
|
43
|
+
&-cursor {
|
|
44
|
+
position: absolute;
|
|
45
|
+
display: flex;
|
|
46
|
+
width: auto;
|
|
47
|
+
height: 34px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&-username {
|
|
51
|
+
display: flex;
|
|
52
|
+
justify-content: center;
|
|
53
|
+
align-items: center;
|
|
54
|
+
margin-top: 12px;
|
|
55
|
+
padding: 4px 10px;
|
|
56
|
+
gap: 10px;
|
|
57
|
+
border-radius: 4px;
|
|
58
|
+
background-color: #212529;
|
|
59
|
+
|
|
60
|
+
color: #FFFFFF;
|
|
61
|
+
font-size: 12px;
|
|
62
|
+
font-style: normal;
|
|
63
|
+
font-weight: 400;
|
|
64
|
+
line-height: normal;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
</style>
|
|
@@ -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 + '_di'] : nodeIdGenerator.generate();
|
|
76
75
|
if (!this.id) {
|
|
77
76
|
this.id = nodeId;
|
|
78
77
|
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
<TopRail
|
|
9
9
|
:validation-errors="validationErrors"
|
|
10
10
|
:warnings="warnings"
|
|
11
|
+
:players="players"
|
|
11
12
|
>
|
|
12
13
|
<component
|
|
13
14
|
:is="component.button"
|
|
@@ -129,6 +130,7 @@ export default {
|
|
|
129
130
|
'warnings',
|
|
130
131
|
'xmlManager',
|
|
131
132
|
'validationBar',
|
|
133
|
+
'players',
|
|
132
134
|
'extraActions',
|
|
133
135
|
],
|
|
134
136
|
watch: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="top-rail-container">
|
|
3
|
+
<MultiplayerViewUsers :players="players"/>
|
|
3
4
|
<ValidateIssue
|
|
4
5
|
v-show="isOpenIssue"
|
|
5
6
|
:number-of-errors="numberOfErrors"
|
|
@@ -20,12 +21,13 @@
|
|
|
20
21
|
<script>
|
|
21
22
|
import store from '@/store';
|
|
22
23
|
import { ValidateButton, ValidateIssue, ValidatePanel } from '@/components/topRail/validateControl';
|
|
23
|
-
|
|
24
|
+
import MultiplayerViewUsers from '@/components/topRail/multiplayerViewUsers/MultiplayerViewUsers';
|
|
24
25
|
export default {
|
|
25
26
|
components: {
|
|
26
27
|
ValidateButton,
|
|
27
28
|
ValidateIssue,
|
|
28
29
|
ValidatePanel,
|
|
30
|
+
MultiplayerViewUsers,
|
|
29
31
|
},
|
|
30
32
|
props: {
|
|
31
33
|
validationErrors: {
|
|
@@ -36,6 +38,10 @@ export default {
|
|
|
36
38
|
type: Array,
|
|
37
39
|
required: true,
|
|
38
40
|
},
|
|
41
|
+
players: {
|
|
42
|
+
type: Array,
|
|
43
|
+
required: false,
|
|
44
|
+
},
|
|
39
45
|
},
|
|
40
46
|
data() {
|
|
41
47
|
return {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<b-avatar-group class="container">
|
|
3
|
+
<template v-for="item in players" >
|
|
4
|
+
<Avatar :badgeBackgroundColor="item.color" :imgSrc="item.imgSrc" :userName="item.name" :key="item.key"/>
|
|
5
|
+
</template>
|
|
6
|
+
</b-avatar-group>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script>
|
|
10
|
+
import Avatar from '@/components/topRail/multiplayerViewUsers/avatar/Avatar';
|
|
11
|
+
export default {
|
|
12
|
+
components:{
|
|
13
|
+
Avatar,
|
|
14
|
+
},
|
|
15
|
+
props: {
|
|
16
|
+
players: {
|
|
17
|
+
type: Array,
|
|
18
|
+
required: true,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
</script>
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<span
|
|
3
|
+
class="b-avatar rounded-circle"
|
|
4
|
+
:style="{'backgroundColor': generateColorHsl(userName, saturationRange, lightnessRange)}"
|
|
5
|
+
>
|
|
6
|
+
<span v-if="imgSrc" class="b-avatar-img">
|
|
7
|
+
<img :src="imgSrc" :alt=userName>
|
|
8
|
+
</span>
|
|
9
|
+
<span v-else class="b-avatar-text avatar-initials">
|
|
10
|
+
<span>
|
|
11
|
+
{{ this.getInitials(userName) }}
|
|
12
|
+
</span>
|
|
13
|
+
|
|
14
|
+
</span>
|
|
15
|
+
<span class="b-avatar-badge badge-danger"
|
|
16
|
+
:style="{bottom: '0px', right: '0px', backgroundColor: badgeBackgroundColor}"
|
|
17
|
+
/>
|
|
18
|
+
</span>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<script>
|
|
22
|
+
|
|
23
|
+
export default {
|
|
24
|
+
props: {
|
|
25
|
+
badgeBackgroundColor: {
|
|
26
|
+
type: String,
|
|
27
|
+
required: false,
|
|
28
|
+
},
|
|
29
|
+
imgSrc: {
|
|
30
|
+
type: String,
|
|
31
|
+
required: false,
|
|
32
|
+
},
|
|
33
|
+
userName: {
|
|
34
|
+
type: String,
|
|
35
|
+
required: false,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
data() {
|
|
39
|
+
return {
|
|
40
|
+
saturation: 50,
|
|
41
|
+
lightness: 50,
|
|
42
|
+
range: 10,
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
computed: {
|
|
46
|
+
saturationRange() {
|
|
47
|
+
return this.getRange(this.saturation, this.range);
|
|
48
|
+
},
|
|
49
|
+
lightnessRange() {
|
|
50
|
+
return this.getRange(this.lightness, this.range);
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
methods: {
|
|
54
|
+
/**
|
|
55
|
+
* Get the initials from a given name.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} name - The full name from which to extract initials.
|
|
58
|
+
* @returns {string} The initials of the first and last names.
|
|
59
|
+
*/
|
|
60
|
+
getInitials(name = '') {
|
|
61
|
+
const nameArray = name.split(' ');
|
|
62
|
+
const firstNameIn = nameArray[0].charAt(0).toUpperCase();
|
|
63
|
+
const lastNameIn = nameArray[nameArray.length - 1].charAt(0).toUpperCase();
|
|
64
|
+
return `${firstNameIn}${lastNameIn}`;
|
|
65
|
+
},
|
|
66
|
+
/**
|
|
67
|
+
* Calculates a hash value for a given string.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} str - The input string for which the hash needs to be calculated.
|
|
70
|
+
* @returns {number} The calculated hash value for the input string.
|
|
71
|
+
*/
|
|
72
|
+
getHashOfString(str){
|
|
73
|
+
let hash = 0;
|
|
74
|
+
for (let i = 0; i < str.length; i++) {
|
|
75
|
+
hash = str.charCodeAt(i) + ((hash << 5) - hash);
|
|
76
|
+
}
|
|
77
|
+
hash = Math.abs(hash);
|
|
78
|
+
return hash;
|
|
79
|
+
},
|
|
80
|
+
/**
|
|
81
|
+
* Calculates a range around a given value.
|
|
82
|
+
*
|
|
83
|
+
* @param {number} value - The central value.
|
|
84
|
+
* @param {number} range - The range value.
|
|
85
|
+
* @returns {number[]} An array containing the lower and upper bounds of the range.
|
|
86
|
+
*/
|
|
87
|
+
getRange(value, range) {
|
|
88
|
+
return [Math.max(0, value-range), Math.min(value + range, 100)];
|
|
89
|
+
},
|
|
90
|
+
/**
|
|
91
|
+
* Get the hash number to within our range
|
|
92
|
+
*
|
|
93
|
+
* @param {Number} hash
|
|
94
|
+
* @param {Number} min
|
|
95
|
+
* @param {Number} max
|
|
96
|
+
* @returns {Number}
|
|
97
|
+
*/
|
|
98
|
+
normalizeHash(hash, min, max){
|
|
99
|
+
return Math.floor((hash % (max - min)) + min);
|
|
100
|
+
},
|
|
101
|
+
/**
|
|
102
|
+
*Generate Unique Color, create a string using our h,s,l values.
|
|
103
|
+
* @param {String} name
|
|
104
|
+
* @param {Array} saturationRange
|
|
105
|
+
* @param {Array} lightnessRange
|
|
106
|
+
* @returns {Number}
|
|
107
|
+
*/
|
|
108
|
+
generateHSL(name, saturationRange, lightnessRange) {
|
|
109
|
+
const hash = this.getHashOfString(name);
|
|
110
|
+
const h = this.normalizeHash(hash, 0, 360);
|
|
111
|
+
const s = this.normalizeHash(hash, saturationRange[0], saturationRange[1]);
|
|
112
|
+
const l = this.normalizeHash(hash, lightnessRange[0], lightnessRange[1]);
|
|
113
|
+
return [h, s, l];
|
|
114
|
+
},
|
|
115
|
+
/**
|
|
116
|
+
* Convert HSL array to string
|
|
117
|
+
* @param {Array} hsl
|
|
118
|
+
* @returns {String}
|
|
119
|
+
*/
|
|
120
|
+
HSLtoString(hsl) {
|
|
121
|
+
return `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`;
|
|
122
|
+
},
|
|
123
|
+
/**
|
|
124
|
+
* Generate a unique hsl value.
|
|
125
|
+
* @param {String} name
|
|
126
|
+
* @param {Array} saturationRange
|
|
127
|
+
* @param {Array} lightnessRange
|
|
128
|
+
* @returns {String}
|
|
129
|
+
*/
|
|
130
|
+
generateColorHsl(id, saturationRange, lightnessRange) {
|
|
131
|
+
return this.HSLtoString(this.generateHSL(id, saturationRange, lightnessRange));
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
</script>
|
|
137
|
+
<style scoped lang="scss" src="./avatar.scss"></style>
|
|
@@ -0,0 +1,48 @@
|
|
|
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
|
+
constructor(modeler) {
|
|
12
|
+
// define document
|
|
13
|
+
this.ydoc = new Y.Doc();
|
|
14
|
+
this.modeler = modeler;
|
|
15
|
+
}
|
|
16
|
+
init() {
|
|
17
|
+
this.#nodeIdGenerator = getNodeIdGenerator(this.modeler.definitions);
|
|
18
|
+
|
|
19
|
+
this.room = new Room('room-' + window.ProcessMaker.modeler.process.id);
|
|
20
|
+
const wsProvider = new WebsocketProvider('ws://localhost:1234', this.room.getRoom(), this.ydoc);
|
|
21
|
+
wsProvider.on('status', () => {
|
|
22
|
+
// todo status handler
|
|
23
|
+
});
|
|
24
|
+
// array of numbers which produce a sum
|
|
25
|
+
this.yarray = this.ydoc.getArray('modeler');
|
|
26
|
+
// observe changes of the diagram
|
|
27
|
+
this.yarray.observe(event => {
|
|
28
|
+
event.changes.delta.forEach((value) =>{
|
|
29
|
+
if (value.insert) {
|
|
30
|
+
value.insert.forEach((value) => {
|
|
31
|
+
this.createShape(value);
|
|
32
|
+
this.#nodeIdGenerator.updateCounters();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
window.ProcessMaker.EventBus.$on('multiplayer-addNode', ( data ) => {
|
|
38
|
+
this.addNode(data);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
addNode(data) {
|
|
42
|
+
this.yarray.push([data]);
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
createShape(value) {
|
|
46
|
+
this.modeler.handleDropProcedure(value, false);
|
|
47
|
+
}
|
|
48
|
+
}
|
package/src/setup/globals.js
CHANGED
package/src/setup/initialLoad.js
CHANGED
|
@@ -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
|
+
|