@5minds/node-red-dashboard-2-processcube-dynamic-form 2.1.0-feature-48cebe-mdiwg5tc → 2.1.0-file-preview-6b0a6e-mdmztpfy

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.
@@ -1,60 +1,140 @@
1
1
  <template>
2
2
  <div className="ui-dynamic-form-external-sizing-wrapper" :style="props.card_size_styling">
3
3
  <!-- Component must be wrapped in a block so props such as className and style can be passed in from parent -->
4
- <UIDynamicFormTitleText v-if="props.title_style === 'outside' && hasUserTask" :style="props.title_style"
5
- :title="effectiveTitle" :customStyles="props.title_custom_text_styling" :titleIcon="props.title_icon"
6
- :collapsible="props.collapsible || (props.collapse_when_finished && formIsFinished)" :collapsed="collapsed"
7
- :toggleCollapse="toggleCollapse" />
4
+ <UIDynamicFormTitleText
5
+ v-if="props.title_style === 'outside' && hasUserTask"
6
+ :style="props.title_style"
7
+ :title="effectiveTitle"
8
+ :customStyles="props.title_custom_text_styling"
9
+ :titleIcon="props.title_icon"
10
+ :collapsible="props.collapsible || (props.collapse_when_finished && formIsFinished)"
11
+ :collapsed="collapsed"
12
+ :toggleCollapse="toggleCollapse"
13
+ />
8
14
  <div className="ui-dynamic-form-wrapper">
9
15
  <p v-if="hasUserTask" style="margin-bottom: 0px">
10
16
  <v-form ref="form" v-model="form" :class="dynamicClass">
11
- <UIDynamicFormTitleText v-if="props.title_style != 'outside'" :style="props.title_style"
12
- :title="effectiveTitle" :customStyles="props.title_custom_text_styling"
17
+ <UIDynamicFormTitleText
18
+ v-if="props.title_style != 'outside'"
19
+ :style="props.title_style"
20
+ :title="effectiveTitle"
21
+ :customStyles="props.title_custom_text_styling"
13
22
  :titleIcon="props.title_icon"
14
23
  :collapsible="props.collapsible || (props.collapse_when_finished && formIsFinished)"
15
- :collapsed="collapsed" :toggleCollapse="toggleCollapse" />
24
+ :collapsed="collapsed"
25
+ :toggleCollapse="toggleCollapse"
26
+ />
16
27
  <Transition name="cardCollapse">
17
28
  <div v-if="!collapsed">
18
- <div className="ui-dynamic-form-formfield-positioner" :style="props.inner_card_styling"
19
- :data-columns="props.form_columns || 1">
29
+ <div
30
+ className="ui-dynamic-form-formfield-positioner"
31
+ :style="props.inner_card_styling"
32
+ :data-columns="props.form_columns || 1"
33
+ >
20
34
  <FormKit id="form" type="group">
21
- <v-row v-for="(field, index) in fields()" :key="field"
35
+ <v-row
36
+ v-for="(field, index) in fields()"
37
+ :key="field"
22
38
  :class="field.type === 'header' ? 'ui-dynamic-form-header-row' : ''"
23
- :style="getRowWidthStyling(field, index)">
39
+ :style="getRowWidthStyling(field, index)"
40
+ >
24
41
  <v-col cols="12">
25
- <component :is="createComponent(field).type"
26
- v-if="createComponent(field).innerHTML"
27
- v-bind="createComponent(field).props"
28
- :class="createComponent(field).class"
29
- v-html="createComponent(field).innerHTML" :ref="(el) => {
30
- if (index === 0) firstFormFieldRef = el;
31
- }
32
- " />
33
- <component :is="createComponent(field).type"
34
- v-else-if="createComponent(field).innerText"
35
- v-bind="createComponent(field).props" :ref="(el) => {
36
- if (index === 0) firstFormFieldRef = el;
37
- }
38
- " v-model="formData[field.id]">
39
- {{ createComponent(field).innerText }}
42
+ <component
43
+ :is="getFieldComponent(field).type"
44
+ v-if="getFieldComponent(field).innerHTML"
45
+ v-bind="getFieldComponent(field).props"
46
+ :class="getFieldComponent(field).class"
47
+ v-html="getFieldComponent(field).innerHTML"
48
+ :ref="
49
+ (el) => {
50
+ if (index === 0) firstFormFieldRef = el;
51
+ }
52
+ "
53
+ />
54
+ <component
55
+ :is="getFieldComponent(field).type"
56
+ v-else-if="getFieldComponent(field).innerText"
57
+ v-bind="getFieldComponent(field).props"
58
+ :ref="
59
+ (el) => {
60
+ if (index === 0) firstFormFieldRef = el;
61
+ }
62
+ "
63
+ v-model="formData[field.id]"
64
+ >
65
+ {{ getFieldComponent(field).innerText }}
40
66
  </component>
41
- <div v-else-if="createComponent(field).type == 'v-slider'">
67
+ <div v-else-if="getFieldComponent(field).type == 'v-slider'">
42
68
  <p class="formkit-label">{{ field.label }}</p>
43
- <component :is="createComponent(field).type"
44
- v-bind="createComponent(field).props" :ref="(el) => {
69
+ <component
70
+ :is="getFieldComponent(field).type"
71
+ v-bind="getFieldComponent(field).props"
72
+ :ref="
73
+ (el) => {
74
+ if (index === 0) firstFormFieldRef = el;
75
+ }
76
+ "
77
+ v-model="field.defaultValue"
78
+ />
79
+ <p class="formkit-help">
80
+ {{
81
+ field.customForm ? JSON.parse(field.customForm).hint : undefined
82
+ }}
83
+ </p>
84
+ </div>
85
+ <component
86
+ :is="getFieldComponent(field).type"
87
+ v-else
88
+ v-bind="getFieldComponent(field).props"
89
+ :ref="
90
+ (el) => {
45
91
  if (index === 0) firstFormFieldRef = el;
46
92
  }
47
- " v-model="field.defaultValue" />
93
+ "
94
+ v-model="formData[field.id]"
95
+ />
96
+ <!-- <component
97
+ :is="getFieldComponent(field).type"
98
+ v-if="getFieldComponent(field).innerText"
99
+ v-bind="getFieldComponent(field).props"
100
+ :ref="
101
+ (el) => {
102
+ if (index === 0) firstFormFieldRef = el;
103
+ }
104
+ "
105
+ v-model="formData[field.id]"
106
+ >
107
+ {{ getFieldComponent(field).innerText }}
108
+ </component>
109
+ <div v-else-if="getFieldComponent(field).type == 'v-slider'">
110
+ <p class="formkit-label">{{ field.label }}</p>
111
+ <component
112
+ :is="getFieldComponent(field).type"
113
+ v-bind="getFieldComponent(field).props"
114
+ :ref="
115
+ (el) => {
116
+ if (index === 0) firstFormFieldRef = el;
117
+ }
118
+ "
119
+ v-model="field.defaultValue"
120
+ />
48
121
  <p class="formkit-help">
49
- {{ field.customForm ? JSON.parse(field.customForm).hint : undefined
122
+ {{
123
+ field.customForm ? JSON.parse(field.customForm).hint : undefined
50
124
  }}
51
125
  </p>
52
126
  </div>
53
- <component :is="createComponent(field).type" v-else
54
- v-bind="createComponent(field).props" :ref="(el) => {
55
- if (index === 0) firstFormFieldRef = el;
56
- }
57
- " v-model="formData[field.id]" />
127
+ <component
128
+ :is="getFieldComponent(field).type"
129
+ v-else
130
+ v-bind="getFieldComponent(field).props"
131
+ :ref="
132
+ (el) => {
133
+ if (index === 0) firstFormFieldRef = el;
134
+ }
135
+ "
136
+ v-model="formData[field.id]"
137
+ /> -->
58
138
  </v-col>
59
139
  </v-row>
60
140
  </FormKit>
@@ -63,17 +143,24 @@
63
143
  <v-row v-if="errorMsg.length > 0" style="padding: 12px">
64
144
  <v-alert type="error">Error: {{ errorMsg }}</v-alert>
65
145
  </v-row>
66
- <UIDynamicFormFooterAction v-if="props.actions_inside_card && actions.length > 0"
67
- :actions="actions" :actionCallback="actionFn" :formIsFinished="formIsFinished"
68
- style="padding: 16px; padding-top: 0px" />
146
+ <UIDynamicFormFooterAction
147
+ v-if="props.actions_inside_card && actions.length > 0"
148
+ :actions="actions"
149
+ :actionCallback="actionFn"
150
+ :formIsFinished="formIsFinished"
151
+ style="padding: 16px; padding-top: 0px"
152
+ />
69
153
  </v-row>
70
154
  </div>
71
155
  </Transition>
72
156
  </v-form>
73
157
  </p>
74
158
  <p v-else>
75
- <v-alert v-if="props.waiting_info.length > 0 || props.waiting_title.length > 0"
76
- :text="props.waiting_info" :title="props.waiting_title" />
159
+ <v-alert
160
+ v-if="props.waiting_info.length > 0 || props.waiting_title.length > 0"
161
+ :text="props.waiting_info"
162
+ :title="props.waiting_title"
163
+ />
77
164
  </p>
78
165
  </div>
79
166
  <div v-if="!props.actions_inside_card && actions.length > 0 && hasUserTask" style="padding-top: 32px">
@@ -182,6 +269,7 @@ export default {
182
269
  msg: null,
183
270
  collapsed: false,
184
271
  firstFormFieldRef: null,
272
+ componentCache: new Map(), // Add caching for components
185
273
  };
186
274
  },
187
275
  computed: {
@@ -282,6 +370,26 @@ export default {
282
370
  this.$socket?.off('msg-input:' + this.id);
283
371
  },
284
372
  methods: {
373
+ // Performance optimized component caching
374
+ getFieldComponent(field) {
375
+ const cacheKey = `${field.id}_${JSON.stringify(this.formData[field.id])}_${this.formIsFinished}_${
376
+ this.theme
377
+ }`;
378
+
379
+ if (this.componentCache.has(cacheKey)) {
380
+ return this.componentCache.get(cacheKey);
381
+ }
382
+
383
+ const component = this.createComponent(field);
384
+ this.componentCache.set(cacheKey, component);
385
+ return component;
386
+ },
387
+
388
+ // Clear cache when form data changes
389
+ clearComponentCache() {
390
+ this.componentCache.clear();
391
+ },
392
+
285
393
  createComponent(field) {
286
394
  console.debug('Creating component for field:', field);
287
395
  const customForm = field.customForm ? JSON.parse(field.customForm) : {};
@@ -292,8 +400,10 @@ export default {
292
400
  const customProperties = customForm.customProperties ?? [];
293
401
  const isReadOnly =
294
402
  this.props.readonly ||
295
- this.formIsFinished ||
296
- customProperties.find((entry) => ['readOnly', 'readonly'].includes(entry.name) && entry.value === 'true')
403
+ this.formIsFinished ||
404
+ customProperties.find(
405
+ (entry) => ['readOnly', 'readonly'].includes(entry.name) && entry.value === 'true'
406
+ )
297
407
  ? 'true'
298
408
  : undefined;
299
409
  switch (field.type) {
@@ -314,7 +424,9 @@ export default {
314
424
  wrapperClass: '$remove:formkit-wrapper',
315
425
  labelClass: 'ui-dynamic-form-input-label',
316
426
  inputClass: `input-${this.theme}`,
317
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
427
+ innerClass: `ui-dynamic-form-input-outlines ${
428
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
429
+ }`,
318
430
  readonly: isReadOnly,
319
431
  validationVisibility: 'live',
320
432
  },
@@ -337,7 +449,9 @@ export default {
337
449
  wrapperClass: '$remove:formkit-wrapper',
338
450
  labelClass: 'ui-dynamic-form-input-label',
339
451
  inputClass: `input-${this.theme}`,
340
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
452
+ innerClass: `ui-dynamic-form-input-outlines ${
453
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
454
+ }`,
341
455
  readonly: isReadOnly,
342
456
  validationVisibility: 'live',
343
457
  },
@@ -356,7 +470,9 @@ export default {
356
470
  wrapperClass: '$remove:formkit-wrapper',
357
471
  labelClass: 'ui-dynamic-form-input-label',
358
472
  inputClass: `input-${this.theme}`,
359
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
473
+ innerClass: `ui-dynamic-form-input-outlines ${
474
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
475
+ }`,
360
476
  readonly: isReadOnly,
361
477
  validation,
362
478
  validationVisibility: 'live',
@@ -380,7 +496,9 @@ export default {
380
496
  wrapperClass: '$remove:formkit-wrapper',
381
497
  labelClass: 'ui-dynamic-form-input-label',
382
498
  inputClass: `input-${this.theme}`,
383
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
499
+ innerClass: `ui-dynamic-form-input-outlines ${
500
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
501
+ }`,
384
502
  readonly: isReadOnly,
385
503
  disabled: isReadOnly,
386
504
  validation,
@@ -406,7 +524,9 @@ export default {
406
524
  wrapperClass: '$remove:formkit-wrapper',
407
525
  labelClass: 'ui-dynamic-form-input-label',
408
526
  inputClass: `input-${this.theme}`,
409
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
527
+ innerClass: `ui-dynamic-form-input-outlines ${
528
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
529
+ }`,
410
530
  readonly: isReadOnly,
411
531
  disabled: isReadOnly,
412
532
  validation,
@@ -428,7 +548,9 @@ export default {
428
548
  wrapperClass: '$remove:formkit-wrapper',
429
549
  labelClass: 'ui-dynamic-form-input-label',
430
550
  inputClass: `input-${this.theme}`,
431
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
551
+ innerClass: `ui-dynamic-form-input-outlines ${
552
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
553
+ }`,
432
554
  readonly: isReadOnly,
433
555
  validation,
434
556
  validationVisibility: 'live',
@@ -452,13 +574,131 @@ export default {
452
574
  help: hint,
453
575
  labelClass: 'ui-dynamic-form-input-label',
454
576
  inputClass: `input-${this.theme}`,
455
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
577
+ innerClass: `ui-dynamic-form-input-outlines ${
578
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
579
+ }`,
456
580
  readonly: isReadOnly,
457
581
  disabled: isReadOnly,
458
582
  validation,
459
583
  validationVisibility: 'live',
460
584
  },
461
585
  };
586
+ case 'file-preview':
587
+ // Handle file preview display only (no upload functionality)
588
+ const originalFieldId = field.id.replace('_preview', '');
589
+ if (this.formData && this.formData[originalFieldId] && this.formData[originalFieldId].length != 0) {
590
+ const fileDataArray = Array.isArray(this.formData[originalFieldId])
591
+ ? this.formData[originalFieldId]
592
+ : [this.formData[originalFieldId]];
593
+
594
+ // Separate images from other files
595
+ const images = [];
596
+ const otherFiles = [];
597
+
598
+ fileDataArray.forEach((fileData) => {
599
+ const fileName = fileData.name || '';
600
+ const isImage = fileName.toLowerCase().match(/\.(png|jpg|jpeg|gif|webp)$/);
601
+
602
+ if (isImage && fileData.file && fileData.file.data) {
603
+ // Convert buffer to base64 data URL for image display - safe for large files
604
+ const uint8Array = new Uint8Array(fileData.file.data);
605
+ let binaryString = '';
606
+
607
+ // Process in chunks to avoid call stack overflow
608
+ const chunkSize = 1024;
609
+ for (let i = 0; i < uint8Array.length; i += chunkSize) {
610
+ const chunk = uint8Array.slice(i, i + chunkSize);
611
+ binaryString += String.fromCharCode.apply(null, chunk);
612
+ }
613
+
614
+ const base64String = btoa(binaryString);
615
+ const mimeType = fileName.toLowerCase().endsWith('.png')
616
+ ? 'image/png'
617
+ : fileName.toLowerCase().endsWith('.gif')
618
+ ? 'image/gif'
619
+ : 'image/jpeg';
620
+ const dataURL = `data:${mimeType};base64,${base64String}`;
621
+
622
+ images.push({ fileName, dataURL, fileData });
623
+ } else {
624
+ otherFiles.push({ fileName, fileData });
625
+ }
626
+ });
627
+
628
+ let content = `<label class="ui-dynamic-form-input-label">${field.label} (Vorschau)${
629
+ field.required ? ' *' : ''
630
+ }</label>`;
631
+
632
+ // Display images
633
+ if (images.length > 0) {
634
+ content += '<div style="margin-top: 8px;">';
635
+ content += '<div style="font-weight: bold; margin-bottom: 8px;">Bilder:</div>';
636
+ images.forEach((img, index) => {
637
+ const downloadId = `download-img-${field.id}-${index}`;
638
+ content += `
639
+ <div style="display: inline-block; margin: 8px; text-align: center; vertical-align: top;">
640
+ <img src="${img.dataURL}" alt="${img.fileName}"
641
+ style="max-width: 300px; max-height: 200px; border: 1px solid #ccc; display: block; cursor: pointer;"
642
+ onclick="document.getElementById('${downloadId}').click();" />
643
+ <div style="margin-top: 4px; font-size: 0.9em; color: #666; max-width: 300px; word-break: break-word;">
644
+ ${img.fileName}
645
+ </div>
646
+ <a id="${downloadId}" href="${img.dataURL}" download="${img.fileName}" style="display: none;"></a>
647
+ </div>
648
+ `;
649
+ });
650
+ content += '</div>';
651
+ }
652
+
653
+ // Display other files as list
654
+ if (otherFiles.length > 0) {
655
+ content +=
656
+ '<div style="margin-top: 12px; padding: 12px; border: 1px solid #ddd; border-radius: 4px; background: #f9f9f9;">';
657
+ content += '<div style="font-weight: bold; margin-bottom: 8px;">Weitere Dateien:</div>';
658
+ otherFiles.forEach((file, index) => {
659
+ const downloadId = `download-file-${field.id}-${index}`;
660
+ const uint8Array = new Uint8Array(file.fileData.file.data);
661
+ let binaryString = '';
662
+
663
+ // Process in chunks for download
664
+ const chunkSize = 1024;
665
+ for (let i = 0; i < uint8Array.length; i += chunkSize) {
666
+ const chunk = uint8Array.slice(i, i + chunkSize);
667
+ binaryString += String.fromCharCode.apply(null, chunk);
668
+ }
669
+
670
+ const base64String = btoa(binaryString);
671
+ const dataURL = `data:application/octet-stream;base64,${base64String}`;
672
+
673
+ content += `
674
+ <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px; padding: 4px; border-radius: 3px; cursor: pointer;"
675
+ onclick="document.getElementById('${downloadId}').click();"
676
+ onmouseover="this.style.backgroundColor='#e6e6e6';"
677
+ onmouseout="this.style.backgroundColor='transparent';">
678
+ <span style="font-size: 1.2em;">📎</span>
679
+ <span style="flex: 1; word-break: break-word;">${file.fileName}</span>
680
+ <span style="font-size: 0.8em; color: #007bff;">Download</span>
681
+ <a id="${downloadId}" href="${dataURL}" download="${file.fileName}" style="display: none;"></a>
682
+ </div>
683
+ `;
684
+ });
685
+ content += '</div>';
686
+ }
687
+
688
+ return {
689
+ type: 'div',
690
+ props: {
691
+ innerHTML: content,
692
+ },
693
+ };
694
+ }
695
+ // If no files to preview, return empty div
696
+ return {
697
+ type: 'div',
698
+ props: {
699
+ style: 'display: none;',
700
+ },
701
+ };
462
702
  case 'file':
463
703
  const multiple = field.customForm ? JSON.parse(field.customForm).multiple === 'true' : false;
464
704
  return {
@@ -469,13 +709,11 @@ export default {
469
709
  name,
470
710
  label: field.label,
471
711
  required: field.required,
472
- value: this.formData[field.id],
473
712
  help: hint,
474
713
  innerClass: 'reset-background',
475
714
  wrapperClass: '$remove:formkit-wrapper',
476
715
  labelClass: 'ui-dynamic-form-input-label',
477
716
  inputClass: `input-${this.theme}`,
478
- // innerClass: ui-dynamic-form-input-outlines `${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
479
717
  readonly: isReadOnly,
480
718
  disabled: isReadOnly,
481
719
  multiple,
@@ -501,7 +739,9 @@ export default {
501
739
  fieldsetClass: 'custom-fieldset',
502
740
  labelClass: 'ui-dynamic-form-input-label',
503
741
  inputClass: `input-${this.theme}`,
504
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
742
+ innerClass: `ui-dynamic-form-input-outlines ${
743
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
744
+ }`,
505
745
  readonly: isReadOnly,
506
746
  disabled: isReadOnly,
507
747
  validation,
@@ -539,7 +779,9 @@ export default {
539
779
  wrapperClass: '$remove:formkit-wrapper',
540
780
  labelClass: 'ui-dynamic-form-input-label',
541
781
  inputClass: `input-${this.theme}`,
542
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
782
+ innerClass: `ui-dynamic-form-input-outlines ${
783
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
784
+ }`,
543
785
  readonly: isReadOnly,
544
786
  validation,
545
787
  validationVisibility: 'live',
@@ -560,7 +802,9 @@ export default {
560
802
  wrapperClass: '$remove:formkit-wrapper',
561
803
  labelClass: 'ui-dynamic-form-input-label',
562
804
  inputClass: `input-${this.theme}`,
563
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
805
+ innerClass: `ui-dynamic-form-input-outlines ${
806
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
807
+ }`,
564
808
  readonly: isReadOnly,
565
809
  validation,
566
810
  validationVisibility: 'live',
@@ -600,7 +844,9 @@ export default {
600
844
  wrapperClass: '$remove:formkit-wrapper',
601
845
  labelClass: 'ui-dynamic-form-input-label',
602
846
  inputClass: `input-${this.theme}`,
603
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
847
+ innerClass: `ui-dynamic-form-input-outlines ${
848
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
849
+ }`,
604
850
  readonly: isReadOnly,
605
851
  validation,
606
852
  validationVisibility: 'live',
@@ -629,7 +875,9 @@ export default {
629
875
  wrapperClass: '$remove:formkit-wrapper',
630
876
  labelClass: 'ui-dynamic-form-input-label',
631
877
  inputClass: `input-${this.theme}`,
632
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
878
+ innerClass: `ui-dynamic-form-input-outlines ${
879
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
880
+ }`,
633
881
  readonly: isReadOnly,
634
882
  validation,
635
883
  validationVisibility: 'live',
@@ -653,7 +901,9 @@ export default {
653
901
  fieldsetClass: 'custom-fieldset',
654
902
  labelClass: 'ui-dynamic-form-input-label',
655
903
  inputClass: `input-${this.theme}`,
656
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
904
+ innerClass: `ui-dynamic-form-input-outlines ${
905
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
906
+ }`,
657
907
  readonly: isReadOnly,
658
908
  disabled: isReadOnly,
659
909
  validation,
@@ -700,7 +950,9 @@ export default {
700
950
  wrapperClass: '$remove:formkit-wrapper',
701
951
  labelClass: 'ui-dynamic-form-input-label',
702
952
  inputClass: `input-${this.theme}`,
703
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
953
+ innerClass: `ui-dynamic-form-input-outlines ${
954
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
955
+ }`,
704
956
  readonly: isReadOnly,
705
957
  validation,
706
958
  validationVisibility: 'live',
@@ -723,7 +975,9 @@ export default {
723
975
  wrapperClass: '$remove:formkit-wrapper',
724
976
  labelClass: 'ui-dynamic-form-input-label',
725
977
  inputClass: `input-${this.theme}`,
726
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
978
+ innerClass: `ui-dynamic-form-input-outlines ${
979
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
980
+ }`,
727
981
  readonly: isReadOnly,
728
982
  validation,
729
983
  validationVisibility: 'live',
@@ -744,7 +998,9 @@ export default {
744
998
  wrapperClass: '$remove:formkit-wrapper',
745
999
  labelClass: 'ui-dynamic-form-input-label',
746
1000
  inputClass: `input-${this.theme}`,
747
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
1001
+ innerClass: `ui-dynamic-form-input-outlines ${
1002
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
1003
+ }`,
748
1004
  readonly: isReadOnly,
749
1005
  validation,
750
1006
  validationVisibility: 'live',
@@ -765,7 +1021,9 @@ export default {
765
1021
  wrapperClass: '$remove:formkit-wrapper',
766
1022
  labelClass: 'ui-dynamic-form-input-label',
767
1023
  inputClass: `input-${this.theme}`,
768
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
1024
+ innerClass: `ui-dynamic-form-input-outlines ${
1025
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
1026
+ }`,
769
1027
  readonly: isReadOnly,
770
1028
  validation,
771
1029
  validationVisibility: 'live',
@@ -786,7 +1044,9 @@ export default {
786
1044
  wrapperClass: '$remove:formkit-wrapper',
787
1045
  labelClass: 'ui-dynamic-form-input-label',
788
1046
  inputClass: `input-${this.theme}`,
789
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
1047
+ innerClass: `ui-dynamic-form-input-outlines ${
1048
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
1049
+ }`,
790
1050
  readonly: isReadOnly,
791
1051
  validation,
792
1052
  validationVisibility: 'live',
@@ -805,7 +1065,9 @@ export default {
805
1065
  help: hint,
806
1066
  labelClass: 'ui-dynamic-form-input-label',
807
1067
  inputClass: `input-${this.theme}`,
808
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
1068
+ innerClass: `ui-dynamic-form-input-outlines ${
1069
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
1070
+ }`,
809
1071
  readonly: isReadOnly,
810
1072
  validation,
811
1073
  validationVisibility: 'live',
@@ -848,6 +1110,9 @@ export default {
848
1110
  },
849
1111
  init(msg) {
850
1112
  this.msg = msg;
1113
+ // Clear component cache when form data changes for performance
1114
+ this.clearComponentCache();
1115
+
851
1116
  if (!msg) {
852
1117
  return;
853
1118
  }
@@ -897,14 +1162,28 @@ export default {
897
1162
  ];
898
1163
  }
899
1164
  });
1165
+
1166
+ // Check for file fields and duplicate them as file-preview if initial values exist
1167
+ // Insert preview fields directly before their corresponding file fields
1168
+ for (let i = formFields.length - 1; i >= 0; i--) {
1169
+ const field = formFields[i];
1170
+ if (field.type === 'file' && initialValues && initialValues[field.id]) {
1171
+ const previewField = { ...field };
1172
+ previewField.type = 'file-preview';
1173
+ previewField.id = `${field.id}_preview`; // Give it a unique ID
1174
+ this.userTask.userTaskConfig.formFields.splice(i, 0, previewField);
1175
+ }
1176
+ }
900
1177
  }
901
1178
 
902
1179
  if (initialValues) {
903
- Object.keys(initialValues)
904
- .filter((key) => formFieldIds.includes(key))
905
- .forEach((key) => {
906
- this.formData[key] = initialValues[key];
907
- });
1180
+ if (initialValues) {
1181
+ Object.keys(initialValues)
1182
+ .filter((key) => formFieldIds.includes(key))
1183
+ .forEach((key) => {
1184
+ this.formData[key] = initialValues[key];
1185
+ });
1186
+ }
908
1187
  }
909
1188
 
910
1189
  if (this.formIsFinished) {
@@ -967,7 +1246,7 @@ export default {
967
1246
  this.send(
968
1247
  msg,
969
1248
  this.actions.findIndex((element) => element.label === action.label) +
970
- (this.isConfirmDialog ? this.props.options.length : 0)
1249
+ (this.isConfirmDialog ? this.props.options.length : 0)
971
1250
  );
972
1251
  // TODO: mm - end
973
1252
  } else {
@@ -1003,7 +1282,9 @@ export default {
1003
1282
  if (['INPUT', 'TEXTAREA', 'SELECT'].includes(this.firstFormFieldRef.$el.tagName)) {
1004
1283
  inputElement = this.firstFormFieldRef.$el;
1005
1284
  } else {
1006
- inputElement = this.firstFormFieldRef.$el.querySelector('input:not([type="hidden"]), textarea, select');
1285
+ inputElement = this.firstFormFieldRef.$el.querySelector(
1286
+ 'input:not([type="hidden"]), textarea, select'
1287
+ );
1007
1288
  }
1008
1289
  }
1009
1290