@processmaker/modeler 1.27.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.
Files changed (32) hide show
  1. package/dist/modeler.common.js +1519 -822
  2. package/dist/modeler.common.js.map +1 -1
  3. package/dist/modeler.umd.js +1519 -822
  4. package/dist/modeler.umd.js.map +1 -1
  5. package/dist/modeler.umd.min.js +3 -3
  6. package/dist/modeler.umd.min.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/NodeIdGenerator.js +55 -22
  9. package/src/NodeInspector.js +2 -2
  10. package/src/components/crown/crownButtons/{duplicateButton.vue → cloneButton.vue} +10 -9
  11. package/src/components/crown/crownConfig/crownConfig.vue +3 -4
  12. package/src/components/crown/crownMultiselect/crownMultiselect.vue +20 -7
  13. package/src/components/crown/utils.js +12 -1
  14. package/src/components/hotkeys/copyPaste.js +7 -4
  15. package/src/components/hotkeys/main.js +6 -1
  16. package/src/components/inspectors/DocumentationFormTextArea.vue +6 -2
  17. package/src/components/inspectors/LoopCharacteristics.vue +5 -2
  18. package/src/components/inspectors/process.js +5 -1
  19. package/src/components/modeler/Modeler.vue +92 -99
  20. package/src/components/modeler/Selection.vue +40 -29
  21. package/src/components/nodes/association/index.js +3 -0
  22. package/src/components/nodes/dataInputAssociation/dataInputAssociation.vue +36 -26
  23. package/src/components/nodes/genericFlow/DataOutputAssociation.js +54 -2
  24. package/src/components/nodes/genericFlow/genericFlow.vue +0 -17
  25. package/src/components/nodes/node.js +81 -8
  26. package/src/components/nodes/pool/poolEventHandlers.js +0 -9
  27. package/src/components/toolbar/ToolBar.vue +21 -9
  28. package/src/components/toolbar/toolbar.scss +2 -1
  29. package/src/mixins/cloneSelection.js +153 -0
  30. package/src/mixins/linkConfig.js +4 -1
  31. package/src/store.js +7 -11
  32. package/src/undoRedoStore.js +4 -19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@processmaker/modeler",
3
- "version": "1.27.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,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
- generate() {
26
- let definitionId = this.#generateDefinitionId();
27
- let diagramId = this.#generateDiagramId();
28
-
29
- while (!this.#isDefinitionIdUnique(definitionId)) {
30
- definitionId = this.#generateDefinitionId();
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
- while (!this.#isDiagramIdUnique(diagramId)) {
34
- diagramId = this.#generateDiagramId();
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 + this.#counter;
42
- this.#counter++;
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 + this.#diagramCounter + '_di';
49
- this.#diagramCounter++;
79
+ const id = NodeIdGenerator.prefix + NodeIdGenerator.#diagramCounter + '_di';
80
+ NodeIdGenerator.#diagramCounter++;
50
81
 
51
82
  return id;
52
83
  };
84
+ }
53
85
 
54
- #isDefinitionIdUnique = id => {
55
- return !this.findById(id) && !this.findById(id, this.definitions.diagrams);
56
- };
86
+ // Singleton instance
87
+ let singleton = null;
57
88
 
58
- #isDiagramIdUnique = id => {
59
- return !this.findById(id) && !this.findById(id, this.definitions.diagrams);
60
- };
89
+ export function getNodeIdGenerator(definitions) {
90
+ if (!singleton) {
91
+ singleton = new NodeIdGenerator(definitions);
92
+ }
93
+ return singleton;
61
94
  }
@@ -1,4 +1,4 @@
1
- import NodeIdGenerator from './NodeIdGenerator';
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 = new NodeIdGenerator(this.definitions);
9
+ this.nodeIdGenerator = getNodeIdGenerator(this.definitions);
10
10
  this.options = Object.assign({
11
11
  prefix: this.nodeIdGenerator.generate()[0],
12
12
  }, options);
@@ -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
  },
@@ -140,7 +140,6 @@ export default {
140
140
  handler() {
141
141
  this.setNodeColor();
142
142
  },
143
- deep: true,
144
143
  },
145
144
  highlightedShapes(shapes, prevShapes) {
146
145
  if (isEqual(shapes, prevShapes)) {
@@ -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');
@@ -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') {
@@ -1,13 +1,16 @@
1
1
  export default {
2
2
  methods: {
3
3
  copyPasteHandler(event, options) {
4
- const isCopy = event.key === 'c';
5
- const isPaste = event.key === 'v';
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';
6
9
 
7
- if (isCopy && options.mod) {
10
+ if (isBody && isCopy && options.mod) {
8
11
  this.copy(event);
9
12
  }
10
- if (isPaste && options.mod) {
13
+ if (isBody && isPaste && options.mod) {
11
14
  this.paste(event);
12
15
  }
13
16
  },
@@ -5,6 +5,11 @@ import moveShapeByKeypress from './moveWithArrowKeys';
5
5
 
6
6
  export default {
7
7
  mixins: [ZoomInOut, CopyPaste],
8
+ computed: {
9
+ clientLeftPaper() {
10
+ return store.getters.clientLeftPaper;
11
+ },
12
+ },
8
13
  mounted() {
9
14
  document.addEventListener('keydown', this.keydownListener);
10
15
  document.addEventListener('keyup', this.keyupListener);
@@ -45,7 +50,7 @@ export default {
45
50
  const scale = this.paperManager.scale;
46
51
  this.canvasDragPosition = { x: x * scale.sx, y: y * scale.sy };
47
52
  }
48
- if (this.canvasDragPosition) {
53
+ if (this.canvasDragPosition && !this.clientLeftPaper) {
49
54
  this.paperManager.translate(
50
55
  event.offsetX - this.canvasDragPosition.x,
51
56
  event.offsetY - this.canvasDragPosition.y
@@ -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
  };
@@ -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.dataOutputs[0].name;
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.dataInputs[0].name;
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
- export default {
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;