@processmaker/modeler 1.26.0 → 1.27.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/dist/img/clipboard.bcc7796a.svg +1 -0
- package/dist/modeler.common.js +1077 -596
- package/dist/modeler.common.js.map +1 -1
- package/dist/modeler.umd.js +1077 -596
- package/dist/modeler.umd.js.map +1 -1
- package/dist/modeler.umd.min.js +3 -3
- package/dist/modeler.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/src/assets/clipboard.svg +1 -0
- package/src/components/crown/crownButtons/copyButton.vue +3 -3
- package/src/components/crown/crownButtons/duplicateButton.vue +40 -0
- package/src/components/crown/crownConfig/crownConfig.vue +7 -0
- package/src/components/crown/crownMultiselect/crownMultiselect.vue +15 -6
- package/src/components/hotkeys/copyPaste.js +23 -0
- package/src/components/hotkeys/main.js +3 -1
- package/src/components/inspectors/InspectorPanel.vue +1 -0
- package/src/components/modeler/Modeler.vue +129 -1
- package/src/components/modeler/Selection.vue +39 -4
- package/src/components/nodes/node.js +39 -0
- package/src/components/toolbar/ToolBar.vue +17 -3
- package/src/components/toolbar/breadcrumb/Breadcrumb.vue +7 -0
- package/src/components/toolbar/toolbar.scss +11 -0
- package/src/store.js +18 -0
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path fill="#fff" d="M280 64h40c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128C0 92.7 28.7 64 64 64h40 9.6C121 27.5 153.3 0 192 0s71 27.5 78.4 64H280zM64 112c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16H320c8.8 0 16-7.2 16-16V128c0-8.8-7.2-16-16-16H304v24c0 13.3-10.7 24-24 24H192 104c-13.3 0-24-10.7-24-24V112H64zm128-8a24 24 0 1 0 0-48 24 24 0 1 0 0 48z"/></svg>
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
aria-hidden="true"
|
|
14
14
|
>
|
|
15
15
|
</crown-button>
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
</template>
|
|
18
18
|
|
|
19
19
|
<script>
|
|
20
20
|
import CrownButton from '@/components/crown/crownButtons/crownButton';
|
|
21
|
-
import copyIcon from '@/assets/
|
|
21
|
+
import copyIcon from '@/assets/clipboard.svg';
|
|
22
22
|
import validCopyElements from '@/components/crown/crownButtons/validCopyElements';
|
|
23
23
|
|
|
24
24
|
export default {
|
|
@@ -33,7 +33,7 @@ export default {
|
|
|
33
33
|
},
|
|
34
34
|
methods: {
|
|
35
35
|
copyElement() {
|
|
36
|
-
this.$emit('copy-element'
|
|
36
|
+
this.$emit('copy-element');
|
|
37
37
|
},
|
|
38
38
|
},
|
|
39
39
|
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<crown-button
|
|
3
|
+
v-if="node.isBpmnType(...validCopyElements)"
|
|
4
|
+
:title="$t('Duplicate Element')"
|
|
5
|
+
v-b-tooltip.hover.viewport.d50="{ customClass: 'no-pointer-events' }"
|
|
6
|
+
aria-label="Duplicate Element"
|
|
7
|
+
data-test="duplicate-button"
|
|
8
|
+
role="menuitem"
|
|
9
|
+
@click="duplicateElement"
|
|
10
|
+
>
|
|
11
|
+
<img
|
|
12
|
+
:src="duplicateIcon"
|
|
13
|
+
aria-hidden="true"
|
|
14
|
+
>
|
|
15
|
+
</crown-button>
|
|
16
|
+
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script>
|
|
20
|
+
import CrownButton from '@/components/crown/crownButtons/crownButton';
|
|
21
|
+
import duplicateIcon from '@/assets/copy-regular.svg';
|
|
22
|
+
import validCopyElements from '@/components/crown/crownButtons/validCopyElements';
|
|
23
|
+
|
|
24
|
+
export default {
|
|
25
|
+
components: { CrownButton },
|
|
26
|
+
props: ['node'],
|
|
27
|
+
data() {
|
|
28
|
+
return {
|
|
29
|
+
copyCount: 0,
|
|
30
|
+
duplicateIcon,
|
|
31
|
+
validCopyElements,
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
methods: {
|
|
35
|
+
duplicateElement() {
|
|
36
|
+
this.$emit('duplicate-element', this.node, ++this.copyCount);
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
</script>
|
|
@@ -50,6 +50,11 @@
|
|
|
50
50
|
v-on="$listeners"
|
|
51
51
|
/>
|
|
52
52
|
|
|
53
|
+
<duplicate-button
|
|
54
|
+
:node="node"
|
|
55
|
+
v-on="$listeners"
|
|
56
|
+
/>
|
|
57
|
+
|
|
53
58
|
<delete-button
|
|
54
59
|
:graph="graph"
|
|
55
60
|
:shape="shape"
|
|
@@ -79,6 +84,7 @@ import GenericFlowButton from '@/components/crown/crownButtons/genericFlowButton
|
|
|
79
84
|
import AssociationFlowButton from '@/components/crown/crownButtons/associationFlowButton';
|
|
80
85
|
import DataAssociationFlowButton from '@/components/crown/crownButtons/dataAssociationFlowButton';
|
|
81
86
|
import CopyButton from '@/components/crown/crownButtons/copyButton.vue';
|
|
87
|
+
import DuplicateButton from '@/components/crown/crownButtons/duplicateButton.vue';
|
|
82
88
|
import CrownDropdowns from '@/components/crown/crownButtons/crownDropdowns';
|
|
83
89
|
import DefaultFlow from '@/components/crown/crownButtons/defaultFlowButton.vue';
|
|
84
90
|
import poolLaneCrownConfig from '@/mixins/poolLaneCrownConfig';
|
|
@@ -95,6 +101,7 @@ export default {
|
|
|
95
101
|
GenericFlowButton,
|
|
96
102
|
AssociationFlowButton,
|
|
97
103
|
CopyButton,
|
|
104
|
+
DuplicateButton,
|
|
98
105
|
DefaultFlow,
|
|
99
106
|
DataAssociationFlowButton,
|
|
100
107
|
},
|
|
@@ -42,11 +42,18 @@ export default {
|
|
|
42
42
|
nodeToReplace: null,
|
|
43
43
|
buttons: [
|
|
44
44
|
{
|
|
45
|
-
label: 'Copy
|
|
46
|
-
icon: '
|
|
45
|
+
label: 'Copy Seletion',
|
|
46
|
+
icon: 'clipboard',
|
|
47
47
|
testId: 'copy-button',
|
|
48
48
|
role: 'menuitem',
|
|
49
|
-
action: this.
|
|
49
|
+
action: this.copySelection,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
label: 'Duplicate Selection',
|
|
53
|
+
icon: 'copy',
|
|
54
|
+
testId: 'duplicate-button',
|
|
55
|
+
role: 'menuitem',
|
|
56
|
+
action: this.duplicateSelection,
|
|
50
57
|
},
|
|
51
58
|
{
|
|
52
59
|
label: 'Delete Element',
|
|
@@ -70,9 +77,11 @@ export default {
|
|
|
70
77
|
highlightedShapes: () => store.getters.highlightedShapes,
|
|
71
78
|
},
|
|
72
79
|
methods: {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
copySelection() {
|
|
81
|
+
this.$emit('copy-selection');
|
|
82
|
+
},
|
|
83
|
+
duplicateSelection() {
|
|
84
|
+
this.$emit('duplicate-selection');
|
|
76
85
|
},
|
|
77
86
|
deleteElement() {
|
|
78
87
|
this.$emit('remove-nodes');
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
methods: {
|
|
3
|
+
copyPasteHandler(event, options) {
|
|
4
|
+
const isCopy = event.key === 'c';
|
|
5
|
+
const isPaste = event.key === 'v';
|
|
6
|
+
|
|
7
|
+
if (isCopy && options.mod) {
|
|
8
|
+
this.copy(event);
|
|
9
|
+
}
|
|
10
|
+
if (isPaste && options.mod) {
|
|
11
|
+
this.paste(event);
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
copy(event) {
|
|
15
|
+
event.preventDefault();
|
|
16
|
+
window.ProcessMaker.$modeler.copyElement();
|
|
17
|
+
},
|
|
18
|
+
paste(event) {
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
window.ProcessMaker.$modeler.pasteElements();
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import ZoomInOut from './zoomInOut';
|
|
2
|
+
import CopyPaste from './copyPaste.js';
|
|
2
3
|
import store from '@/store';
|
|
3
4
|
import moveShapeByKeypress from './moveWithArrowKeys';
|
|
4
5
|
|
|
5
6
|
export default {
|
|
6
|
-
mixins: [ZoomInOut],
|
|
7
|
+
mixins: [ZoomInOut, CopyPaste],
|
|
7
8
|
mounted() {
|
|
8
9
|
document.addEventListener('keydown', this.keydownListener);
|
|
9
10
|
document.addEventListener('keyup', this.keyupListener);
|
|
@@ -12,6 +13,7 @@ export default {
|
|
|
12
13
|
handleHotkeys(event, options) {
|
|
13
14
|
// Pass event to all handlers
|
|
14
15
|
this.zoomInOutHandler(event, options);
|
|
16
|
+
this.copyPasteHandler(event, options);
|
|
15
17
|
},
|
|
16
18
|
keyupListener(event) {
|
|
17
19
|
if (event.code === 'Space') {
|
|
@@ -103,6 +103,10 @@
|
|
|
103
103
|
@replace-node="replaceNode"
|
|
104
104
|
@replace-generic-flow="replaceGenericFlow"
|
|
105
105
|
@copy-element="copyElement"
|
|
106
|
+
@copy-selection="copyElement"
|
|
107
|
+
@paste-element="pasteElements"
|
|
108
|
+
@duplicate-element="duplicateElement"
|
|
109
|
+
@duplicate-selection="duplicateSelection"
|
|
106
110
|
@default-flow="toggleDefaultFlow"
|
|
107
111
|
@shape-resize="shapeResize"
|
|
108
112
|
/>
|
|
@@ -112,6 +116,7 @@
|
|
|
112
116
|
:graph="graph"
|
|
113
117
|
:paperManager="paperManager"
|
|
114
118
|
:useModelGeometry="false"
|
|
119
|
+
@duplicate-selection="duplicateSelection"
|
|
115
120
|
@remove-nodes="removeNodes"
|
|
116
121
|
:processNode="processNode"
|
|
117
122
|
@save-state="pushToUndoStack"
|
|
@@ -188,6 +193,7 @@ export default {
|
|
|
188
193
|
mixins: [hotkeys],
|
|
189
194
|
data() {
|
|
190
195
|
return {
|
|
196
|
+
internalClipboard: [],
|
|
191
197
|
tooltipTarget: null,
|
|
192
198
|
|
|
193
199
|
/* Custom parsers for handling certain bpmn node types */
|
|
@@ -285,6 +291,7 @@ export default {
|
|
|
285
291
|
currentXML() {
|
|
286
292
|
return undoRedoStore.getters.currentState;
|
|
287
293
|
},
|
|
294
|
+
copiedElements: () => store.getters.copiedElements,
|
|
288
295
|
/* connectors expect a highlightedNode property */
|
|
289
296
|
highlightedNode: () => store.getters.highlightedNodes[0],
|
|
290
297
|
highlightedNodes: () => store.getters.highlightedNodes,
|
|
@@ -308,13 +315,112 @@ export default {
|
|
|
308
315
|
}
|
|
309
316
|
source.set('default', flow);
|
|
310
317
|
},
|
|
311
|
-
|
|
318
|
+
duplicateElement(node, copyCount) {
|
|
312
319
|
const clonedNode = node.clone(this.nodeRegistry, this.moddle, this.$t);
|
|
313
320
|
const yOffset = (node.diagram.bounds.height + 30) * copyCount;
|
|
314
321
|
|
|
315
322
|
clonedNode.diagram.bounds.y += yOffset;
|
|
316
323
|
this.addNode(clonedNode);
|
|
317
324
|
},
|
|
325
|
+
copyElement() {
|
|
326
|
+
// Checking if User selected a single flow and tries to copy it, to deny it.
|
|
327
|
+
const flows = [
|
|
328
|
+
sequenceFlowId,
|
|
329
|
+
dataOutputAssociationFlowId,
|
|
330
|
+
dataInputAssociationFlowId,
|
|
331
|
+
genericFlowId,
|
|
332
|
+
];
|
|
333
|
+
if (this.highlightedNodes.length === 1 && flows.includes(this.highlightedNodes[0].type)) return;
|
|
334
|
+
store.commit('setCopiedElements', this.cloneSelection());
|
|
335
|
+
this.$bvToast.toast(this.$t('Object(s) have been copied'), { noCloseButton:true, variant: 'success', solid: true, toaster: 'b-toaster-top-center' });
|
|
336
|
+
},
|
|
337
|
+
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
|
+
}
|
|
343
|
+
},
|
|
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
|
+
async duplicateSelection() {
|
|
413
|
+
const clonedNodes = this.cloneSelection();
|
|
414
|
+
await this.addClonedNodes(clonedNodes);
|
|
415
|
+
this.$refs.selector.selectElements(this.findViewElementsFromNodes(clonedNodes));
|
|
416
|
+
},
|
|
417
|
+
findViewElementsFromNodes(nodes) {
|
|
418
|
+
return nodes.map(node => {
|
|
419
|
+
const component = this.$refs.nodeComponent.find(cmp => cmp.node === node);
|
|
420
|
+
const shape = component.shape;
|
|
421
|
+
return this.paper.findViewByModel(shape);
|
|
422
|
+
});
|
|
423
|
+
},
|
|
318
424
|
async close() {
|
|
319
425
|
this.$emit('close');
|
|
320
426
|
},
|
|
@@ -851,6 +957,22 @@ export default {
|
|
|
851
957
|
});
|
|
852
958
|
});
|
|
853
959
|
},
|
|
960
|
+
async addClonedNodes(nodes) {
|
|
961
|
+
nodes.forEach(node => {
|
|
962
|
+
if (!node.pool) {
|
|
963
|
+
node.pool = this.poolTarget;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
const targetProcess = node.getTargetProcess(this.processes, this.processNode);
|
|
967
|
+
addNodeToProcess(node, targetProcess);
|
|
968
|
+
|
|
969
|
+
this.planeElements.push(node.diagram);
|
|
970
|
+
store.commit('addNode', node);
|
|
971
|
+
this.poolTarget = null;
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
await this.pushToUndoStack();
|
|
975
|
+
},
|
|
854
976
|
async removeNode(node, { removeRelationships = true } = {}) {
|
|
855
977
|
if (removeRelationships) {
|
|
856
978
|
removeNodeFlows(node, this);
|
|
@@ -1153,7 +1275,13 @@ export default {
|
|
|
1153
1275
|
}, this);
|
|
1154
1276
|
|
|
1155
1277
|
this.$el.addEventListener('mousemove', event => {
|
|
1278
|
+
const { clientX, clientY } = event;
|
|
1156
1279
|
this.pointerMoveHandler(event);
|
|
1280
|
+
store.commit('setClientMousePosition', { clientX, clientY });
|
|
1281
|
+
});
|
|
1282
|
+
|
|
1283
|
+
this.$el.addEventListener('mouseleave', () => {
|
|
1284
|
+
store.commit('clientLeftPaper');
|
|
1157
1285
|
});
|
|
1158
1286
|
|
|
1159
1287
|
this.paperManager.addEventHandler('cell:pointerclick', (cellView, evt, x, y) => {
|
|
@@ -29,6 +29,13 @@ 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
|
export default {
|
|
33
40
|
name: 'Selection',
|
|
34
41
|
components: {
|
|
@@ -85,6 +92,17 @@ export default {
|
|
|
85
92
|
},
|
|
86
93
|
},
|
|
87
94
|
methods: {
|
|
95
|
+
async selectElements(elements) {
|
|
96
|
+
await this.$nextTick();
|
|
97
|
+
this.clearSelection();
|
|
98
|
+
this.selected = elements;
|
|
99
|
+
this.showLasso = true;
|
|
100
|
+
this.isSelected = true;
|
|
101
|
+
this.isSelecting = true;
|
|
102
|
+
this.start = null;
|
|
103
|
+
await this.$nextTick();
|
|
104
|
+
this.updateSelectionBox();
|
|
105
|
+
},
|
|
88
106
|
/**
|
|
89
107
|
* Select an element dinamically.
|
|
90
108
|
* Shift key will manage the condition to push to selection
|
|
@@ -321,19 +339,36 @@ export default {
|
|
|
321
339
|
* Filter the selected elements
|
|
322
340
|
*/
|
|
323
341
|
filterSelected() {
|
|
324
|
-
//
|
|
342
|
+
// Get the selected pools IDs
|
|
325
343
|
const selectedPoolsIds = this.selected
|
|
326
344
|
.filter(shape => shape.model.component)
|
|
327
345
|
.filter(shape => shape.model.component.node.type === 'processmaker-modeler-pool')
|
|
328
346
|
.map(shape => shape.model.component.node.id);
|
|
347
|
+
// remove from selection the selected children that belongs to a selected pool
|
|
329
348
|
this.selected = this.selected.filter(shape => {
|
|
330
349
|
if (shape.model.component && shape.model.component.node.pool) {
|
|
331
350
|
return shape.model.component.node.pool && !selectedPoolsIds.includes(shape.model.component.node.pool.component.node.id);
|
|
332
351
|
}
|
|
333
352
|
return true;
|
|
334
|
-
}).filter(shape => {
|
|
335
|
-
return !(shape.model.getParentCell() && shape.model.getParentCell().get('parent'));
|
|
336
353
|
});
|
|
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
|
+
}
|
|
337
372
|
},
|
|
338
373
|
/**
|
|
339
374
|
* Pan paper handler
|
|
@@ -377,7 +412,7 @@ export default {
|
|
|
377
412
|
return shapes && selected.length === shapes.length;
|
|
378
413
|
},
|
|
379
414
|
/**
|
|
380
|
-
* Start the drag procedure for the
|
|
415
|
+
* Start the drag procedure for the select box
|
|
381
416
|
* @param {Object} event
|
|
382
417
|
*/
|
|
383
418
|
startDrag(event) {
|
|
@@ -11,6 +11,7 @@ import cloneDeep from 'lodash/cloneDeep';
|
|
|
11
11
|
export default class Node {
|
|
12
12
|
static diagramPropertiesToCopy = ['x', 'y', 'width', 'height'];
|
|
13
13
|
static definitionPropertiesToNotCopy = ['$type', 'id'];
|
|
14
|
+
static flowDefinitionPropertiesToNotCopy = ['$type', 'id', 'sourceRef', 'targetRef'];
|
|
14
15
|
static eventDefinitionPropertiesToNotCopy = ['errorRef', 'messageRef'];
|
|
15
16
|
|
|
16
17
|
type;
|
|
@@ -89,6 +90,8 @@ export default class Node {
|
|
|
89
90
|
|
|
90
91
|
clonedNode.id = null;
|
|
91
92
|
clonedNode.pool = this.pool;
|
|
93
|
+
clonedNode.definition.cloneOf = this.id;
|
|
94
|
+
|
|
92
95
|
Node.diagramPropertiesToCopy.forEach(prop => clonedNode.diagram.bounds[prop] = this.diagram.bounds[prop]);
|
|
93
96
|
Object.keys(this.definition).filter(key => !Node.definitionPropertiesToNotCopy.includes(key)).forEach(key => {
|
|
94
97
|
const definition = this.definition.get(key);
|
|
@@ -112,6 +115,42 @@ export default class Node {
|
|
|
112
115
|
return clonedNode;
|
|
113
116
|
}
|
|
114
117
|
|
|
118
|
+
cloneFlow(nodeRegistry, moddle, $t) {
|
|
119
|
+
const definition = nodeRegistry[this.type].definition(moddle, $t);
|
|
120
|
+
const diagram = nodeRegistry[this.type].diagram(moddle);
|
|
121
|
+
const clonedFlow = new this.constructor(this.type, definition, diagram);
|
|
122
|
+
|
|
123
|
+
clonedFlow.id = null;
|
|
124
|
+
clonedFlow.pool = this.pool;
|
|
125
|
+
clonedFlow.definition.cloneOf = this.id;
|
|
126
|
+
clonedFlow.diagram.waypoint = [];
|
|
127
|
+
|
|
128
|
+
this.diagram.waypoint.forEach(point => clonedFlow.diagram.waypoint.push(point));
|
|
129
|
+
|
|
130
|
+
Object.keys(this.definition).filter(key => !Node.flowDefinitionPropertiesToNotCopy.includes(key)).forEach(key => {
|
|
131
|
+
const definition = this.definition.get(key);
|
|
132
|
+
const clonedDefinition = typeof definition === 'object' ? cloneDeep(definition) : definition;
|
|
133
|
+
if (key === 'eventDefinitions') {
|
|
134
|
+
for (var i in clonedDefinition) {
|
|
135
|
+
if (definition[i].signalRef && !clonedDefinition[i].signalRef) {
|
|
136
|
+
clonedDefinition[i].signalRef = { ...definition[i].signalRef };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
clonedFlow.definition.set(key, clonedDefinition);
|
|
141
|
+
clonedFlow.definition.sourceRef = clonedFlow.definition.targetRef = null;
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
Node.eventDefinitionPropertiesToNotCopy.forEach(
|
|
145
|
+
prop => clonedFlow.definition.eventDefinitions &&
|
|
146
|
+
clonedFlow.definition.eventDefinitions[0] &&
|
|
147
|
+
clonedFlow.definition.eventDefinitions[0].hasOwnProperty(prop) &&
|
|
148
|
+
clonedFlow.definition.eventDefinitions[0].set(prop, null)
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
return clonedFlow;
|
|
152
|
+
}
|
|
153
|
+
|
|
115
154
|
getTargetProcess(processes, processNode) {
|
|
116
155
|
return this.pool
|
|
117
156
|
? processes.find(({ id }) => id === this.pool.component.node.definition.get('processRef').id)
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
</span>
|
|
102
102
|
</div>
|
|
103
103
|
<a
|
|
104
|
-
class="btn btn-sm btn-primary
|
|
104
|
+
class="btn btn-sm btn-primary autosave-btn text-uppercase mx-2"
|
|
105
105
|
data-test="publish-btn"
|
|
106
106
|
:title="$t('Publish')"
|
|
107
107
|
@click="$emit('saveBpmn')"
|
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
{{ $t('Publish') }}
|
|
110
110
|
</a>
|
|
111
111
|
<a
|
|
112
|
-
class="btn btn-sm btn-link toolbar-item
|
|
112
|
+
class="btn btn-sm btn-link toolbar-item autosave-btn text-black text-uppercase"
|
|
113
113
|
data-test="close-btn"
|
|
114
114
|
:title="$t('Close')"
|
|
115
115
|
@click="$emit('close')"
|
|
@@ -117,9 +117,11 @@
|
|
|
117
117
|
{{ $t('Close') }}
|
|
118
118
|
</a>
|
|
119
119
|
<EllipsisMenu
|
|
120
|
-
@navigate="onNavigate"
|
|
121
120
|
:actions="ellipsisMenuActions"
|
|
122
121
|
:divider="false"
|
|
122
|
+
@navigate="onNavigate"
|
|
123
|
+
@show="onShow"
|
|
124
|
+
@hide="onHide"
|
|
123
125
|
/>
|
|
124
126
|
</template>
|
|
125
127
|
<b-button
|
|
@@ -258,6 +260,18 @@ export default {
|
|
|
258
260
|
break;
|
|
259
261
|
}
|
|
260
262
|
},
|
|
263
|
+
onShow() {
|
|
264
|
+
const inspectorDiv = document.getElementById('inspector');
|
|
265
|
+
if (inspectorDiv) {
|
|
266
|
+
inspectorDiv.style.zIndex = '1';
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
onHide() {
|
|
270
|
+
const inspectorDiv = document.getElementById('inspector');
|
|
271
|
+
if (inspectorDiv) {
|
|
272
|
+
inspectorDiv.style.zIndex = '2';
|
|
273
|
+
}
|
|
274
|
+
},
|
|
261
275
|
},
|
|
262
276
|
};
|
|
263
277
|
</script>
|
|
@@ -27,3 +27,14 @@ $toolbar-background-color: #fff;
|
|
|
27
27
|
.cursor-default {
|
|
28
28
|
cursor: default !important;
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
.autosave-btn {
|
|
32
|
+
display: flex;
|
|
33
|
+
justify-content: center;
|
|
34
|
+
align-items: center;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.btn-ellipsis {
|
|
38
|
+
border-top-left-radius: 4px !important;
|
|
39
|
+
border-bottom-left-radius: 4px !important;
|
|
40
|
+
}
|
package/src/store.js
CHANGED
|
@@ -35,6 +35,9 @@ export default new Vuex.Store({
|
|
|
35
35
|
autoValidate: false,
|
|
36
36
|
globalProcesses: [],
|
|
37
37
|
allowSavingElementPosition: true,
|
|
38
|
+
copiedElements: [],
|
|
39
|
+
clientX: null,
|
|
40
|
+
clientY: null,
|
|
38
41
|
},
|
|
39
42
|
getters: {
|
|
40
43
|
nodes: state => state.nodes,
|
|
@@ -52,6 +55,9 @@ export default new Vuex.Store({
|
|
|
52
55
|
globalProcesses: state => state.globalProcesses,
|
|
53
56
|
globalProcessEvents: (state, getters) => flatten(getters.globalProcesses.map(process => process.events)),
|
|
54
57
|
allowSavingElementPosition: state => state.allowSavingElementPosition,
|
|
58
|
+
copiedElements: state => state.copiedElements,
|
|
59
|
+
clientX: state => state.clientX,
|
|
60
|
+
clientY: state => state.clientY,
|
|
55
61
|
},
|
|
56
62
|
mutations: {
|
|
57
63
|
preventSavingElementPosition(state) {
|
|
@@ -138,6 +144,18 @@ export default new Vuex.Store({
|
|
|
138
144
|
setGlobalProcesses(state, globalProcesses) {
|
|
139
145
|
state.globalProcesses = globalProcesses;
|
|
140
146
|
},
|
|
147
|
+
// Copy Nodes to the clipboard or in this case, to the state
|
|
148
|
+
setCopiedElements(state, elements) {
|
|
149
|
+
state.copiedElements = elements;
|
|
150
|
+
},
|
|
151
|
+
setClientMousePosition(state, position) {
|
|
152
|
+
const { clientX, clientY } = position;
|
|
153
|
+
state = { clientX, clientY };
|
|
154
|
+
},
|
|
155
|
+
clientLeftPaper(state) {
|
|
156
|
+
state.clientX = null;
|
|
157
|
+
state.clientY = null;
|
|
158
|
+
},
|
|
141
159
|
},
|
|
142
160
|
actions: {
|
|
143
161
|
async fetchGlobalProcesses({ commit }) {
|