@processmaker/screen-builder 3.8.17 → 3.8.19

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/screen-builder",
3
- "version": "3.8.17",
3
+ "version": "3.8.19",
4
4
  "scripts": {
5
5
  "dev": "VITE_COVERAGE=true vite",
6
6
  "build": "vite build",
@@ -61,7 +61,7 @@
61
61
  "@fortawesome/fontawesome-free": "^5.6.1",
62
62
  "@originjs/vite-plugin-commonjs": "^1.0.3",
63
63
  "@panter/vue-i18next": "^0.15.2",
64
- "@processmaker/vue-form-elements": "0.65.5",
64
+ "@processmaker/vue-form-elements": "0.65.6",
65
65
  "@processmaker/vue-multiselect": "2.3.0",
66
66
  "@storybook/addon-essentials": "^7.6.13",
67
67
  "@storybook/addon-interactions": "^7.6.13",
@@ -125,7 +125,7 @@
125
125
  },
126
126
  "peerDependencies": {
127
127
  "@panter/vue-i18next": "^0.15.0",
128
- "@processmaker/vue-form-elements": "0.65.5",
128
+ "@processmaker/vue-form-elements": "0.65.6",
129
129
  "i18next": "^15.0.8",
130
130
  "vue": "^2.6.12",
131
131
  "vuex": "^3.1.1"
@@ -43,15 +43,16 @@
43
43
 
44
44
  <!-- Select All and Search Section -->
45
45
  <div class="controls-section">
46
- <button
47
- type="button"
48
- class="select-all-button"
49
- @click="selectAll"
50
- :disabled="filteredVariables.length === 0 || selectedVariables.length === filteredVariables.length"
46
+ <b-form-checkbox
47
+ :checked="allSelected"
48
+ :indeterminate="someSelected"
49
+ @change="toggleSelectAll"
50
+ :disabled="filteredVariables.length === 0"
51
+ class="select-all-checkbox"
51
52
  data-cy="variables-to-submit-select-all"
52
53
  >
53
54
  {{ $t('Select All') }}
54
- </button>
55
+ </b-form-checkbox>
55
56
  <button
56
57
  type="button"
57
58
  class="search-button"
@@ -164,10 +165,13 @@ export default {
164
165
 
165
166
  // Extract calculated variables (computed properties)
166
167
  Object.assign(variables, this.extractCalculatedVariables());
168
+
169
+ // Extract watcher output variables
170
+ Object.assign(variables, this.extractWatcherVariables());
167
171
 
168
- // Filter: exclude _parent variables, include all others
172
+ // Filter: exclude _parent variables and invalid variable names
169
173
  return Object.keys(variables)
170
- .filter(variable => !variable.startsWith('_parent.'))
174
+ .filter(variable => this.isValidVariableName(variable))
171
175
  .sort();
172
176
  },
173
177
 
@@ -211,6 +215,36 @@ export default {
211
215
  return this.requiredVariables.filter(
212
216
  variable => !this.selectedVariables.includes(variable)
213
217
  );
218
+ },
219
+
220
+ /**
221
+ * Check if all filtered variables are selected
222
+ */
223
+ allSelected() {
224
+ return this.filteredVariables.length > 0 &&
225
+ this.filteredVariables.every(v => this.selectedVariables.includes(v));
226
+ },
227
+
228
+ /**
229
+ * Check if some (but not all) filtered variables are selected
230
+ */
231
+ someSelected() {
232
+ const selectedCount = this.filteredVariables.filter(v => this.selectedVariables.includes(v)).length;
233
+ return selectedCount > 0 && selectedCount < this.filteredVariables.length;
234
+ },
235
+
236
+ /**
237
+ * Source for computed properties to watch for changes
238
+ */
239
+ computedPropertiesSource() {
240
+ return this.getComputedProperties() || [];
241
+ },
242
+
243
+ /**
244
+ * Source for watchers to watch for changes
245
+ */
246
+ watchersSource() {
247
+ return this.getWatchers() || [];
214
248
  }
215
249
  },
216
250
  watch: {
@@ -231,8 +265,13 @@ export default {
231
265
  this.$emit('change', []);
232
266
  }
233
267
  },
234
- isEnabled(newValue) {
235
- if (!newValue) {
268
+ isEnabled(newValue, oldValue) {
269
+ if (newValue && !oldValue) {
270
+ // When enabled for the first time, select all variables
271
+ this.selectedVariables = [...this.availableVariables];
272
+ this.$emit('input', this.selectedVariables);
273
+ this.$emit('change', this.selectedVariables);
274
+ } else if (!newValue) {
236
275
  // When disabled, clear selection to submit all variables
237
276
  this.selectedVariables = [];
238
277
  this.$emit('input', []);
@@ -242,17 +281,49 @@ export default {
242
281
  'selectedControl.config.event'(newVal) {
243
282
  this.event = newVal;
244
283
  },
245
- 'builder.variablesTree'() {
246
- // Force recomputation when variables tree changes
247
- this.$forceUpdate();
284
+ formConfig: {
285
+ handler() {
286
+ this.$nextTick(() => {
287
+ this.cleanupInvalidSelections();
288
+ });
289
+ },
290
+ deep: true,
291
+ immediate: true
248
292
  },
249
- // Watch for computed properties changes in App.vue
250
- '$root.computed'() {
251
- // Force recomputation when computed properties change
252
- this.$forceUpdate();
293
+ // Watch for computed properties changes
294
+ computedPropertiesSource: {
295
+ handler() {
296
+ this.$nextTick(() => {
297
+ this.cleanupInvalidSelections();
298
+ });
299
+ },
300
+ deep: true
301
+ },
302
+ // Watch for watchers changes
303
+ watchersSource: {
304
+ handler() {
305
+ this.$nextTick(() => {
306
+ this.cleanupInvalidSelections();
307
+ });
308
+ },
309
+ deep: true
253
310
  }
254
311
  },
255
312
  methods: {
313
+ /**
314
+ * Remove selected variables that no longer exist in availableVariables
315
+ */
316
+ cleanupInvalidSelections() {
317
+ const available = this.availableVariables;
318
+ const validSelected = this.selectedVariables.filter(v => available.includes(v));
319
+ if (validSelected.length !== this.selectedVariables.length) {
320
+ this.selectedVariables = validSelected;
321
+ // Turn off toggle if no variables remain selected
322
+ if (validSelected.length === 0) {
323
+ this.isEnabled = false;
324
+ }
325
+ }
326
+ },
256
327
  /**
257
328
  * Load variables from the variables tree
258
329
  * Only includes root-level variables (no prefix, no dots in name)
@@ -289,6 +360,14 @@ export default {
289
360
  this.selectedVariables = this.selectedVariables.filter(v => !filteredSet.has(v));
290
361
  },
291
362
 
363
+ toggleSelectAll(checked) {
364
+ if (checked) {
365
+ this.selectAll();
366
+ } else {
367
+ this.deselectAll();
368
+ }
369
+ },
370
+
292
371
  toggleSearch() {
293
372
  this.showSearch = !this.showSearch;
294
373
  if (!this.showSearch) {
@@ -296,6 +375,23 @@ export default {
296
375
  }
297
376
  },
298
377
 
378
+ /**
379
+ * Check if a variable name is valid
380
+ * Uses same logic as dot_notation validation rule
381
+ */
382
+ isValidVariableName(name) {
383
+ if (!name || typeof name !== 'string') {
384
+ return false;
385
+ }
386
+ if (name.startsWith('_parent.')) {
387
+ return false;
388
+ }
389
+ // Same regex as dot_notation: starts with letter, followed by letters, numbers, or underscores
390
+ const validPartRegex = /^[a-zA-Z][a-zA-Z0-9_]*$/;
391
+ const parts = name.split('.');
392
+ return parts.every(part => validPartRegex.test(part));
393
+ },
394
+
299
395
  /**
300
396
  * Extract calculated variables (computed properties) from the screen
301
397
  * Searches in multiple locations: App.vue, builder, or parent components
@@ -324,6 +420,11 @@ export default {
324
420
  return this.$root.$data.computed;
325
421
  }
326
422
 
423
+ // Try $root.$children[0] (App.vue pattern)
424
+ if (this.$root?.$children?.[0]?.computed && Array.isArray(this.$root.$children[0].computed)) {
425
+ return this.$root.$children[0].computed;
426
+ }
427
+
327
428
  // Try builder sources
328
429
  if (this.builder?.screen?.computed) {
329
430
  return this.builder.screen.computed;
@@ -348,6 +449,47 @@ export default {
348
449
 
349
450
  return [];
350
451
  },
452
+
453
+ /**
454
+ * Extract watcher output variables from the screen
455
+ */
456
+ extractWatcherVariables() {
457
+ const watcherVars = {};
458
+ const watchers = this.getWatchers() || [];
459
+
460
+ watchers.forEach(watcher => {
461
+ if (watcher.byPass) return;
462
+
463
+ // Output variable (for scripts)
464
+ if (watcher.output_variable) {
465
+ watcherVars[watcher.output_variable] = null;
466
+ }
467
+
468
+ // Data mapping variables (for data sources)
469
+ try {
470
+ const config = typeof watcher.script_configuration === 'string'
471
+ ? JSON.parse(watcher.script_configuration)
472
+ : watcher.script_configuration;
473
+ (config?.dataMapping || []).forEach(m => {
474
+ if (m.key) watcherVars[m.key] = null;
475
+ });
476
+ } catch {
477
+ console.error('Invalid JSON in script_configuration for watcher:', watcher.name);
478
+ }
479
+ });
480
+
481
+ return watcherVars;
482
+ },
483
+
484
+ /**
485
+ * Get watchers from various sources
486
+ */
487
+ getWatchers() {
488
+ return this.$root?.$data?.watchers
489
+ || this.$root?.$children?.[0]?.watchers
490
+ || this.$root?.$children?.[0]?.$data?.watchers
491
+ || [];
492
+ },
351
493
 
352
494
  /**
353
495
  * Extract variables from form config
@@ -634,25 +776,16 @@ export default {
634
776
  margin-top: 0;
635
777
  }
636
778
 
637
- .select-all-button {
638
- background: none;
639
- border: none;
640
- padding: 0;
641
- color: #0d6efd;
642
- font-size: 15px;
779
+ .select-all-checkbox {
780
+ margin: 0;
781
+ font-size: 14px;
643
782
  font-weight: 600;
644
- cursor: pointer;
645
- text-decoration: none;
646
- }
647
-
648
- .select-all-button:hover:not(:disabled) {
649
- color: #0a58ca;
650
- text-decoration: none;
783
+ color: #495057;
651
784
  }
652
785
 
653
- .select-all-button:disabled {
654
- color: #adb5bd;
655
- cursor: not-allowed;
786
+ .select-all-checkbox >>> .custom-control-label {
787
+ cursor: pointer;
788
+ user-select: none;
656
789
  }
657
790
 
658
791
  .search-button {