@processmaker/modeler 1.26.0 → 1.28.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 +1790 -740
- package/dist/modeler.common.js.map +1 -1
- package/dist/modeler.umd.js +1790 -740
- 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/NodeIdGenerator.js +55 -22
- package/src/NodeInspector.js +2 -2
- 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 -1
- package/src/components/crown/crownMultiselect/crownMultiselect.vue +15 -6
- package/src/components/crown/utils.js +12 -1
- package/src/components/hotkeys/copyPaste.js +26 -0
- package/src/components/hotkeys/main.js +9 -2
- package/src/components/inspectors/InspectorPanel.vue +1 -0
- package/src/components/inspectors/LoopCharacteristics.vue +5 -2
- package/src/components/inspectors/process.js +5 -1
- package/src/components/modeler/Modeler.vue +104 -4
- package/src/components/modeler/Selection.vue +18 -4
- package/src/components/nodes/association/index.js +3 -0
- package/src/components/nodes/dataInputAssociation/dataInputAssociation.vue +36 -26
- package/src/components/nodes/genericFlow/DataOutputAssociation.js +54 -2
- package/src/components/nodes/genericFlow/genericFlow.vue +0 -17
- package/src/components/nodes/node.js +106 -2
- 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/mixins/cloneSelection.js +145 -0
- package/src/mixins/linkConfig.js +4 -1
- package/src/store.js +11 -0
package/package.json
CHANGED
package/src/NodeIdGenerator.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
export default class NodeIdGenerator {
|
|
2
2
|
static prefix = 'node_';
|
|
3
3
|
|
|
4
|
-
#counter = 1;
|
|
5
|
-
#diagramCounter = 1;
|
|
4
|
+
static #counter = 1;
|
|
5
|
+
static #diagramCounter = 1;
|
|
6
6
|
|
|
7
7
|
constructor(definitions) {
|
|
8
8
|
this.definitions = definitions;
|
|
9
|
+
this.refreshLastIdCounter();
|
|
10
|
+
this.refreshLastDiagramIdCounter();
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
findById(id, root = this.definitions.rootElements, walked = []) {
|
|
@@ -17,45 +19,76 @@ export default class NodeIdGenerator {
|
|
|
17
19
|
} else if (root instanceof Object && root.$type) {
|
|
18
20
|
walked.push(root);
|
|
19
21
|
if (root.id === id) return root;
|
|
20
|
-
Object.getOwnPropertyNames(root).find(key => found = !(root[key] instanceof Function) && this.findById(id, root[key], walked));
|
|
22
|
+
Object.getOwnPropertyNames(root).find(key => found = !(root[key] instanceof Function) && (key.substring(0, 1) !== '$') && this.findById(id, root[key], walked));
|
|
21
23
|
}
|
|
22
24
|
return found;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
matchIds(idRegex, root, walked = [], lastIdCounter = 0) {
|
|
28
|
+
if (walked.indexOf(root) > -1) return lastIdCounter;
|
|
29
|
+
if (root instanceof Array) {
|
|
30
|
+
walked.push(root);
|
|
31
|
+
root.forEach(item => lastIdCounter = this.matchIds(idRegex, item, walked, lastIdCounter));
|
|
32
|
+
} else if (root instanceof Object) {
|
|
33
|
+
walked.push(root);
|
|
34
|
+
if (root.id) {
|
|
35
|
+
const match = String(root.id).match(idRegex);
|
|
36
|
+
const idCounter = match ? parseInt(match[1]) : 0;
|
|
37
|
+
if (idCounter > lastIdCounter) {
|
|
38
|
+
lastIdCounter = idCounter;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
Object.getOwnPropertyNames(root).forEach(key => {
|
|
42
|
+
if (!(root[key] instanceof Function) && (key.substring(0, 1) !== '$')) {
|
|
43
|
+
lastIdCounter = this.matchIds(idRegex, root[key], walked, lastIdCounter);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
31
46
|
}
|
|
47
|
+
return lastIdCounter;
|
|
48
|
+
}
|
|
32
49
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
50
|
+
refreshLastIdCounter() {
|
|
51
|
+
let lastIdCounter = this.matchIds(new RegExp(`^${NodeIdGenerator.prefix}(\\d+)$`), this.definitions.rootElements);
|
|
52
|
+
NodeIdGenerator.#counter = lastIdCounter + 1;
|
|
53
|
+
}
|
|
36
54
|
|
|
55
|
+
getCounter() {
|
|
56
|
+
this.refreshLastIdCounter();
|
|
57
|
+
return NodeIdGenerator.#counter;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
refreshLastDiagramIdCounter() {
|
|
61
|
+
let lastIdCounter = this.matchIds(new RegExp(`^${NodeIdGenerator.prefix}(\\d+)_di$`), this.definitions.diagrams);
|
|
62
|
+
NodeIdGenerator.#diagramCounter = lastIdCounter + 1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
generate() {
|
|
66
|
+
let definitionId = this.#generateDefinitionId();
|
|
67
|
+
let diagramId = this.#generateDiagramId();
|
|
37
68
|
return [definitionId, diagramId];
|
|
38
69
|
}
|
|
39
70
|
|
|
40
71
|
#generateDefinitionId = () => {
|
|
41
|
-
const id = NodeIdGenerator.prefix +
|
|
42
|
-
|
|
72
|
+
const id = NodeIdGenerator.prefix + NodeIdGenerator.#counter;
|
|
73
|
+
NodeIdGenerator.#counter++;
|
|
43
74
|
|
|
44
75
|
return id;
|
|
45
76
|
};
|
|
46
77
|
|
|
47
78
|
#generateDiagramId = () => {
|
|
48
|
-
const id = NodeIdGenerator.prefix +
|
|
49
|
-
|
|
79
|
+
const id = NodeIdGenerator.prefix + NodeIdGenerator.#diagramCounter + '_di';
|
|
80
|
+
NodeIdGenerator.#diagramCounter++;
|
|
50
81
|
|
|
51
82
|
return id;
|
|
52
83
|
};
|
|
84
|
+
}
|
|
53
85
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
};
|
|
86
|
+
// Singleton instance
|
|
87
|
+
let singleton = null;
|
|
57
88
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
89
|
+
export function getNodeIdGenerator(definitions) {
|
|
90
|
+
if (!singleton) {
|
|
91
|
+
singleton = new NodeIdGenerator(definitions);
|
|
92
|
+
}
|
|
93
|
+
return singleton;
|
|
61
94
|
}
|
package/src/NodeInspector.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getNodeIdGenerator } from './NodeIdGenerator';
|
|
2
2
|
import omit from 'lodash/omit';
|
|
3
3
|
|
|
4
4
|
export default class NodeInspector {
|
|
@@ -6,7 +6,7 @@ export default class NodeInspector {
|
|
|
6
6
|
constructor(definitions, options = {}) {
|
|
7
7
|
this.index = window.NODE_INSPECTOR_FIRST_INDEX || new Date().getTime();
|
|
8
8
|
this.definitions = definitions;
|
|
9
|
-
this.nodeIdGenerator =
|
|
9
|
+
this.nodeIdGenerator = getNodeIdGenerator(this.definitions);
|
|
10
10
|
this.options = Object.assign({
|
|
11
11
|
prefix: this.nodeIdGenerator.generate()[0],
|
|
12
12
|
}, options);
|
|
@@ -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
|
},
|
|
@@ -133,7 +140,6 @@ export default {
|
|
|
133
140
|
handler() {
|
|
134
141
|
this.setNodeColor();
|
|
135
142
|
},
|
|
136
|
-
deep: true,
|
|
137
143
|
},
|
|
138
144
|
highlightedShapes(shapes, prevShapes) {
|
|
139
145
|
if (isEqual(shapes, prevShapes)) {
|
|
@@ -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');
|
|
@@ -114,6 +114,7 @@ export function getOrFindDataInput(moddle, task, sourceNode) {
|
|
|
114
114
|
inputSets: [],
|
|
115
115
|
outputSets: [],
|
|
116
116
|
});
|
|
117
|
+
task.definition.ioSpecification.$parent = task.definition;
|
|
117
118
|
}
|
|
118
119
|
// Check if dataInput exists
|
|
119
120
|
if (!task.definition.ioSpecification.dataInputs) {
|
|
@@ -126,6 +127,7 @@ export function getOrFindDataInput(moddle, task, sourceNode) {
|
|
|
126
127
|
isCollection: 'false',
|
|
127
128
|
name: sourceNode.name,
|
|
128
129
|
}));
|
|
130
|
+
task.definition.ioSpecification.dataInputs[task.definition.ioSpecification.dataInputs.length - 1].$parent = task.definition.ioSpecification;
|
|
129
131
|
task.definition.ioSpecification.set('dataInputs', task.definition.ioSpecification.dataInputs);
|
|
130
132
|
}
|
|
131
133
|
dataInput = task.definition.ioSpecification.dataInputs.find(input => input.id === dataInputId);
|
|
@@ -164,13 +166,22 @@ export function getOrFindDataInput(moddle, task, sourceNode) {
|
|
|
164
166
|
}
|
|
165
167
|
inputSet = task.definition.ioSpecification.inputSets[0];
|
|
166
168
|
// Check if dataInputRef exists
|
|
167
|
-
const dataInputRef = inputSet.dataInputRefs.find(ref => ref.id === dataInputId);
|
|
169
|
+
const dataInputRef = inputSet.get('dataInputRefs').find(ref => ref.id === dataInputId);
|
|
168
170
|
if (!dataInputRef) {
|
|
169
171
|
inputSet.dataInputRefs.push(dataInput);
|
|
170
172
|
}
|
|
171
173
|
return dataInput;
|
|
172
174
|
}
|
|
173
175
|
|
|
176
|
+
export function findIOSpecificationOwner(ioSpec, modeler) {
|
|
177
|
+
const owner = ioSpec.$parent;
|
|
178
|
+
if (!owner) {
|
|
179
|
+
return modeler.nodes.find(node => node.definition.ioSpecification === ioSpec ||
|
|
180
|
+
node.definition.ioSpecification?.id === ioSpec.id
|
|
181
|
+
)?.definition;
|
|
182
|
+
}
|
|
183
|
+
return owner;
|
|
184
|
+
}
|
|
174
185
|
|
|
175
186
|
export function removeDataInput(task, sourceNode) {
|
|
176
187
|
if (sourceNode.$type !== 'bpmn:DataObjectReference' && sourceNode.$type !== 'bpmn:DataStoreReference') {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
methods: {
|
|
3
|
+
copyPasteHandler(event, options) {
|
|
4
|
+
const node = event.target.nodeName.toLowerCase();
|
|
5
|
+
const isBody = node === 'body';
|
|
6
|
+
const key = event.key.toLowerCase();
|
|
7
|
+
const isCopy = key === 'c';
|
|
8
|
+
const isPaste = key === 'v';
|
|
9
|
+
|
|
10
|
+
if (isBody && isCopy && options.mod) {
|
|
11
|
+
this.copy(event);
|
|
12
|
+
}
|
|
13
|
+
if (isBody && isPaste && options.mod) {
|
|
14
|
+
this.paste(event);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
copy(event) {
|
|
18
|
+
event.preventDefault();
|
|
19
|
+
window.ProcessMaker.$modeler.copyElement();
|
|
20
|
+
},
|
|
21
|
+
paste(event) {
|
|
22
|
+
event.preventDefault();
|
|
23
|
+
window.ProcessMaker.$modeler.pasteElements();
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
@@ -1,9 +1,15 @@
|
|
|
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],
|
|
8
|
+
computed: {
|
|
9
|
+
clientLeftPaper() {
|
|
10
|
+
return store.getters.clientLeftPaper;
|
|
11
|
+
},
|
|
12
|
+
},
|
|
7
13
|
mounted() {
|
|
8
14
|
document.addEventListener('keydown', this.keydownListener);
|
|
9
15
|
document.addEventListener('keyup', this.keyupListener);
|
|
@@ -12,6 +18,7 @@ export default {
|
|
|
12
18
|
handleHotkeys(event, options) {
|
|
13
19
|
// Pass event to all handlers
|
|
14
20
|
this.zoomInOutHandler(event, options);
|
|
21
|
+
this.copyPasteHandler(event, options);
|
|
15
22
|
},
|
|
16
23
|
keyupListener(event) {
|
|
17
24
|
if (event.code === 'Space') {
|
|
@@ -43,7 +50,7 @@ export default {
|
|
|
43
50
|
const scale = this.paperManager.scale;
|
|
44
51
|
this.canvasDragPosition = { x: x * scale.sx, y: y * scale.sy };
|
|
45
52
|
}
|
|
46
|
-
if (this.canvasDragPosition) {
|
|
53
|
+
if (this.canvasDragPosition && !this.clientLeftPaper) {
|
|
47
54
|
this.paperManager.translate(
|
|
48
55
|
event.offsetX - this.canvasDragPosition.x,
|
|
49
56
|
event.offsetY - this.canvasDragPosition.y
|
|
@@ -115,6 +115,9 @@ export default {
|
|
|
115
115
|
},
|
|
116
116
|
loopMaximum: 0,
|
|
117
117
|
loopCondition: null,
|
|
118
|
+
ioSpecification: {
|
|
119
|
+
dataInputs: [],
|
|
120
|
+
},
|
|
118
121
|
},
|
|
119
122
|
loopType: null,
|
|
120
123
|
multiType: null,
|
|
@@ -218,7 +221,7 @@ export default {
|
|
|
218
221
|
},
|
|
219
222
|
getLoopDataOutputRef() {
|
|
220
223
|
if (!this.local.loopCharacteristics || !this.local.loopCharacteristics.loopDataOutputRef) return null;
|
|
221
|
-
return this.local.ioSpecification
|
|
224
|
+
return this.local.ioSpecification?.dataOutputs[0].name;
|
|
222
225
|
},
|
|
223
226
|
setLoopDataOutputRef(value) {
|
|
224
227
|
if (!this.local.ioSpecification) {
|
|
@@ -252,7 +255,7 @@ export default {
|
|
|
252
255
|
},
|
|
253
256
|
getLoopDataInputRef() {
|
|
254
257
|
if (!this.local.loopCharacteristics || !this.local.loopCharacteristics.loopDataInputRef) return null;
|
|
255
|
-
return this.local.ioSpecification
|
|
258
|
+
return this.local.ioSpecification?.dataInputs[0].name;
|
|
256
259
|
},
|
|
257
260
|
setLoopDataInputRef(value) {
|
|
258
261
|
const dataDef = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import idConfigSettings from './idConfigSettings';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const process = {
|
|
4
4
|
id: 'processmaker-modeler-process',
|
|
5
5
|
bpmnType: 'bpmn:Process',
|
|
6
6
|
control: false,
|
|
@@ -38,3 +38,7 @@ export default {
|
|
|
38
38
|
},
|
|
39
39
|
],
|
|
40
40
|
};
|
|
41
|
+
|
|
42
|
+
export const id = process.id;
|
|
43
|
+
|
|
44
|
+
export default process;
|
|
@@ -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"
|
|
@@ -134,12 +139,13 @@ import InspectorPanel from '@/components/inspectors/InspectorPanel';
|
|
|
134
139
|
import undoRedoStore from '@/undoRedoStore';
|
|
135
140
|
import { Linter } from 'bpmnlint';
|
|
136
141
|
import linterConfig from '../../../.bpmnlintrc';
|
|
137
|
-
import
|
|
142
|
+
import { getNodeIdGenerator } from '../../NodeIdGenerator';
|
|
138
143
|
import Process from '../inspectors/process';
|
|
139
144
|
import runningInCypressTest from '@/runningInCypressTest';
|
|
140
145
|
import getValidationProperties from '@/targetValidationUtils';
|
|
141
146
|
import MiniPaper from '@/components/miniPaper/MiniPaper';
|
|
142
147
|
import { id as laneId } from '@/components/nodes/poolLane/config';
|
|
148
|
+
import { id as processId } from '@/components/inspectors/process';
|
|
143
149
|
import { id as sequenceFlowId } from '../nodes/sequenceFlow';
|
|
144
150
|
import { id as associationId } from '../nodes/association';
|
|
145
151
|
import { id as messageFlowId } from '../nodes/messageFlow/config';
|
|
@@ -162,6 +168,7 @@ import { removeNodeFlows, removeNodeMessageFlows, removeNodeAssociations, remove
|
|
|
162
168
|
import { getInvalidNodes } from '@/components/modeler/modelerUtils';
|
|
163
169
|
import { NodeMigrator } from '@/components/modeler/NodeMigrator';
|
|
164
170
|
import addLoopCharacteristics from '@/setup/addLoopCharacteristics';
|
|
171
|
+
import cloneSelection from '../../mixins/cloneSelection';
|
|
165
172
|
|
|
166
173
|
import ProcessmakerModelerGenericFlow from '@/components/nodes/genericFlow/genericFlow';
|
|
167
174
|
|
|
@@ -185,9 +192,11 @@ export default {
|
|
|
185
192
|
},
|
|
186
193
|
},
|
|
187
194
|
},
|
|
188
|
-
mixins: [hotkeys],
|
|
195
|
+
mixins: [hotkeys, cloneSelection],
|
|
189
196
|
data() {
|
|
190
197
|
return {
|
|
198
|
+
pasteInProgress: false,
|
|
199
|
+
internalClipboard: [],
|
|
191
200
|
tooltipTarget: null,
|
|
192
201
|
|
|
193
202
|
/* Custom parsers for handling certain bpmn node types */
|
|
@@ -285,6 +294,7 @@ export default {
|
|
|
285
294
|
currentXML() {
|
|
286
295
|
return undoRedoStore.getters.currentState;
|
|
287
296
|
},
|
|
297
|
+
copiedElements: () => store.getters.copiedElements,
|
|
288
298
|
/* connectors expect a highlightedNode property */
|
|
289
299
|
highlightedNode: () => store.getters.highlightedNodes[0],
|
|
290
300
|
highlightedNodes: () => store.getters.highlightedNodes,
|
|
@@ -308,13 +318,73 @@ export default {
|
|
|
308
318
|
}
|
|
309
319
|
source.set('default', flow);
|
|
310
320
|
},
|
|
311
|
-
|
|
321
|
+
duplicateElement(node, copyCount) {
|
|
312
322
|
const clonedNode = node.clone(this.nodeRegistry, this.moddle, this.$t);
|
|
313
323
|
const yOffset = (node.diagram.bounds.height + 30) * copyCount;
|
|
314
324
|
|
|
315
325
|
clonedNode.diagram.bounds.y += yOffset;
|
|
316
326
|
this.addNode(clonedNode);
|
|
317
327
|
},
|
|
328
|
+
copyElement() {
|
|
329
|
+
// Checking if User selected a single flow and tries to copy it, to deny it.
|
|
330
|
+
const flows = [
|
|
331
|
+
sequenceFlowId,
|
|
332
|
+
dataOutputAssociationFlowId,
|
|
333
|
+
dataInputAssociationFlowId,
|
|
334
|
+
genericFlowId,
|
|
335
|
+
processId,
|
|
336
|
+
];
|
|
337
|
+
if (this.highlightedNodes.length === 1 && flows.includes(this.highlightedNodes[0].type)) return;
|
|
338
|
+
store.commit('setCopiedElements', this.cloneSelection());
|
|
339
|
+
this.$bvToast.toast(this.$t('Object(s) have been copied'), { noCloseButton:true, variant: 'success', solid: true, toaster: 'b-toaster-top-center' });
|
|
340
|
+
},
|
|
341
|
+
async pasteElements() {
|
|
342
|
+
if (this.copiedElements && !this.pasteInProgress) {
|
|
343
|
+
this.pasteInProgress = true;
|
|
344
|
+
try {
|
|
345
|
+
await this.addClonedNodes(this.copiedElements);
|
|
346
|
+
await this.$nextTick();
|
|
347
|
+
await this.paperManager.awaitScheduledUpdates();
|
|
348
|
+
await this.$refs.selector.selectElements(this.findViewElementsFromNodes(this.copiedElements), true);
|
|
349
|
+
await this.$nextTick();
|
|
350
|
+
await store.commit('setCopiedElements', this.cloneSelection());
|
|
351
|
+
this.scrollToSelection();
|
|
352
|
+
} finally {
|
|
353
|
+
this.pasteInProgress = false;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
async duplicateSelection() {
|
|
358
|
+
const clonedNodes = this.cloneSelection();
|
|
359
|
+
if (clonedNodes && clonedNodes.length === 0) {
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
this.$refs.selector.clearSelection();
|
|
363
|
+
await this.addClonedNodes(clonedNodes);
|
|
364
|
+
await this.$nextTick();
|
|
365
|
+
await this.paperManager.awaitScheduledUpdates();
|
|
366
|
+
await this.$refs.selector.selectElements(this.findViewElementsFromNodes(clonedNodes));
|
|
367
|
+
this.scrollToSelection();
|
|
368
|
+
},
|
|
369
|
+
scrollToSelection() {
|
|
370
|
+
const containerRect = this.$refs['paper-container'].getBoundingClientRect();
|
|
371
|
+
const selector = this.$refs.selector;
|
|
372
|
+
const selectorRect = selector.$el.getBoundingClientRect();
|
|
373
|
+
// Scroll to the cloned elements only when they are not visible on the screen.
|
|
374
|
+
if (selectorRect.right > containerRect.right || selectorRect.bottom > containerRect.bottom || selectorRect.left < containerRect.left || selectorRect.top < containerRect.top) {
|
|
375
|
+
const currentPosition = this.paper.translate();
|
|
376
|
+
const newTy = currentPosition.ty - (selectorRect.top - containerRect.top - selectorRect.height);
|
|
377
|
+
this.paper.translate(currentPosition.tx, newTy);
|
|
378
|
+
selector.updateSelectionBox(true);
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
findViewElementsFromNodes(nodes) {
|
|
382
|
+
return nodes.map(node => {
|
|
383
|
+
const component = this.$refs.nodeComponent.find(cmp => cmp.node === node);
|
|
384
|
+
const shape = component.shape;
|
|
385
|
+
return this.paper.findViewByModel(shape);
|
|
386
|
+
});
|
|
387
|
+
},
|
|
318
388
|
async close() {
|
|
319
389
|
this.$emit('close');
|
|
320
390
|
},
|
|
@@ -726,7 +796,7 @@ export default {
|
|
|
726
796
|
async loadXML(xml = this.currentXML) {
|
|
727
797
|
this.definitions = await this.xmlManager.getDefinitionsFromXml(xml);
|
|
728
798
|
this.xmlManager.definitions = this.definitions;
|
|
729
|
-
this.nodeIdGenerator =
|
|
799
|
+
this.nodeIdGenerator = getNodeIdGenerator(this.definitions);
|
|
730
800
|
store.commit('clearNodes');
|
|
731
801
|
this.renderPaper();
|
|
732
802
|
},
|
|
@@ -851,8 +921,29 @@ export default {
|
|
|
851
921
|
});
|
|
852
922
|
});
|
|
853
923
|
},
|
|
924
|
+
async addClonedNodes(nodes) {
|
|
925
|
+
nodes.forEach(node => {
|
|
926
|
+
if (!node.pool) {
|
|
927
|
+
node.pool = this.poolTarget;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
const targetProcess = node.getTargetProcess(this.processes, this.processNode);
|
|
931
|
+
addNodeToProcess(node, targetProcess);
|
|
932
|
+
|
|
933
|
+
this.planeElements.push(node.diagram);
|
|
934
|
+
store.commit('addNode', node);
|
|
935
|
+
this.poolTarget = null;
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
await this.pushToUndoStack();
|
|
939
|
+
},
|
|
854
940
|
async removeNode(node, { removeRelationships = true } = {}) {
|
|
941
|
+
if (!node) {
|
|
942
|
+
// already removed
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
855
945
|
if (removeRelationships) {
|
|
946
|
+
|
|
856
947
|
removeNodeFlows(node, this);
|
|
857
948
|
removeNodeMessageFlows(node, this);
|
|
858
949
|
removeNodeAssociations(node, this);
|
|
@@ -1152,10 +1243,19 @@ export default {
|
|
|
1152
1243
|
this.pointerUpHandler(event, cellView);
|
|
1153
1244
|
}, this);
|
|
1154
1245
|
|
|
1246
|
+
this.$el.addEventListener('mouseenter', () => {
|
|
1247
|
+
store.commit('setClientLeftPaper', false);
|
|
1248
|
+
});
|
|
1249
|
+
|
|
1155
1250
|
this.$el.addEventListener('mousemove', event => {
|
|
1156
1251
|
this.pointerMoveHandler(event);
|
|
1157
1252
|
});
|
|
1158
1253
|
|
|
1254
|
+
this.$el.addEventListener('mouseleave', () => {
|
|
1255
|
+
this.paperManager.removeEventHandler('blank:pointermove');
|
|
1256
|
+
store.commit('setClientLeftPaper', true);
|
|
1257
|
+
});
|
|
1258
|
+
|
|
1159
1259
|
this.paperManager.addEventHandler('cell:pointerclick', (cellView, evt, x, y) => {
|
|
1160
1260
|
const clickHandler = cellView.model.get('onClick');
|
|
1161
1261
|
if (clickHandler) {
|