@processmaker/screen-builder 2.18.1 → 2.21.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.
@@ -2,7 +2,7 @@ import extensions from './extensions';
2
2
  import ScreenBase from './ScreenBase';
3
3
  import CountElements from '../CountElements';
4
4
  import ValidationsFactory from '../ValidationsFactory';
5
- import _ from 'lodash';
5
+ import _, { debounce, isEqual } from 'lodash';
6
6
 
7
7
  let screenRenderer;
8
8
 
@@ -214,7 +214,12 @@ export default {
214
214
  return name && typeof name === 'string' && name.match(/^[a-zA-Z_][0-9a-zA-Z_.]*$/);
215
215
  },
216
216
  isComputedVariable(name, definition) {
217
- return definition.computed && definition.computed.find(c => c.property === name);
217
+ return definition.computed && definition.computed.some(computed => {
218
+ // Check if the first part of an element's name (up to the first `.`)
219
+ // matches the name of a computed property.
220
+ const regex = new RegExp(`^${computed.property}(\\.|$)`, 'i');
221
+ return regex.test(name);
222
+ });
218
223
  },
219
224
  registerVariable(name, element = {}) {
220
225
  if (!this.validVariableName(name)) {
@@ -298,7 +303,7 @@ export default {
298
303
  Object.keys(component.watch).forEach((key) => {
299
304
  const watch = { deep: true };
300
305
  component.watch[key].forEach(w => Object.assign(watch, w.options));
301
- watch.handler = new Function('value', component.watch[key].map(w => w.code).join('\n'));
306
+ watch.handler = new Function('value', component.watch[key].map(w => `try{${w.code}}catch(e){console.warn(e)}`).join('\n'));
302
307
  component.watch[key] = watch;
303
308
  });
304
309
  // Add validation rules
@@ -336,19 +341,43 @@ export default {
336
341
  },
337
342
  addValidationRulesLoader(component, definition) {
338
343
  const firstPage = parseInt(this.currentPage) || 0;
344
+ function getKeys(input) {
345
+ if (input instanceof Array) {
346
+ const response = [];
347
+ input.forEach((item) => {
348
+ response.push(getKeys(item));
349
+ });
350
+ return response;
351
+ }
352
+ if (!(input instanceof Object)) {
353
+ return typeof input;
354
+ }
355
+ const keys = Object.keys(input);
356
+ const response = {};
357
+ keys.forEach((key) => {
358
+ response[key] = getKeys(input[key]);
359
+ });
360
+ return response;
361
+ }
362
+ let updateValidationRules = function(screenComponent, validations) {
363
+ const a = getKeys(screenComponent.ValidationRules__);
364
+ const b = getKeys(validations);
365
+ if (isEqual(a, b)) {
366
+ return;
367
+ }
368
+ screenComponent.ValidationRules__ = validations;
369
+ screenComponent.$nextTick(() => {
370
+ if (screenComponent.$v) {
371
+ screenComponent.$v.$touch();
372
+ }
373
+ });
374
+ };
375
+ updateValidationRules = debounce(updateValidationRules, 25);
339
376
  component.methods.loadValidationRules = function() {
340
377
  // Asynchronous loading of validations
341
378
  const validations = {};
342
379
  ValidationsFactory(definition, { screen: definition, firstPage, data: {_parent: this._parent, ...this.vdata} }).addValidations(validations).then(() => {
343
- if (_.isEqual(this.ValidationRules__, validations)) {
344
- return;
345
- }
346
- this.ValidationRules__ = validations;
347
- this.$nextTick(() => {
348
- if (this.$v) {
349
- this.$v.$touch();
350
- }
351
- });
380
+ updateValidationRules(this, validations);
352
381
  });
353
382
  };
354
383
  component.mounted.push('this.loadValidationRules()');
@@ -7,7 +7,7 @@ const stringFormats = ['string', 'datetime', 'date', 'password'];
7
7
  export default {
8
8
  schema: [
9
9
  function() {
10
- if (window.ProcessMaker && window.ProcessMaker.packages && window.ProcessMaker.packages.indexOf('package-vocabularies')) {
10
+ if (window.ProcessMaker && window.ProcessMaker.packages && window.ProcessMaker.packages.includes('package-vocabularies')) {
11
11
  if (window.ProcessMaker.VocabulariesSchemaUrl) {
12
12
  let response = window.ProcessMaker.apiClient.get(window.ProcessMaker.VocabulariesSchemaUrl);
13
13
  return response.then(response => {
@@ -60,10 +60,14 @@ export default {
60
60
  };
61
61
  },
62
62
  findParent(child, data = this.vdata, parent = this._parent) {
63
+
63
64
  if (child === data) {
64
65
  return parent;
65
66
  }
66
67
  for (const key in data) {
68
+ if (key === '_parent') {
69
+ continue;
70
+ }
67
71
  if (data[key] instanceof Array) {
68
72
  for (const item of data[key]) {
69
73
  const result = this.findParent(child, item, data);
@@ -120,7 +124,7 @@ export default {
120
124
  },
121
125
  });
122
126
  },
123
- initialValue(component, dataFormat) {
127
+ initialValue(component, dataFormat, config) {
124
128
  let value = null;
125
129
  if (component === 'FormInput') {
126
130
  if (stringFormats.includes(dataFormat)) {
@@ -130,6 +134,10 @@ export default {
130
134
  }
131
135
  } else if (component === 'FormTextArea') {
132
136
  value = '';
137
+ } else if (component === 'FormSelectList' && config.options.allowMultiSelect) {
138
+ value = [];
139
+ } else if (component === 'FormSelectList' && !config.options.allowMultiSelect) {
140
+ value = null;
133
141
  }
134
142
  return value;
135
143
  },
@@ -21,6 +21,8 @@ import {
21
21
  not,
22
22
  or,
23
23
  and,
24
+ maxItems,
25
+ minItems,
24
26
  } from 'vuelidate/lib/validators';
25
27
 
26
28
  export const ValidationMsg = {
@@ -55,6 +57,8 @@ export const ValidationMsg = {
55
57
  invalid_default_value: 'Invalid default value',
56
58
  customDate: 'Must be a valid Date',
57
59
  regex: 'Invalid value',
60
+ maxItems: 'Should NOT have more than {max} items',
61
+ minItems: 'Must have at least {min}',
58
62
  };
59
63
 
60
64
  export const custom_date = (date) => {
@@ -225,4 +229,6 @@ export const validators = {
225
229
  notIn,
226
230
  regex,
227
231
  afterOrEqual: after_or_equal,
232
+ maxItems,
233
+ minItems,
228
234
  };
@@ -3,7 +3,7 @@ import { debounce } from 'lodash';
3
3
 
4
4
  export default {
5
5
  mounted() {
6
- this.refreshValidationRulesByName = debounce(this.refreshValidationRulesByName, 300);
6
+ this.refreshValidationRulesByName = debounce(this.refreshValidationRulesByName, 1000);
7
7
 
8
8
  this.$root.$on('refresh-validation-rules', () => {
9
9
  this.loadValidationRules();
@@ -28,20 +28,9 @@ export default {
28
28
  const data = Object.assign({ _parent: this._parent }, this.vdata);
29
29
  const isVisible = !!Parser.evaluate(rule, Object.assign({}, data));
30
30
 
31
- // Update the array of hidden fields
32
- if (fieldName) {
33
- const fieldExists = this.hiddenFields__.indexOf(fieldName) !== -1;
34
- if (isVisible && fieldExists) {
35
- this.hiddenFields__ = this.hiddenFields__.filter((f) => f !== fieldName);
36
- this.$root.$emit('refresh-validation-rules');
37
- } else if (!isVisible && !fieldExists) {
38
- this.hiddenFields__.push(fieldName);
39
- this.$root.$emit('refresh-validation-rules');
40
- }
41
- }
42
-
43
-
44
- this.refreshValidationRulesByName(fieldName, isVisible);
31
+ window.setTimeout(() => {
32
+ this.refreshValidationRulesByName(fieldName, isVisible);
33
+ }, 1000);
45
34
  return isVisible;
46
35
  } catch (e) {
47
36
  return false;
@@ -1,21 +1,20 @@
1
+ import _ from 'lodash';
1
2
  import { Parser } from 'expr-eval';
2
3
 
3
4
  export default {
4
5
  methods: {
5
6
  evaluateExpression(expression, type) {
6
7
  let value = null;
7
- try {
8
- const self = this;
9
8
 
9
+ const merged = {};
10
+ _.merge(merged, this.vdata, this._data);
11
+
12
+ try {
10
13
  //monitor if variable belongs to data (defined variables) or vdata (external variables)
11
14
  //in this way the event is not executed again when the variable is update
12
- const data = new Proxy(Object.assign({}, this), {
15
+ const data = new Proxy(merged, {
13
16
  get(data, name) {
14
- if (data[name] === undefined) {
15
- return self.vdata[name];
16
- } else {
17
- return data[name];
18
- }
17
+ return data[name];
19
18
  },
20
19
  set() {
21
20
  throw 'You are not allowed to set properties from inside an expression';
@@ -10,7 +10,7 @@ export default {
10
10
  this.addData(screen, v.name, `
11
11
  this.getValue(${JSON.stringify(v.name)}, this.vdata) ||
12
12
  this.getValue(${JSON.stringify(v.name)}, data) ||
13
- this.initialValue('${component}', '${dataFormat}')
13
+ this.initialValue('${component}', '${dataFormat}', ${JSON.stringify(v.config)})
14
14
  `);
15
15
  this.addWatch(screen, v.name, `this.setValue(${JSON.stringify(v.name)}, value, this.vdata);this.setValue(${JSON.stringify(v.name)}, value, this.schema);`);
16
16
  this.addWatch(screen, `vdata.${v.name}`, `this.setValue(${JSON.stringify(v.name)}, value, this);`);
@@ -25,7 +25,8 @@ export default {
25
25
  });
26
26
  },
27
27
  setupDefaultValue(screen, name, value) {
28
- const defaultComputedName = `default_${name}__`;
28
+ const splittedName = name.split('.').join('_DOT_');
29
+ const defaultComputedName = `default_${splittedName}__`;
29
30
  this.addData(screen, `${name}_was_filled__`, `!!this.getValue(${JSON.stringify(name)}, this.vdata) || !!this.getValue(${JSON.stringify(name)}, data)`);
30
31
  this.addMounted(screen, `if (!this.${name}) {
31
32
  this.tryFormField(${JSON.stringify(name)}, () => {this.${this.dot2bracket(name)} = ${value};});
@@ -46,7 +47,8 @@ export default {
46
47
  const name = element.config.name;
47
48
  if (this.isComputedVariable(name, definition)) return;
48
49
  if (element.config.defaultValue || element.config.initiallyChecked) {
49
- const event = `${name}_was_filled__ |= !!$event; !${name}_was_filled__ && (vdata.${this.dot2bracket(name)} = default_${name}__)`;
50
+ const splittedName = name.split('.').join('_DOT_');
51
+ const event = `${name}_was_filled__ |= !!$event; !${name}_was_filled__ && (vdata.${this.dot2bracket(name)} = default_${splittedName}__)`;
50
52
  this.addEvent(properties, 'input', event);
51
53
  }
52
54
  },
@@ -26,7 +26,7 @@ export default {
26
26
  }
27
27
  });
28
28
  },
29
- loadFieldProperties({ properties, element, componentName, definition , formIndex}) {
29
+ loadFieldProperties({ properties, element, componentName, definition , formIndex, screen}) {
30
30
  properties.class = this.elementCssClass(element);
31
31
  properties[':validation-data'] = 'getValidationData()';
32
32
 
@@ -38,7 +38,19 @@ export default {
38
38
  properties[':image'] = this.byRef(element.config.image);
39
39
  } else if (this.validVariableName(element.config.name)) {
40
40
  this.registerVariable(element.config.name, element);
41
- properties['v-model'] = `${element.config.name}`;
41
+ // v-model are not assigned directly to the field name, to prevent invalid references like:
42
+ // `person.content` when `person`=null
43
+ const computed_property = `computedProxy__${element.config.name.split('.').join('_DOT_')}`;
44
+ properties['v-model'] = computed_property;
45
+ screen.computed[computed_property] = {
46
+ get() {
47
+ return this.getValue(element.config.name);
48
+ },
49
+ set(value) {
50
+ this.setValue(element.config.name, value);
51
+ return true;
52
+ },
53
+ };
42
54
  }
43
55
  }
44
56
  // Do not replace mustache in RichText control, it is replaced by the control
@@ -57,7 +69,7 @@ export default {
57
69
  properties[':form-computed'] = JSON.stringify(definition.computed);
58
70
  properties[':form-watchers'] = JSON.stringify(definition.watchers);
59
71
  // Check if control is assigned to a calculated property
60
- const isCalcProp = definition.computed && !!definition.computed.find(computed => computed.property == element.config.name);
72
+ const isCalcProp = this.isComputedVariable(element.config.name, definition);
61
73
  properties[':readonly'] = isCalcProp || element.config.readonly;
62
74
  properties[':disabled'] = isCalcProp || element.config.disabled;
63
75
  // Events
@@ -47,6 +47,7 @@ export default {
47
47
  const addLoopRow = this.createComponent('AddLoopRow', {
48
48
  ':value': element.config.settings.varname,
49
49
  ':config': this.byValue(element.config),
50
+ ':error': `${this.checkVariableExists('$v.vdata.' + element.config.name)} && validationMessage($v.vdata.${element.config.name}) || ${this.checkVariableExists('$v.schema.' + element.config.name)} && validationMessage($v.schema.${element.config.name})`,
50
51
  });
51
52
  loop.appendChild(child);
52
53
  node.appendChild(loop);