@processmaker/modeler 1.28.0 → 1.29.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@processmaker/modeler",
3
- "version": "1.28.0",
3
+ "version": "1.29.0",
4
4
  "scripts": {
5
5
  "serve": "vue-cli-service serve",
6
6
  "open-cypress": "TZ=UTC cypress open",
@@ -1,16 +1,17 @@
1
1
  <template>
2
2
  <crown-button
3
3
  v-if="node.isBpmnType(...validCopyElements)"
4
- :title="$t('Duplicate Element')"
4
+ :title="$t('Clone Element')"
5
5
  v-b-tooltip.hover.viewport.d50="{ customClass: 'no-pointer-events' }"
6
- aria-label="Duplicate Element"
7
- data-test="duplicate-button"
6
+ aria-label="Clone Element"
7
+ data-test="clone-button"
8
8
  role="menuitem"
9
- @click="duplicateElement"
9
+ @click="cloneElement"
10
10
  >
11
11
  <img
12
- :src="duplicateIcon"
12
+ :src="cloneIcon"
13
13
  aria-hidden="true"
14
+ alt="Clone element icon"
14
15
  >
15
16
  </crown-button>
16
17
 
@@ -18,7 +19,7 @@
18
19
 
19
20
  <script>
20
21
  import CrownButton from '@/components/crown/crownButtons/crownButton';
21
- import duplicateIcon from '@/assets/copy-regular.svg';
22
+ import cloneIcon from '@/assets/copy-regular.svg';
22
23
  import validCopyElements from '@/components/crown/crownButtons/validCopyElements';
23
24
 
24
25
  export default {
@@ -27,13 +28,13 @@ export default {
27
28
  data() {
28
29
  return {
29
30
  copyCount: 0,
30
- duplicateIcon,
31
+ cloneIcon,
31
32
  validCopyElements,
32
33
  };
33
34
  },
34
35
  methods: {
35
- duplicateElement() {
36
- this.$emit('duplicate-element', this.node, ++this.copyCount);
36
+ cloneElement() {
37
+ this.$emit('clone-element', this.node, ++this.copyCount);
37
38
  },
38
39
  },
39
40
  };
@@ -50,7 +50,7 @@
50
50
  v-on="$listeners"
51
51
  />
52
52
 
53
- <duplicate-button
53
+ <clone-button
54
54
  :node="node"
55
55
  v-on="$listeners"
56
56
  />
@@ -84,7 +84,7 @@ import GenericFlowButton from '@/components/crown/crownButtons/genericFlowButton
84
84
  import AssociationFlowButton from '@/components/crown/crownButtons/associationFlowButton';
85
85
  import DataAssociationFlowButton from '@/components/crown/crownButtons/dataAssociationFlowButton';
86
86
  import CopyButton from '@/components/crown/crownButtons/copyButton.vue';
87
- import DuplicateButton from '@/components/crown/crownButtons/duplicateButton.vue';
87
+ import CloneButton from '@/components/crown/crownButtons/cloneButton.vue';
88
88
  import CrownDropdowns from '@/components/crown/crownButtons/crownDropdowns';
89
89
  import DefaultFlow from '@/components/crown/crownButtons/defaultFlowButton.vue';
90
90
  import poolLaneCrownConfig from '@/mixins/poolLaneCrownConfig';
@@ -101,7 +101,7 @@ export default {
101
101
  GenericFlowButton,
102
102
  AssociationFlowButton,
103
103
  CopyButton,
104
- DuplicateButton,
104
+ CloneButton,
105
105
  DefaultFlow,
106
106
  DataAssociationFlowButton,
107
107
  },
@@ -8,7 +8,7 @@
8
8
  <slot />
9
9
 
10
10
  <button
11
- v-for="button in buttons"
11
+ v-for="button in availableButtons"
12
12
  :key="button.label"
13
13
  :aria-label="button.label"
14
14
  class="btn"
@@ -30,6 +30,7 @@ import runningInCypressTest from '@/runningInCypressTest';
30
30
  export default {
31
31
  props: {
32
32
  paper: Object,
33
+ hasPools: Boolean,
33
34
  },
34
35
  data() {
35
36
  return {
@@ -42,18 +43,18 @@ export default {
42
43
  nodeToReplace: null,
43
44
  buttons: [
44
45
  {
45
- label: 'Copy Seletion',
46
+ label: 'Copy Selection',
46
47
  icon: 'clipboard',
47
48
  testId: 'copy-button',
48
49
  role: 'menuitem',
49
50
  action: this.copySelection,
50
51
  },
51
52
  {
52
- label: 'Duplicate Selection',
53
+ label: 'Clone Selection',
53
54
  icon: 'copy',
54
- testId: 'duplicate-button',
55
+ testId: 'clone-button',
55
56
  role: 'menuitem',
56
- action: this.duplicateSelection,
57
+ action: this.cloneSelection,
57
58
  },
58
59
  {
59
60
  label: 'Delete Element',
@@ -75,13 +76,25 @@ export default {
75
76
  return countSelected > 1;
76
77
  },
77
78
  highlightedShapes: () => store.getters.highlightedShapes,
79
+ availableButtons() {
80
+ const hasPoolsSelected = this.hasPools;
81
+ return this.buttons.filter(button => {
82
+ if (button.testId === 'copy-button') {
83
+ return !hasPoolsSelected;
84
+ }
85
+ if (button.testId === 'clone-button') {
86
+ return !hasPoolsSelected;
87
+ }
88
+ return true;
89
+ });
90
+ },
78
91
  },
79
92
  methods: {
80
93
  copySelection() {
81
94
  this.$emit('copy-selection');
82
95
  },
83
- duplicateSelection() {
84
- this.$emit('duplicate-selection');
96
+ cloneSelection() {
97
+ this.$emit('clone-selection');
85
98
  },
86
99
  deleteElement() {
87
100
  this.$emit('remove-nodes');
@@ -12,6 +12,7 @@
12
12
  data-test="documentation-text-area"
13
13
  id="documentation-editor"
14
14
  @input="$emit('input', $event)"
15
+ @blur="onBlur"
15
16
  />
16
17
 
17
18
  <b-modal
@@ -34,6 +35,7 @@
34
35
  data-test="documentation-modal-text-area"
35
36
  id="documentation-editor-modal"
36
37
  @input="$emit('input', $event)"
38
+ @blur="onBlur"
37
39
  />
38
40
  </b-modal>
39
41
  </div>
@@ -63,8 +65,10 @@ export default {
63
65
  },
64
66
  },
65
67
  methods: {
66
- emitValue(value) {
67
- this.$emit('input', value);
68
+ onBlur() {
69
+ // Update the undoStack when the Editor loses focus to trigger the autosave.
70
+ const child = this.$root.$children.find((c) => c.$refs.modeler);
71
+ child.$refs.modeler.pushToUndoStack();
68
72
  },
69
73
  },
70
74
  };
@@ -11,6 +11,7 @@
11
11
  @toggle-panels-compressed="panelsCompressed = !panelsCompressed"
12
12
  @toggle-mini-map-open="miniMapOpen = $event"
13
13
  @saveBpmn="saveBpmn"
14
+ @publishTemplate="publishTemplate"
14
15
  @close="close"
15
16
  @save-state="pushToUndoStack"
16
17
  @clearSelection="clearSelection"
@@ -63,7 +64,7 @@
63
64
  :parent-height="parentHeight"
64
65
  :canvas-drag-position="canvasDragPosition"
65
66
  :compressed="panelsCompressed && noElementsSelected"
66
- @shape-resize="shapeResize"
67
+ @shape-resize="shapeResize(false)"
67
68
  />
68
69
 
69
70
  <component
@@ -105,8 +106,8 @@
105
106
  @copy-element="copyElement"
106
107
  @copy-selection="copyElement"
107
108
  @paste-element="pasteElements"
108
- @duplicate-element="duplicateElement"
109
- @duplicate-selection="duplicateSelection"
109
+ @clone-element="cloneElement"
110
+ @clone-selection="cloneSelection"
110
111
  @default-flow="toggleDefaultFlow"
111
112
  @shape-resize="shapeResize"
112
113
  />
@@ -116,7 +117,7 @@
116
117
  :graph="graph"
117
118
  :paperManager="paperManager"
118
119
  :useModelGeometry="false"
119
- @duplicate-selection="duplicateSelection"
120
+ @clone-selection="cloneSelection"
120
121
  @remove-nodes="removeNodes"
121
122
  :processNode="processNode"
122
123
  @save-state="pushToUndoStack"
@@ -196,6 +197,7 @@ export default {
196
197
  data() {
197
198
  return {
198
199
  pasteInProgress: false,
200
+ cloneInProgress: false,
199
201
  internalClipboard: [],
200
202
  tooltipTarget: null,
201
203
 
@@ -306,10 +308,10 @@ export default {
306
308
  isAppleOS() {
307
309
  return typeof navigator !== 'undefined' && /Mac|iPad|iPhone/.test(navigator.platform);
308
310
  },
309
- async shapeResize() {
311
+ async shapeResize(clearIfEmpty=true) {
310
312
  await this.$nextTick();
311
313
  await this.paperManager.awaitScheduledUpdates();
312
- this.$refs.selector.updateSelectionBox(true);
314
+ this.$refs.selector.updateSelectionBox(true, clearIfEmpty);
313
315
  },
314
316
  toggleDefaultFlow(flow) {
315
317
  const source = flow.definition.sourceRef;
@@ -318,7 +320,7 @@ export default {
318
320
  }
319
321
  source.set('default', flow);
320
322
  },
321
- duplicateElement(node, copyCount) {
323
+ cloneElement(node, copyCount) {
322
324
  const clonedNode = node.clone(this.nodeRegistry, this.moddle, this.$t);
323
325
  const yOffset = (node.diagram.bounds.height + 30) * copyCount;
324
326
 
@@ -335,11 +337,14 @@ export default {
335
337
  processId,
336
338
  ];
337
339
  if (this.highlightedNodes.length === 1 && flows.includes(this.highlightedNodes[0].type)) return;
338
- store.commit('setCopiedElements', this.cloneSelection());
340
+ store.commit('setCopiedElements', this.cloneNodesSelection());
339
341
  this.$bvToast.toast(this.$t('Object(s) have been copied'), { noCloseButton:true, variant: 'success', solid: true, toaster: 'b-toaster-top-center' });
340
342
  },
343
+ publishTemplate() {
344
+ this.$emit('publishTemplate');
345
+ },
341
346
  async pasteElements() {
342
- if (this.copiedElements && !this.pasteInProgress) {
347
+ if (this.copiedElements.length > 0 && !this.pasteInProgress) {
343
348
  this.pasteInProgress = true;
344
349
  try {
345
350
  await this.addClonedNodes(this.copiedElements);
@@ -347,24 +352,31 @@ export default {
347
352
  await this.paperManager.awaitScheduledUpdates();
348
353
  await this.$refs.selector.selectElements(this.findViewElementsFromNodes(this.copiedElements), true);
349
354
  await this.$nextTick();
350
- await store.commit('setCopiedElements', this.cloneSelection());
355
+ await store.commit('setCopiedElements', this.cloneNodesSelection());
351
356
  this.scrollToSelection();
352
357
  } finally {
353
358
  this.pasteInProgress = false;
359
+ await this.pushToUndoStack();
354
360
  }
355
361
  }
356
362
  },
357
- async duplicateSelection() {
358
- const clonedNodes = this.cloneSelection();
363
+ async cloneSelection() {
364
+ const clonedNodes = this.cloneNodesSelection();
359
365
  if (clonedNodes && clonedNodes.length === 0) {
360
366
  return;
361
367
  }
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
+ try {
369
+ this.cloneInProgress = true;
370
+ this.$refs.selector.clearSelection();
371
+ await this.addClonedNodes(clonedNodes);
372
+ await this.$nextTick();
373
+ await this.paperManager.awaitScheduledUpdates();
374
+ await this.$refs.selector.selectElements(this.findViewElementsFromNodes(clonedNodes));
375
+ this.scrollToSelection();
376
+ } finally {
377
+ this.cloneInProgress = false;
378
+ await this.pushToUndoStack();
379
+ }
368
380
  },
369
381
  scrollToSelection() {
370
382
  const containerRect = this.$refs['paper-container'].getBoundingClientRect();
@@ -440,9 +452,12 @@ export default {
440
452
  }
441
453
  },
442
454
  async pushToUndoStack() {
455
+ if (this.pasteInProgress || this.cloneInProgress) {
456
+ return;
457
+ }
443
458
  try {
444
459
  const xml = await this.getXmlFromDiagram();
445
- undoRedoStore.dispatch('pushState', xml);
460
+ await undoRedoStore.dispatch('pushState', xml);
446
461
  window.ProcessMaker.EventBus.$emit('modeler-change');
447
462
  } catch (invalidXml) {
448
463
  // eslint-disable-next-line no-console
@@ -793,12 +808,20 @@ export default {
793
808
  this.isRendering = false;
794
809
  this.$emit('parsed');
795
810
  },
796
- async loadXML(xml = this.currentXML) {
811
+ async loadXML(xml = null) {
812
+ let emitChangeEvent = false;
813
+ if (xml === null) {
814
+ xml = this.currentXML;
815
+ emitChangeEvent = true;
816
+ }
797
817
  this.definitions = await this.xmlManager.getDefinitionsFromXml(xml);
798
818
  this.xmlManager.definitions = this.definitions;
799
819
  this.nodeIdGenerator = getNodeIdGenerator(this.definitions);
800
820
  store.commit('clearNodes');
801
- this.renderPaper();
821
+ await this.renderPaper();
822
+ if (emitChangeEvent) {
823
+ window.ProcessMaker.EventBus.$emit('modeler-change');
824
+ }
802
825
  },
803
826
  getBoundaryEvents(process) {
804
827
  return process.get('flowElements').filter(({ $type }) => $type === 'bpmn:BoundaryEvent');
@@ -934,8 +957,6 @@ export default {
934
957
  store.commit('addNode', node);
935
958
  this.poolTarget = null;
936
959
  });
937
-
938
- await this.pushToUndoStack();
939
960
  },
940
961
  async removeNode(node, { removeRelationships = true } = {}) {
941
962
  if (!node) {
@@ -958,10 +979,10 @@ export default {
958
979
  store.commit('highlightNode', this.processNode);
959
980
  this.$refs.selector.clearSelection();
960
981
  await this.$nextTick();
961
- this.pushToUndoStack();
982
+ await this.pushToUndoStack();
962
983
  },
963
984
  async removeNodes() {
964
- this.performSingleUndoRedoTransaction(async() => {
985
+ await this.performSingleUndoRedoTransaction(async() => {
965
986
  await this.paperManager.performAtomicAction(async() => {
966
987
  const waitPromises = [];
967
988
  this.highlightedNodes.forEach((node) =>
@@ -1004,7 +1025,7 @@ export default {
1004
1025
  undoRedoStore.commit('disableSavingState');
1005
1026
  await cb();
1006
1027
  undoRedoStore.commit('enableSavingState');
1007
- this.pushToUndoStack();
1028
+ await this.pushToUndoStack();
1008
1029
  },
1009
1030
  removeNodesFromLane(node) {
1010
1031
  const containingLane = node.pool && node.pool.component.laneSet &&
@@ -1283,9 +1304,9 @@ export default {
1283
1304
 
1284
1305
  /* Register custom nodes */
1285
1306
  window.ProcessMaker.EventBus.$emit('modeler-start', {
1286
- loadXML: xml => {
1287
- this.loadXML(xml);
1288
- undoRedoStore.dispatch('pushState', xml);
1307
+ loadXML: async(xml) => {
1308
+ await this.loadXML(xml);
1309
+ await undoRedoStore.dispatch('pushState', xml);
1289
1310
  },
1290
1311
  addWarnings: warnings => this.$emit('warnings', warnings),
1291
1312
  addBreadcrumbs: breadcrumbs => this.breadcrumbData.push(breadcrumbs),
@@ -16,6 +16,7 @@
16
16
  :plane-elements="$parent.planeElements"
17
17
  :is-rendering="$parent.isRendering"
18
18
  :dropdown-data="[]"
19
+ :has-pools="hasPoolsOrLanesSelected"
19
20
  v-on="$listeners"
20
21
  />
21
22
  </div>
@@ -28,6 +29,11 @@ import CrownMultiselect from '@/components/crown/crownMultiselect/crownMultisele
28
29
  import { id as poolId } from '@/components/nodes/pool/config';
29
30
  import { id as laneId } from '@/components/nodes/poolLane/config';
30
31
  import { id as genericFlowId } from '@/components/nodes/genericFlow/config';
32
+ import { id as sequenceFlowId } from '@/components/nodes/sequenceFlow';
33
+ import { id as associationId } from '@/components/nodes/association';
34
+ import { id as messageFlowId } from '@/components/nodes/messageFlow/config';
35
+ import { id as dataOutputAssociationFlowId } from '@/components/nodes/dataOutputAssociation/config';
36
+ import { id as dataInputAssociationFlowId } from '@/components/nodes/dataInputAssociation/config';
31
37
  import { labelWidth, poolPadding } from '../nodes/pool/poolSizes';
32
38
 
33
39
  export default {
@@ -79,6 +85,14 @@ export default {
79
85
  this.paperManager.paper.on('scale:changed ', this.updateSelectionBox);
80
86
  this.paperManager.paper.on('translate:changed ', this.translateChanged);
81
87
  },
88
+ computed: {
89
+ hasPoolsOrLanesSelected() {
90
+ return this.selected.some((view) => {
91
+ return view.model.component.node.type === poolId ||
92
+ view.model.component.node.type === laneId;
93
+ });
94
+ },
95
+ },
82
96
  watch: {
83
97
  // whenever selected changes
84
98
  selected(newSelected) {
@@ -312,7 +326,7 @@ export default {
312
326
  /**
313
327
  * Update the selection Box
314
328
  */
315
- updateSelectionBox(force=false) {
329
+ updateSelectionBox(force=false, clearIfEmpty=true) {
316
330
  if (force || this.isSelecting && this.style) {
317
331
  if (this.selected.length > 0) {
318
332
  const box = this.getSelectionVertex(this.selected, false, true);
@@ -324,7 +338,7 @@ export default {
324
338
  // Set the dimensions of the element
325
339
  this.style.width = `${box.maxX - box.minX}px`;
326
340
  this.style.height = `${box.maxY - box.minY}px`;
327
- } else {
341
+ } else if (clearIfEmpty) {
328
342
  this.clearSelection();
329
343
  }
330
344
  }
@@ -333,6 +347,13 @@ export default {
333
347
  * Filter the selected elements
334
348
  */
335
349
  filterSelected() {
350
+ const flowTypes = [
351
+ sequenceFlowId,
352
+ dataOutputAssociationFlowId,
353
+ dataInputAssociationFlowId,
354
+ associationId,
355
+ messageFlowId,
356
+ ];
336
357
  // Get the selected pools IDs
337
358
  const selectedPoolsIds = this.selected
338
359
  .filter(shape => shape.model.component)
@@ -343,6 +364,13 @@ export default {
343
364
  if (shape.model.component && shape.model.component.node.pool) {
344
365
  return shape.model.component.node.pool && !selectedPoolsIds.includes(shape.model.component.node.pool.component.node.id);
345
366
  }
367
+ // remove from selection the selected flows that belongs to a selected pools
368
+ if (shape.model.component && flowTypes.includes(shape.model.component.node.type)) {
369
+ const parent = shape.model.getParentCell();
370
+ if (parent.component && parent.component.node.pool) {
371
+ return !selectedPoolsIds.includes(parent.component.node.pool.component.node.id);
372
+ }
373
+ }
346
374
  return true;
347
375
  });
348
376
  },
@@ -615,7 +643,7 @@ export default {
615
643
  /**
616
644
  * Rollback drag an element outside it's pool parent
617
645
  */
618
- rollbackSelection(){
646
+ async rollbackSelection(){
619
647
  const deltaX = this.initialPosition.left - this.left;
620
648
  const deltaY = this.initialPosition.top - this.top;
621
649
  this.style.left = `${this.initialPosition.left}px`;
@@ -624,14 +652,18 @@ export default {
624
652
  const shapesToNotTranslate = [
625
653
  'PoolLane',
626
654
  'standard.Link',
655
+ 'processmaker.components.nodes.boundaryEvent.Shape',
627
656
  ];
628
657
  this.selected.filter(shape => !shapesToNotTranslate.includes(shape.model.get('type')))
629
658
  .forEach(shape => {
630
659
  shape.model.translate(deltaX/scale.sx, deltaY/scale.sy);
631
660
  });
632
661
  this.isOutOfThePool = false;
633
- store.commit('allowSavingElementPosition');
662
+ await store.commit('allowSavingElementPosition');
634
663
  this.paperManager.setStateValid();
664
+ await this.$nextTick();
665
+ await this.paperManager.awaitScheduledUpdates();
666
+ this.updateSelectionBox(true);
635
667
  },
636
668
  /**
637
669
  * Expand and fit the pool container
@@ -187,9 +187,17 @@ export default class Node {
187
187
  clonedFlow.id = null;
188
188
  clonedFlow.pool = this.pool;
189
189
  clonedFlow.cloneOf = this.id;
190
- clonedFlow.diagram.waypoint = [];
190
+ clonedFlow.diagram = moddle.create('bpmndi:BPMNEdge', {
191
+ waypoint: [],
192
+ });
191
193
 
192
- this.diagram.waypoint.forEach(point => clonedFlow.diagram.waypoint.push(point));
194
+ this.diagram.waypoint.forEach(point => {
195
+ const waypoint = moddle.create('dc:Point', {
196
+ x: point.x,
197
+ y: point.y,
198
+ });
199
+ clonedFlow.diagram.waypoint.push(waypoint);
200
+ });
193
201
 
194
202
  Object.keys(this.definition).filter(key => !Node.flowDefinitionPropertiesToNotCopy.includes(key)).forEach(key => {
195
203
  const definition = this.definition.get(key);
@@ -46,14 +46,6 @@ export default class PoolEventHandlers {
46
46
  return;
47
47
  }
48
48
 
49
- if (this.previousValidPosition) {
50
- this.draggingElement.position(this.previousValidPosition.x, this.previousValidPosition.y, { deep: true });
51
- store.commit('updateNodeBounds', {
52
- node: this.draggingElement.component.node,
53
- bounds: this.previousValidPosition,
54
- });
55
- }
56
-
57
49
  if (this.invalidPool) {
58
50
  this.invalidPool.attr('body/fill', poolColor);
59
51
  this.invalidPool = null;
@@ -64,7 +56,6 @@ export default class PoolEventHandlers {
64
56
  this.component.moveElement(this.draggingElement, this.newPool);
65
57
  this.newPool = null;
66
58
  } else {
67
- this.component.expandToFitElement(this.draggingElement, this.shape);
68
59
  this.component.laneSet && this.component.updateLaneChildren();
69
60
  }
70
61
 
@@ -101,7 +101,7 @@
101
101
  </span>
102
102
  </div>
103
103
  <a
104
- class="btn btn-sm btn-primary autosave-btn text-uppercase mx-2"
104
+ class="btn btn-sm btn-primary btn-autosave 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 autosave-btn text-black text-uppercase"
112
+ class="btn btn-sm btn-link toolbar-item btn-autosave text-black text-uppercase"
113
113
  data-test="close-btn"
114
114
  :title="$t('Close')"
115
115
  @click="$emit('close')"
@@ -183,9 +183,6 @@ export default {
183
183
  canRedo() {
184
184
  return undoRedoStore.getters.canRedo;
185
185
  },
186
- saved() {
187
- return undoRedoStore.getters.saved;
188
- },
189
186
  versionStatus() {
190
187
  const status = undoRedoStore.getters.isDraft ? 'Draft' : 'Published';
191
188
  return this.$t(status);
@@ -227,6 +224,11 @@ export default {
227
224
  content: this.$t('Discard Draft'),
228
225
  icon: '',
229
226
  },
227
+ {
228
+ value: 'save-template',
229
+ content: this.$t('Save as Template'),
230
+ icon: '',
231
+ },
230
232
  ],
231
233
  };
232
234
  },
@@ -238,8 +240,7 @@ export default {
238
240
  }
239
241
  undoRedoStore
240
242
  .dispatch('undo')
241
- .then(() => this.$emit('load-xml'))
242
- .then(() => window.ProcessMaker.EventBus.$emit('modeler-change'));
243
+ .then(() => this.$emit('load-xml'));
243
244
  },
244
245
  redo() {
245
246
  this.$emit('clearSelection');
@@ -248,14 +249,16 @@ export default {
248
249
  }
249
250
  undoRedoStore
250
251
  .dispatch('redo')
251
- .then(() => this.$emit('load-xml'))
252
- .then(() => window.ProcessMaker.EventBus.$emit('modeler-change'));
252
+ .then(() => this.$emit('load-xml'));
253
253
  },
254
254
  onNavigate(action) {
255
255
  switch (action.value) {
256
256
  case 'discard-draft':
257
257
  window.ProcessMaker.EventBus.$emit('open-versions-discard-modal');
258
258
  break;
259
+ case 'save-template':
260
+ this.$emit('publishTemplate');
261
+ break;
259
262
  default:
260
263
  break;
261
264
  }
@@ -273,6 +276,15 @@ export default {
273
276
  }
274
277
  },
275
278
  },
279
+ mounted() {
280
+ if (this.$root.$children[0].process.is_template) {
281
+ const indexOfActions = this.ellipsisMenuActions.findIndex(object => {
282
+ return object.value === 'save-template';
283
+ });
284
+
285
+ this.ellipsisMenuActions.splice(indexOfActions, 1);
286
+ }
287
+ },
276
288
  };
277
289
  </script>
278
290
  <style lang="scss" src="./toolbar.scss" />
@@ -28,10 +28,11 @@ $toolbar-background-color: #fff;
28
28
  cursor: default !important;
29
29
  }
30
30
 
31
- .autosave-btn {
31
+ .btn-autosave {
32
32
  display: flex;
33
33
  justify-content: center;
34
34
  align-items: center;
35
+ border-radius: 4px !important;
35
36
  }
36
37
 
37
38
  .btn-ellipsis {
@@ -9,8 +9,8 @@ import { getOrFindDataInput, findIOSpecificationOwner } from '../components/crow
9
9
 
10
10
  export default {
11
11
  methods: {
12
- cloneSelection() {
13
- let clonedNodes = [], clonedFlows = [], clonedDataInputAssociations = [], clonedDataOutputAssociations = [];
12
+ cloneNodesSelection() {
13
+ let clonedNodes = [], clonedFlows = [], clonedBoundaryEvents = [], clonedDataInputAssociations = [], clonedDataOutputAssociations = [];
14
14
  const nodes = this.highlightedNodes;
15
15
  const selector = this.$refs.selector.$el;
16
16
  const flowNodeTypes = [
@@ -42,12 +42,18 @@ export default {
42
42
  clonedDataOutputAssociations.push(clonedFlow);
43
43
  clonedNodes.push(clonedFlow);
44
44
  } else {
45
+ // Validate boundary events and collect in clonedBoundaryEvents array
45
46
  const clonedElement = this.cloneElementAndCalculateOffset(node);
46
- clonedNodes.push(clonedElement);
47
+ if (node.definition && node.definition.$type !== 'bpmn:BoundaryEvent') {
48
+ clonedNodes.push(clonedElement);
49
+ } else {
50
+ clonedBoundaryEvents.push(clonedElement);
51
+ }
47
52
  }
48
53
  });
49
54
  }
50
-
55
+ // Sets the clonedBoundaryEvents at the end of the array
56
+ clonedNodes = [...clonedNodes, ...clonedBoundaryEvents];
51
57
  this.connectClonedFlows(clonedFlows, clonedNodes);
52
58
  this.connectClonedDataInputAssociations(clonedDataInputAssociations, clonedNodes);
53
59
  this.connectClonedDataOutputAssociations(clonedDataOutputAssociations, clonedNodes);
@@ -106,9 +112,11 @@ export default {
106
112
  targetClone.definition.set('incoming', [clonedFlow.definition]);
107
113
  }
108
114
 
115
+ const { height: selectorHeight } = this.$refs.selector.$el.getBoundingClientRect();
116
+ const { sy } = this.paper.scale();
117
+ const yOffset = selectorHeight / sy;
109
118
  clonedFlow.diagram.waypoint.forEach(point => {
110
- const { height: selectorHeight } = this.$refs.selector.$el.getBoundingClientRect();
111
- point.y += selectorHeight;
119
+ point.y += yOffset;
112
120
  });
113
121
  });
114
122
  },