@5minds/node-red-dashboard-2-processcube-dynamic-form 2.0.8-develop-47efa6-mdho9d9t → 2.0.8-file-preview-eccc05-mdiiszar

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,52 +1,81 @@
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 className="ui-dynamic-form-formfield-positioner" :style="props.inner_card_styling">
20
30
  <FormKit id="form" type="group">
21
- <v-row v-for="(field, index) in fields()" :key="field"
22
- :class="field.type === 'header' ? 'ui-dynamic-form-header-row' : ''"
23
- :style="getRowWidthStyling(field, index)">
31
+ <v-row
32
+ v-for="(field, index) in fields()"
33
+ :key="`${field.id}_${index}`"
34
+ :style="getRowWidthStyling(field, index)"
35
+ >
24
36
  <v-col cols="12">
25
- <component :is="createComponent(field).type"
26
- v-if="createComponent(field).innerText"
27
- v-bind="createComponent(field).props" :ref="(el) => {
28
- if (index === 0) firstFormFieldRef = el;
29
- }
30
- " v-model="formData[field.id]">
31
- {{ createComponent(field).innerText }}
32
- </component>
33
- <div v-else-if="createComponent(field).type == 'v-slider'">
34
- <p class="formkit-label">{{ field.label }}</p>
35
- <component :is="createComponent(field).type"
36
- v-bind="createComponent(field).props" :ref="(el) => {
37
+ <component
38
+ :is="getFieldComponent(field).type"
39
+ v-if="getFieldComponent(field).innerText"
40
+ v-bind="getFieldComponent(field).props"
41
+ :ref="
42
+ (el) => {
37
43
  if (index === 0) firstFormFieldRef = el;
38
44
  }
39
- " v-model="field.defaultValue" />
45
+ "
46
+ v-model="formData[field.id]"
47
+ >
48
+ {{ getFieldComponent(field).innerText }}
49
+ </component>
50
+ <div v-else-if="getFieldComponent(field).type == 'v-slider'">
51
+ <p class="formkit-label">{{ field.label }}</p>
52
+ <component
53
+ :is="getFieldComponent(field).type"
54
+ v-bind="getFieldComponent(field).props"
55
+ :ref="
56
+ (el) => {
57
+ if (index === 0) firstFormFieldRef = el;
58
+ }
59
+ "
60
+ v-model="field.defaultValue"
61
+ />
40
62
  <p class="formkit-help">
41
- {{ field.customForm ? JSON.parse(field.customForm).hint : undefined
63
+ {{
64
+ field.customForm ? JSON.parse(field.customForm).hint : undefined
42
65
  }}
43
66
  </p>
44
67
  </div>
45
- <component :is="createComponent(field).type" v-else
46
- v-bind="createComponent(field).props" :ref="(el) => {
47
- if (index === 0) firstFormFieldRef = el;
48
- }
49
- " v-model="formData[field.id]" />
68
+ <component
69
+ :is="getFieldComponent(field).type"
70
+ v-else
71
+ v-bind="getFieldComponent(field).props"
72
+ :ref="
73
+ (el) => {
74
+ if (index === 0) firstFormFieldRef = el;
75
+ }
76
+ "
77
+ v-model="formData[field.id]"
78
+ />
50
79
  </v-col>
51
80
  </v-row>
52
81
  </FormKit>
@@ -55,17 +84,24 @@
55
84
  <v-row v-if="errorMsg.length > 0" style="padding: 12px">
56
85
  <v-alert type="error">Error: {{ errorMsg }}</v-alert>
57
86
  </v-row>
58
- <UIDynamicFormFooterAction v-if="props.actions_inside_card && actions.length > 0"
59
- :actions="actions" :actionCallback="actionFn" :formIsFinished="formIsFinished"
60
- style="padding: 16px; padding-top: 0px" />
87
+ <UIDynamicFormFooterAction
88
+ v-if="props.actions_inside_card && actions.length > 0"
89
+ :actions="actions"
90
+ :actionCallback="actionFn"
91
+ :formIsFinished="formIsFinished"
92
+ style="padding: 16px; padding-top: 0px"
93
+ />
61
94
  </v-row>
62
95
  </div>
63
96
  </Transition>
64
97
  </v-form>
65
98
  </p>
66
99
  <p v-else>
67
- <v-alert v-if="props.waiting_info.length > 0 || props.waiting_title.length > 0"
68
- :text="props.waiting_info" :title="props.waiting_title" />
100
+ <v-alert
101
+ v-if="props.waiting_info.length > 0 || props.waiting_title.length > 0"
102
+ :text="props.waiting_info"
103
+ :title="props.waiting_title"
104
+ />
69
105
  </p>
70
106
  </div>
71
107
  <div v-if="!props.actions_inside_card && actions.length > 0 && hasUserTask" style="padding-top: 32px">
@@ -76,27 +112,27 @@
76
112
 
77
113
  <!-- eslint-disable no-case-declarations -->
78
114
  <script>
79
- import { de } from '@formkit/i18n'
80
- import { FormKit, defaultConfig, plugin } from '@formkit/vue'
81
- import { getCurrentInstance, markRaw, nextTick } from 'vue'
115
+ import { de } from '@formkit/i18n';
116
+ import { FormKit, defaultConfig, plugin } from '@formkit/vue';
117
+ import { getCurrentInstance, markRaw, nextTick } from 'vue';
82
118
 
83
119
  // eslint-disable-next-line import/no-unresolved
84
- import '@formkit/themes/genesis'
85
- import UIDynamicFormFooterAction from './FooterActions.vue'
86
- import UIDynamicFormTitleText from './TitleText.vue'
120
+ import '@formkit/themes/genesis';
121
+ import UIDynamicFormFooterAction from './FooterActions.vue';
122
+ import UIDynamicFormTitleText from './TitleText.vue';
87
123
 
88
124
  // eslint-disable-next-line no-unused-vars
89
125
  function requiredIf({ value }, [targetField, expectedValue], node) {
90
- console.debug(arguments)
126
+ console.debug(arguments);
91
127
 
92
- const actual = node?.root?.value?.[targetField]
93
- const isEmpty = value === '' || value === null || value === undefined
128
+ const actual = node?.root?.value?.[targetField];
129
+ const isEmpty = value === '' || value === null || value === undefined;
94
130
 
95
131
  if (actual === expectedValue && isEmpty) {
96
- return false // oder: `return "Dieses Feld ist erforderlich."`
132
+ return false; // oder: `return "Dieses Feld ist erforderlich."`
97
133
  }
98
134
 
99
- return true
135
+ return true;
100
136
  }
101
137
 
102
138
  export default {
@@ -104,7 +140,7 @@ export default {
104
140
  components: {
105
141
  FormKit,
106
142
  UIDynamicFormFooterAction,
107
- UIDynamicFormTitleText
143
+ UIDynamicFormTitleText,
108
144
  },
109
145
  inject: ['$socket'],
110
146
  props: {
@@ -113,24 +149,24 @@ export default {
113
149
  props: { type: Object, default: () => ({}) },
114
150
  state: {
115
151
  type: Object,
116
- default: () => ({ enabled: false, visible: false })
117
- }
152
+ default: () => ({ enabled: false, visible: false }),
153
+ },
118
154
  },
119
155
  setup(props) {
120
- console.info('UIDynamicForm setup with:', props)
121
- console.debug('Vue function loaded correctly', markRaw)
156
+ console.info('UIDynamicForm setup with:', props);
157
+ console.debug('Vue function loaded correctly', markRaw);
122
158
 
123
- const instance = getCurrentInstance()
124
- const app = instance.appContext.app
159
+ const instance = getCurrentInstance();
160
+ const app = instance.appContext.app;
125
161
 
126
162
  const formkitConfig = defaultConfig({
127
163
  theme: 'genesis',
128
164
  locales: { de },
129
165
  locale: 'de',
130
166
  // eslint-disable-next-line object-shorthand
131
- rules: { requiredIf: requiredIf }
132
- })
133
- app.use(plugin, formkitConfig)
167
+ rules: { requiredIf: requiredIf },
168
+ });
169
+ app.use(plugin, formkitConfig);
134
170
  },
135
171
  data() {
136
172
  return {
@@ -142,120 +178,143 @@ export default {
142
178
  formIsFinished: false,
143
179
  msg: null,
144
180
  collapsed: false,
145
- firstFormFieldRef: null
146
- }
181
+ firstFormFieldRef: null,
182
+ componentCache: new Map(), // Add caching for components
183
+ };
147
184
  },
148
185
  computed: {
149
186
  dynamicClass() {
150
- return `ui-dynamic-form-${this.theme} ui-dynamic-form-common`
187
+ return `ui-dynamic-form-${this.theme} ui-dynamic-form-common`;
151
188
  },
152
189
  dynamicFooterClass() {
153
- return `ui-dynamic-form-footer-${this.theme} ui-dynamic-form-footer-common`
190
+ return `ui-dynamic-form-footer-${this.theme} ui-dynamic-form-footer-common`;
154
191
  },
155
192
  hasUserTask() {
156
- return !!this.userTask
193
+ return !!this.userTask;
157
194
  },
158
195
  totalOutputs() {
159
196
  return (
160
197
  this.props.options.length +
161
198
  (this.props.handle_confirmation_dialogs ? 2 : 0) +
162
199
  (this.props.trigger_on_change ? 1 : 0)
163
- )
200
+ );
164
201
  },
165
202
  isConfirmDialog() {
166
- return this.userTask.userTaskConfig.formFields.some((field) => field.type === 'confirm')
203
+ return this.userTask.userTaskConfig.formFields.some((field) => field.type === 'confirm');
167
204
  },
168
205
  effectiveTitle() {
169
206
  if (this.props.title_text_type === 'str') {
170
- return this.props.title_text
207
+ return this.props.title_text;
171
208
  } else if (this.props.title_text_type === 'msg') {
172
- return this.msg.dynamicTitle
209
+ return this.msg.dynamicTitle;
173
210
  } else {
174
- return ''
211
+ return '';
175
212
  }
176
- }
213
+ },
177
214
  },
178
215
  watch: {
179
216
  formData: {
180
217
  handler(newData, oldData) {
181
218
  if (this.props.trigger_on_change) {
182
- const res = { payload: { formData: newData, userTask: this.userTask } }
183
- this.send(res, this.totalOutputs - 1)
219
+ const res = { payload: { formData: newData, userTask: this.userTask } };
220
+ this.send(res, this.totalOutputs - 1);
184
221
  }
185
222
  },
186
223
  collapsed(newVal) {
187
224
  if (!newVal && this.hasUserTask) {
188
225
  nextTick(() => {
189
- this.focusFirstFormField()
190
- })
226
+ this.focusFirstFormField();
227
+ });
191
228
  }
192
229
  },
193
230
  userTask(newVal) {
194
231
  if (newVal && !this.collapsed) {
195
232
  nextTick(() => {
196
- this.focusFirstFormField()
197
- })
233
+ this.focusFirstFormField();
234
+ });
198
235
  }
199
236
  },
200
- deep: true
201
- }
237
+ deep: true,
238
+ },
202
239
  },
203
240
  created() {
204
- const currentPath = window.location.pathname
205
- const lastPart = currentPath.substring(currentPath.lastIndexOf('/'))
241
+ const currentPath = window.location.pathname;
242
+ const lastPart = currentPath.substring(currentPath.lastIndexOf('/'));
206
243
 
207
- const store = this.$store.state
244
+ const store = this.$store.state;
208
245
 
209
246
  for (const key in store.ui.pages) {
210
247
  if (store.ui.pages[key].path === lastPart) {
211
- const theme = store.ui.pages[key].theme
248
+ const theme = store.ui.pages[key].theme;
212
249
  if (store.ui.themes[theme].name === 'ProcessCube Lightmode') {
213
- this.theme = 'light'
250
+ this.theme = 'light';
214
251
  } else if (store.ui.themes[theme].name === 'ProcessCube Darkmode') {
215
- this.theme = 'dark'
252
+ this.theme = 'dark';
216
253
  } else {
217
- this.theme = 'default'
254
+ this.theme = 'default';
218
255
  }
219
- break
256
+ break;
220
257
  }
221
258
  }
222
259
  },
223
260
  mounted() {
224
- const elements = document.querySelectorAll('.formkit-input')
261
+ const elements = document.querySelectorAll('.formkit-input');
225
262
 
226
263
  elements.forEach((element) => {
227
- element.classList.add('test')
228
- })
264
+ element.classList.add('test');
265
+ });
229
266
 
230
267
  this.$socket.on('widget-load:' + this.id, (msg) => {
231
- this.init(msg)
232
- })
268
+ this.init(msg);
269
+ });
233
270
  this.$socket.on('msg-input:' + this.id, (msg) => {
234
271
  // store the latest message in our client-side vuex store when we receive a new message
235
- this.init(msg)
236
- })
272
+ this.init(msg);
273
+ });
237
274
  // tell Node-RED that we're loading a new instance of this widget
238
- this.$socket.emit('widget-load', this.id)
275
+ this.$socket.emit('widget-load', this.id);
239
276
  },
240
277
  unmounted() {
241
278
  /* Make sure, any events you subscribe to on SocketIO are unsubscribed to here */
242
- this.$socket?.off('widget-load' + this.id)
243
- this.$socket?.off('msg-input:' + this.id)
279
+ this.$socket?.off('widget-load' + this.id);
280
+ this.$socket?.off('msg-input:' + this.id);
244
281
  },
245
282
  methods: {
283
+ // Performance optimized component caching
284
+ getFieldComponent(field) {
285
+ const cacheKey = `${field.id}_${JSON.stringify(this.formData[field.id])}_${this.formIsFinished}_${
286
+ this.theme
287
+ }`;
288
+
289
+ if (this.componentCache.has(cacheKey)) {
290
+ return this.componentCache.get(cacheKey);
291
+ }
292
+
293
+ const component = this.createComponent(field);
294
+ this.componentCache.set(cacheKey, component);
295
+ return component;
296
+ },
297
+
298
+ // Clear cache when form data changes
299
+ clearComponentCache() {
300
+ this.componentCache.clear();
301
+ },
302
+
246
303
  createComponent(field) {
247
- const customForm = field.customForm ? JSON.parse(field.customForm) : {}
248
- const hint = customForm.hint
249
- const placeholder = customForm.placeholder
250
- const validation = customForm.validation
251
- const name = field.id
252
- const customProperties = customForm.customProperties ?? []
304
+ const customForm = field.customForm ? JSON.parse(field.customForm) : {};
305
+ const hint = customForm.hint;
306
+ const placeholder = customForm.placeholder;
307
+ const validation = customForm.validation;
308
+ const name = field.id;
309
+ const customProperties = customForm.customProperties ?? [];
253
310
  const isReadOnly =
254
311
  this.props.readonly ||
255
- this.formIsFinished ||
256
- customProperties.find((entry) => ['readOnly', 'readonly'].includes(entry.name) && entry.value === 'true')
312
+ this.formIsFinished ||
313
+ customProperties.find(
314
+ (entry) => ['readOnly', 'readonly'].includes(entry.name) && entry.value === 'true'
315
+ )
257
316
  ? 'true'
258
- : undefined
317
+ : undefined;
259
318
  switch (field.type) {
260
319
  case 'long':
261
320
  return {
@@ -274,13 +333,15 @@ export default {
274
333
  wrapperClass: '$remove:formkit-wrapper',
275
334
  labelClass: 'ui-dynamic-form-input-label',
276
335
  inputClass: `input-${this.theme}`,
277
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
336
+ innerClass: `ui-dynamic-form-input-outlines ${
337
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
338
+ }`,
278
339
  readonly: isReadOnly,
279
- validationVisibility: 'live'
280
- }
281
- }
340
+ validationVisibility: 'live',
341
+ },
342
+ };
282
343
  case 'number':
283
- const step = field.customForm ? JSON.parse(field.customForm).step : undefined
344
+ const step = field.customForm ? JSON.parse(field.customForm).step : undefined;
284
345
  return {
285
346
  type: 'FormKit',
286
347
  props: {
@@ -297,11 +358,13 @@ export default {
297
358
  wrapperClass: '$remove:formkit-wrapper',
298
359
  labelClass: 'ui-dynamic-form-input-label',
299
360
  inputClass: `input-${this.theme}`,
300
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
361
+ innerClass: `ui-dynamic-form-input-outlines ${
362
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
363
+ }`,
301
364
  readonly: isReadOnly,
302
- validationVisibility: 'live'
303
- }
304
- }
365
+ validationVisibility: 'live',
366
+ },
367
+ };
305
368
  case 'date':
306
369
  return {
307
370
  type: 'FormKit',
@@ -316,16 +379,18 @@ export default {
316
379
  wrapperClass: '$remove:formkit-wrapper',
317
380
  labelClass: 'ui-dynamic-form-input-label',
318
381
  inputClass: `input-${this.theme}`,
319
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
382
+ innerClass: `ui-dynamic-form-input-outlines ${
383
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
384
+ }`,
320
385
  readonly: isReadOnly,
321
386
  validation,
322
- validationVisibility: 'live'
323
- }
324
- }
387
+ validationVisibility: 'live',
388
+ },
389
+ };
325
390
  case 'enum':
326
391
  const enums = field.enumValues.map((obj) => {
327
- return { value: obj.id, label: obj.name }
328
- })
392
+ return { value: obj.id, label: obj.name };
393
+ });
329
394
  return {
330
395
  type: 'FormKit',
331
396
  props: {
@@ -340,17 +405,19 @@ export default {
340
405
  wrapperClass: '$remove:formkit-wrapper',
341
406
  labelClass: 'ui-dynamic-form-input-label',
342
407
  inputClass: `input-${this.theme}`,
343
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
408
+ innerClass: `ui-dynamic-form-input-outlines ${
409
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
410
+ }`,
344
411
  readonly: isReadOnly,
345
412
  disabled: isReadOnly,
346
413
  validation,
347
- validationVisibility: 'live'
348
- }
349
- }
414
+ validationVisibility: 'live',
415
+ },
416
+ };
350
417
  case 'select':
351
418
  const selections = JSON.parse(field.customForm).entries.map((obj) => {
352
- return { value: obj.key, label: obj.value }
353
- })
419
+ return { value: obj.key, label: obj.value };
420
+ });
354
421
  return {
355
422
  type: 'FormKit',
356
423
  props: {
@@ -366,13 +433,15 @@ export default {
366
433
  wrapperClass: '$remove:formkit-wrapper',
367
434
  labelClass: 'ui-dynamic-form-input-label',
368
435
  inputClass: `input-${this.theme}`,
369
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
436
+ innerClass: `ui-dynamic-form-input-outlines ${
437
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
438
+ }`,
370
439
  readonly: isReadOnly,
371
440
  disabled: isReadOnly,
372
441
  validation,
373
- validationVisibility: 'live'
374
- }
375
- }
442
+ validationVisibility: 'live',
443
+ },
444
+ };
376
445
  case 'string':
377
446
  return {
378
447
  type: 'FormKit',
@@ -388,17 +457,19 @@ export default {
388
457
  wrapperClass: '$remove:formkit-wrapper',
389
458
  labelClass: 'ui-dynamic-form-input-label',
390
459
  inputClass: `input-${this.theme}`,
391
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
460
+ innerClass: `ui-dynamic-form-input-outlines ${
461
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
462
+ }`,
392
463
  readonly: isReadOnly,
393
464
  validation,
394
- validationVisibility: 'live'
395
- }
396
- }
465
+ validationVisibility: 'live',
466
+ },
467
+ };
397
468
  case 'confirm':
398
469
  return {
399
470
  type: 'h3',
400
- innerText: field.label
401
- }
471
+ innerText: field.label,
472
+ };
402
473
  case 'boolean':
403
474
  return {
404
475
  type: 'FormKit',
@@ -412,15 +483,133 @@ export default {
412
483
  help: hint,
413
484
  labelClass: 'ui-dynamic-form-input-label',
414
485
  inputClass: `input-${this.theme}`,
415
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
486
+ innerClass: `ui-dynamic-form-input-outlines ${
487
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
488
+ }`,
416
489
  readonly: isReadOnly,
417
490
  disabled: isReadOnly,
418
491
  validation,
419
- validationVisibility: 'live'
492
+ validationVisibility: 'live',
493
+ },
494
+ };
495
+ case 'file-preview':
496
+ // Handle file preview display only (no upload functionality)
497
+ const originalFieldId = field.id.replace('_preview', '');
498
+ if (this.formData && this.formData[originalFieldId] && this.formData[originalFieldId].length != 0) {
499
+ const fileDataArray = Array.isArray(this.formData[originalFieldId])
500
+ ? this.formData[originalFieldId]
501
+ : [this.formData[originalFieldId]];
502
+
503
+ // Separate images from other files
504
+ const images = [];
505
+ const otherFiles = [];
506
+
507
+ fileDataArray.forEach((fileData) => {
508
+ const fileName = fileData.name || '';
509
+ const isImage = fileName.toLowerCase().match(/\.(png|jpg|jpeg|gif|webp)$/);
510
+
511
+ if (isImage && fileData.file && fileData.file.data) {
512
+ // Convert buffer to base64 data URL for image display - safe for large files
513
+ const uint8Array = new Uint8Array(fileData.file.data);
514
+ let binaryString = '';
515
+
516
+ // Process in chunks to avoid call stack overflow
517
+ const chunkSize = 1024;
518
+ for (let i = 0; i < uint8Array.length; i += chunkSize) {
519
+ const chunk = uint8Array.slice(i, i + chunkSize);
520
+ binaryString += String.fromCharCode.apply(null, chunk);
521
+ }
522
+
523
+ const base64String = btoa(binaryString);
524
+ const mimeType = fileName.toLowerCase().endsWith('.png')
525
+ ? 'image/png'
526
+ : fileName.toLowerCase().endsWith('.gif')
527
+ ? 'image/gif'
528
+ : 'image/jpeg';
529
+ const dataURL = `data:${mimeType};base64,${base64String}`;
530
+
531
+ images.push({ fileName, dataURL, fileData });
532
+ } else {
533
+ otherFiles.push({ fileName, fileData });
534
+ }
535
+ });
536
+
537
+ let content = `<label class="ui-dynamic-form-input-label">${field.label} (Vorschau)${
538
+ field.required ? ' *' : ''
539
+ }</label>`;
540
+
541
+ // Display images
542
+ if (images.length > 0) {
543
+ content += '<div style="margin-top: 8px;">';
544
+ content += '<div style="font-weight: bold; margin-bottom: 8px;">Bilder:</div>';
545
+ images.forEach((img, index) => {
546
+ const downloadId = `download-img-${field.id}-${index}`;
547
+ content += `
548
+ <div style="display: inline-block; margin: 8px; text-align: center; vertical-align: top;">
549
+ <img src="${img.dataURL}" alt="${img.fileName}"
550
+ style="max-width: 300px; max-height: 200px; border: 1px solid #ccc; display: block; cursor: pointer;"
551
+ onclick="document.getElementById('${downloadId}').click();" />
552
+ <div style="margin-top: 4px; font-size: 0.9em; color: #666; max-width: 300px; word-break: break-word;">
553
+ ${img.fileName}
554
+ </div>
555
+ <a id="${downloadId}" href="${img.dataURL}" download="${img.fileName}" style="display: none;"></a>
556
+ </div>
557
+ `;
558
+ });
559
+ content += '</div>';
420
560
  }
561
+
562
+ // Display other files as list
563
+ if (otherFiles.length > 0) {
564
+ content +=
565
+ '<div style="margin-top: 12px; padding: 12px; border: 1px solid #ddd; border-radius: 4px; background: #f9f9f9;">';
566
+ content += '<div style="font-weight: bold; margin-bottom: 8px;">Weitere Dateien:</div>';
567
+ otherFiles.forEach((file, index) => {
568
+ const downloadId = `download-file-${field.id}-${index}`;
569
+ const uint8Array = new Uint8Array(file.fileData.file.data);
570
+ let binaryString = '';
571
+
572
+ // Process in chunks for download
573
+ const chunkSize = 1024;
574
+ for (let i = 0; i < uint8Array.length; i += chunkSize) {
575
+ const chunk = uint8Array.slice(i, i + chunkSize);
576
+ binaryString += String.fromCharCode.apply(null, chunk);
577
+ }
578
+
579
+ const base64String = btoa(binaryString);
580
+ const dataURL = `data:application/octet-stream;base64,${base64String}`;
581
+
582
+ content += `
583
+ <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px; padding: 4px; border-radius: 3px; cursor: pointer;"
584
+ onclick="document.getElementById('${downloadId}').click();"
585
+ onmouseover="this.style.backgroundColor='#e6e6e6';"
586
+ onmouseout="this.style.backgroundColor='transparent';">
587
+ <span style="font-size: 1.2em;">📎</span>
588
+ <span style="flex: 1; word-break: break-word;">${file.fileName}</span>
589
+ <span style="font-size: 0.8em; color: #007bff;">Download</span>
590
+ <a id="${downloadId}" href="${dataURL}" download="${file.fileName}" style="display: none;"></a>
591
+ </div>
592
+ `;
593
+ });
594
+ content += '</div>';
595
+ }
596
+
597
+ return {
598
+ type: 'div',
599
+ props: {
600
+ innerHTML: content,
601
+ },
602
+ };
421
603
  }
604
+ // If no files to preview, return empty div
605
+ return {
606
+ type: 'div',
607
+ props: {
608
+ style: 'display: none;',
609
+ },
610
+ };
422
611
  case 'file':
423
- const multiple = field.customForm ? JSON.parse(field.customForm).multiple === 'true' : false
612
+ const multiple = field.customForm ? JSON.parse(field.customForm).multiple === 'true' : false;
424
613
  return {
425
614
  type: 'FormKit',
426
615
  props: {
@@ -429,24 +618,22 @@ export default {
429
618
  name,
430
619
  label: field.label,
431
620
  required: field.required,
432
- value: this.formData[field.id],
433
621
  help: hint,
434
622
  innerClass: 'reset-background',
435
623
  wrapperClass: '$remove:formkit-wrapper',
436
624
  labelClass: 'ui-dynamic-form-input-label',
437
625
  inputClass: `input-${this.theme}`,
438
- // innerClass: ui-dynamic-form-input-outlines `${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
439
626
  readonly: isReadOnly,
440
627
  disabled: isReadOnly,
441
628
  multiple,
442
629
  validation,
443
- validationVisibility: 'live'
444
- }
445
- }
630
+ validationVisibility: 'live',
631
+ },
632
+ };
446
633
  case 'checkbox':
447
634
  const options = JSON.parse(field.customForm).entries.map((obj) => {
448
- return { value: obj.key, label: obj.value }
449
- })
635
+ return { value: obj.key, label: obj.value };
636
+ });
450
637
  return {
451
638
  type: 'FormKit',
452
639
  props: {
@@ -461,13 +648,15 @@ export default {
461
648
  fieldsetClass: 'custom-fieldset',
462
649
  labelClass: 'ui-dynamic-form-input-label',
463
650
  inputClass: `input-${this.theme}`,
464
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
651
+ innerClass: `ui-dynamic-form-input-outlines ${
652
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
653
+ }`,
465
654
  readonly: isReadOnly,
466
655
  disabled: isReadOnly,
467
656
  validation,
468
- validationVisibility: 'live'
469
- }
470
- }
657
+ validationVisibility: 'live',
658
+ },
659
+ };
471
660
  case 'color':
472
661
  return {
473
662
  type: 'FormKit',
@@ -482,9 +671,9 @@ export default {
482
671
  readonly: isReadOnly,
483
672
  disabled: isReadOnly,
484
673
  validation,
485
- validationVisibility: 'live'
486
- }
487
- }
674
+ validationVisibility: 'live',
675
+ },
676
+ };
488
677
  case 'datetime-local':
489
678
  return {
490
679
  type: 'FormKit',
@@ -499,12 +688,14 @@ export default {
499
688
  wrapperClass: '$remove:formkit-wrapper',
500
689
  labelClass: 'ui-dynamic-form-input-label',
501
690
  inputClass: `input-${this.theme}`,
502
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
691
+ innerClass: `ui-dynamic-form-input-outlines ${
692
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
693
+ }`,
503
694
  readonly: isReadOnly,
504
695
  validation,
505
- validationVisibility: 'live'
506
- }
507
- }
696
+ validationVisibility: 'live',
697
+ },
698
+ };
508
699
  case 'email':
509
700
  return {
510
701
  type: 'FormKit',
@@ -520,32 +711,34 @@ export default {
520
711
  wrapperClass: '$remove:formkit-wrapper',
521
712
  labelClass: 'ui-dynamic-form-input-label',
522
713
  inputClass: `input-${this.theme}`,
523
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
714
+ innerClass: `ui-dynamic-form-input-outlines ${
715
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
716
+ }`,
524
717
  readonly: isReadOnly,
525
718
  validation,
526
- validationVisibility: 'live'
527
- }
528
- }
719
+ validationVisibility: 'live',
720
+ },
721
+ };
529
722
  case 'header':
530
- let typeToUse = 'h1'
723
+ let typeToUse = 'h1';
531
724
  if (field.customForm && JSON.parse(field.customForm).style === 'heading_2') {
532
- typeToUse = 'h2'
725
+ typeToUse = 'h2';
533
726
  }
534
727
  if (field.customForm && JSON.parse(field.customForm).style === 'heading_3') {
535
- typeToUse = 'h3'
728
+ typeToUse = 'h3';
536
729
  }
537
730
  return {
538
731
  type: typeToUse,
539
- innerText: this.formData[field.id]
540
- }
732
+ innerText: this.formData[field.id],
733
+ };
541
734
  case 'hidden':
542
735
  return {
543
736
  type: 'input',
544
737
  props: {
545
738
  type: 'hidden',
546
- value: this.formData[field.id]
547
- }
548
- }
739
+ value: this.formData[field.id],
740
+ },
741
+ };
549
742
  case 'month':
550
743
  return {
551
744
  type: 'FormKit',
@@ -560,17 +753,19 @@ export default {
560
753
  wrapperClass: '$remove:formkit-wrapper',
561
754
  labelClass: 'ui-dynamic-form-input-label',
562
755
  inputClass: `input-${this.theme}`,
563
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
756
+ innerClass: `ui-dynamic-form-input-outlines ${
757
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
758
+ }`,
564
759
  readonly: isReadOnly,
565
760
  validation,
566
- validationVisibility: 'live'
567
- }
568
- }
761
+ validationVisibility: 'live',
762
+ },
763
+ };
569
764
  case 'paragraph':
570
765
  return {
571
766
  type: 'p',
572
- innerText: this.formData[field.id]
573
- }
767
+ innerText: this.formData[field.id],
768
+ };
574
769
  case 'password':
575
770
  return {
576
771
  type: 'FormKit',
@@ -586,16 +781,18 @@ export default {
586
781
  wrapperClass: '$remove:formkit-wrapper',
587
782
  labelClass: 'ui-dynamic-form-input-label',
588
783
  inputClass: `input-${this.theme}`,
589
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
784
+ innerClass: `ui-dynamic-form-input-outlines ${
785
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
786
+ }`,
590
787
  readonly: isReadOnly,
591
788
  validation,
592
- validationVisibility: 'live'
593
- }
594
- }
789
+ validationVisibility: 'live',
790
+ },
791
+ };
595
792
  case 'radio':
596
793
  const radioOptions = JSON.parse(field.customForm).entries.map((obj) => {
597
- return { value: obj.key, label: obj.value }
598
- })
794
+ return { value: obj.key, label: obj.value };
795
+ });
599
796
  return {
600
797
  type: 'FormKit',
601
798
  props: {
@@ -610,15 +807,17 @@ export default {
610
807
  fieldsetClass: 'custom-fieldset',
611
808
  labelClass: 'ui-dynamic-form-input-label',
612
809
  inputClass: `input-${this.theme}`,
613
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
810
+ innerClass: `ui-dynamic-form-input-outlines ${
811
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
812
+ }`,
614
813
  readonly: isReadOnly,
615
814
  disabled: isReadOnly,
616
815
  validation,
617
- validationVisibility: 'live'
618
- }
619
- }
816
+ validationVisibility: 'live',
817
+ },
818
+ };
620
819
  case 'range':
621
- const customForm = JSON.parse(field.customForm)
820
+ const customForm = JSON.parse(field.customForm);
622
821
  return {
623
822
  type: 'v-slider',
624
823
  props: {
@@ -639,9 +838,9 @@ export default {
639
838
  readonly: isReadOnly,
640
839
  disabled: isReadOnly,
641
840
  validation,
642
- validationVisibility: 'live'
643
- }
644
- }
841
+ validationVisibility: 'live',
842
+ },
843
+ };
645
844
  case 'tel':
646
845
  return {
647
846
  type: 'FormKit',
@@ -657,14 +856,16 @@ export default {
657
856
  wrapperClass: '$remove:formkit-wrapper',
658
857
  labelClass: 'ui-dynamic-form-input-label',
659
858
  inputClass: `input-${this.theme}`,
660
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
859
+ innerClass: `ui-dynamic-form-input-outlines ${
860
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
861
+ }`,
661
862
  readonly: isReadOnly,
662
863
  validation,
663
- validationVisibility: 'live'
664
- }
665
- }
864
+ validationVisibility: 'live',
865
+ },
866
+ };
666
867
  case 'textarea':
667
- const rows = field.customForm ? JSON.parse(field.customForm).rows : undefined
868
+ const rows = field.customForm ? JSON.parse(field.customForm).rows : undefined;
668
869
  return {
669
870
  type: 'FormKit',
670
871
  props: {
@@ -680,12 +881,14 @@ export default {
680
881
  wrapperClass: '$remove:formkit-wrapper',
681
882
  labelClass: 'ui-dynamic-form-input-label',
682
883
  inputClass: `input-${this.theme}`,
683
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
884
+ innerClass: `ui-dynamic-form-input-outlines ${
885
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
886
+ }`,
684
887
  readonly: isReadOnly,
685
888
  validation,
686
- validationVisibility: 'live'
687
- }
688
- }
889
+ validationVisibility: 'live',
890
+ },
891
+ };
689
892
  case 'time':
690
893
  return {
691
894
  type: 'FormKit',
@@ -701,12 +904,14 @@ export default {
701
904
  wrapperClass: '$remove:formkit-wrapper',
702
905
  labelClass: 'ui-dynamic-form-input-label',
703
906
  inputClass: `input-${this.theme}`,
704
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
907
+ innerClass: `ui-dynamic-form-input-outlines ${
908
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
909
+ }`,
705
910
  readonly: isReadOnly,
706
911
  validation,
707
- validationVisibility: 'live'
708
- }
709
- }
912
+ validationVisibility: 'live',
913
+ },
914
+ };
710
915
  case 'url':
711
916
  return {
712
917
  type: 'FormKit',
@@ -722,12 +927,14 @@ export default {
722
927
  wrapperClass: '$remove:formkit-wrapper',
723
928
  labelClass: 'ui-dynamic-form-input-label',
724
929
  inputClass: `input-${this.theme}`,
725
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
930
+ innerClass: `ui-dynamic-form-input-outlines ${
931
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
932
+ }`,
726
933
  readonly: isReadOnly,
727
934
  validation,
728
- validationVisibility: 'live'
729
- }
730
- }
935
+ validationVisibility: 'live',
936
+ },
937
+ };
731
938
  case 'week':
732
939
  return {
733
940
  type: 'FormKit',
@@ -743,12 +950,14 @@ export default {
743
950
  wrapperClass: '$remove:formkit-wrapper',
744
951
  labelClass: 'ui-dynamic-form-input-label',
745
952
  inputClass: `input-${this.theme}`,
746
- 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
+ }`,
747
956
  readonly: isReadOnly,
748
957
  validation,
749
- validationVisibility: 'live'
750
- }
751
- }
958
+ validationVisibility: 'live',
959
+ },
960
+ };
752
961
  default:
753
962
  return {
754
963
  type: 'FormKit',
@@ -762,226 +971,248 @@ export default {
762
971
  help: hint,
763
972
  labelClass: 'ui-dynamic-form-input-label',
764
973
  inputClass: `input-${this.theme}`,
765
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
974
+ innerClass: `ui-dynamic-form-input-outlines ${
975
+ this.theme === 'dark' ? '$remove:formkit-inner' : ''
976
+ }`,
766
977
  readonly: isReadOnly,
767
978
  validation,
768
- validationVisibility: 'live'
769
- }
770
- }
979
+ validationVisibility: 'live',
980
+ },
981
+ };
771
982
  }
772
983
  },
773
984
  toggleCollapse() {
774
- this.collapsed = !this.collapsed
985
+ this.collapsed = !this.collapsed;
775
986
  },
776
987
  getRowWidthStyling(field, index) {
777
- let style = ''
988
+ let style = '';
778
989
  if (index === 0) {
779
- style += 'margin-top: 12px;'
990
+ style += 'margin-top: 12px;';
780
991
  }
781
992
  if (field.type === 'header') {
782
- style += 'flex-basis: 100%;'
993
+ style += 'flex-basis: 100%;';
783
994
  } else {
784
- style += `flex-basis: 100%;`
995
+ style += `flex-basis: ${(1 / this.props.form_columns) * 100}%;`;
785
996
  }
786
- return style
997
+ return style;
787
998
  },
788
999
  fields() {
789
- const aFields = this.userTask.userTaskConfig?.formFields ?? []
1000
+ const aFields = this.userTask.userTaskConfig?.formFields ?? [];
790
1001
  const fieldMap = aFields.map((field) => ({
791
1002
  ...field,
792
- items: mapItems(field.type, field)
793
- }))
1003
+ items: mapItems(field.type, field),
1004
+ }));
794
1005
 
795
- return fieldMap
1006
+ return fieldMap;
796
1007
  },
797
1008
  /*
798
1009
  widget-action just sends a msg to Node-RED, it does not store the msg state server-side
799
1010
  alternatively, you can use widget-change, which will also store the msg in the Node's datastore
800
1011
  */
801
1012
  send(msg, index) {
802
- const msgArr = []
803
- msgArr[index] = msg
804
- this.$socket.emit('widget-action', this.id, msgArr)
1013
+ const msgArr = [];
1014
+ msgArr[index] = msg;
1015
+ this.$socket.emit('widget-action', this.id, msgArr);
805
1016
  },
806
1017
  init(msg) {
807
- this.msg = msg
1018
+ this.msg = msg;
1019
+ // Clear component cache when form data changes for performance
1020
+ this.clearComponentCache();
1021
+
808
1022
  if (!msg) {
809
- return
1023
+ return;
810
1024
  }
811
1025
 
812
- this.actions = this.props.options
1026
+ this.actions = this.props.options;
813
1027
 
814
- const hasTask = msg.payload && msg.payload.userTask
1028
+ const hasTask = msg.payload && msg.payload.userTask;
815
1029
 
816
1030
  if (hasTask) {
817
- this.userTask = msg.payload.userTask
1031
+ this.userTask = msg.payload.userTask;
818
1032
  } else {
819
- this.userTask = null
820
- this.formData = {}
821
- return
1033
+ this.userTask = null;
1034
+ this.formData = {};
1035
+ return;
822
1036
  }
823
1037
 
824
- const formFields = this.userTask.userTaskConfig.formFields
825
- const formFieldIds = formFields.map((ff) => ff.id)
826
- const initialValues = this.userTask.startToken
827
- const finishedFormData = msg.payload.formData
828
- this.formIsFinished = !!msg.payload.formData
1038
+ const formFields = this.userTask.userTaskConfig.formFields;
1039
+ const formFieldIds = formFields.map((ff) => ff.id);
1040
+ const initialValues = this.userTask.startToken.formData; //luis777
1041
+ const finishedFormData = msg.payload.formData;
1042
+ this.formIsFinished = !!msg.payload.formData;
829
1043
  if (this.formIsFinished) {
830
- this.collapsed = this.props.collapse_when_finished
1044
+ this.collapsed = this.props.collapse_when_finished;
831
1045
  }
832
1046
 
833
1047
  if (formFields) {
834
1048
  formFields.forEach((field) => {
835
- this.formData[field.id] = field.defaultValue
1049
+ this.formData[field.id] = field.defaultValue;
836
1050
 
837
1051
  if (field.type === 'confirm') {
838
- const customForm = field.customForm ? JSON.parse(field.customForm) : {}
839
- const confirmText = customForm.confirmButtonText ?? 'Confirm'
840
- const declineText = customForm.declineButtonText ?? 'Decline'
1052
+ const customForm = field.customForm ? JSON.parse(field.customForm) : {};
1053
+ const confirmText = customForm.confirmButtonText ?? 'Confirm';
1054
+ const declineText = customForm.declineButtonText ?? 'Decline';
841
1055
  this.actions = [
842
1056
  {
843
1057
  alignment: 'right',
844
1058
  primary: 'false',
845
1059
  label: declineText,
846
- condition: ''
1060
+ condition: '',
847
1061
  },
848
1062
  {
849
1063
  alignment: 'right',
850
1064
  primary: 'true',
851
1065
  label: confirmText,
852
- condition: ''
853
- }
854
- ]
1066
+ condition: '',
1067
+ },
1068
+ ];
855
1069
  }
856
- })
1070
+ });
1071
+
1072
+ // Check for file fields and duplicate them as file-preview if initial values exist
1073
+ // Insert preview fields directly before their corresponding file fields
1074
+ for (let i = formFields.length - 1; i >= 0; i--) {
1075
+ const field = formFields[i];
1076
+ if (field.type === 'file' && initialValues && initialValues[field.id]) {
1077
+ const previewField = { ...field };
1078
+ previewField.type = 'file-preview';
1079
+ previewField.id = `${field.id}_preview`; // Give it a unique ID
1080
+ this.userTask.userTaskConfig.formFields.splice(i, 0, previewField);
1081
+ }
1082
+ }
857
1083
  }
858
1084
 
859
1085
  if (initialValues) {
860
- Object.keys(initialValues)
861
- .filter((key) => formFieldIds.includes(key))
862
- .forEach((key) => {
863
- this.formData[key] = initialValues[key]
864
- })
1086
+ if (initialValues) {
1087
+ Object.keys(initialValues)
1088
+ .filter((key) => formFieldIds.includes(key))
1089
+ .forEach((key) => {
1090
+ console.log('luis888.2', key, initialValues[key]);
1091
+ this.formData[key] = initialValues[key];
1092
+ });
1093
+ }
865
1094
  }
866
1095
 
867
1096
  if (this.formIsFinished) {
868
1097
  Object.keys(finishedFormData)
869
1098
  .filter((key) => formFieldIds.includes(key))
870
1099
  .forEach((key) => {
871
- this.formData[key] = finishedFormData[key]
872
- })
1100
+ this.formData[key] = finishedFormData[key];
1101
+ });
873
1102
  }
874
1103
 
875
1104
  nextTick(() => {
876
- this.focusFirstFormField()
877
- })
1105
+ this.focusFirstFormField();
1106
+ });
878
1107
  },
879
1108
  actionFn(action) {
880
1109
  if (action.label === 'Speichern' || action.label === 'Speichern und nächster') {
881
- const formkitInputs = this.$refs.form.$el.querySelectorAll('.formkit-outer')
882
- let allComplete = true
1110
+ const formkitInputs = this.$refs.form.$el.querySelectorAll('.formkit-outer');
1111
+ let allComplete = true;
883
1112
 
884
1113
  formkitInputs.forEach((input) => {
885
- const dataComplete = input.getAttribute('data-complete')
886
- const dataInvalid = input.getAttribute('data-invalid')
1114
+ const dataComplete = input.getAttribute('data-complete');
1115
+ const dataInvalid = input.getAttribute('data-invalid');
887
1116
 
888
1117
  if (dataComplete == null && dataInvalid === 'true') {
889
- allComplete = false
1118
+ allComplete = false;
890
1119
  }
891
- })
1120
+ });
892
1121
 
893
- if (!allComplete) return
1122
+ if (!allComplete) return;
894
1123
  }
895
1124
 
896
1125
  if (this.checkCondition(action.condition)) {
897
- this.showError('')
1126
+ this.showError('');
898
1127
 
899
- const processedFormData = { ...this.formData }
900
- const formFields = this.userTask.userTaskConfig.formFields
1128
+ const processedFormData = { ...this.formData };
1129
+ const formFields = this.userTask.userTaskConfig.formFields;
901
1130
 
902
- formFields.forEach(field => {
903
- const fieldValue = processedFormData[field.id]
1131
+ formFields.forEach((field) => {
1132
+ const fieldValue = processedFormData[field.id];
904
1133
 
905
1134
  if (field.type === 'number' || field.type === 'long') {
906
1135
  if (fieldValue !== null && fieldValue !== undefined && fieldValue !== '') {
907
1136
  if (field.type === 'long') {
908
- const intValue = Number.parseInt(fieldValue, 10)
1137
+ const intValue = Number.parseInt(fieldValue, 10);
909
1138
  if (!isNaN(intValue)) {
910
- processedFormData[field.id] = intValue
1139
+ processedFormData[field.id] = intValue;
911
1140
  }
912
1141
  } else {
913
- const numValue = Number.parseFloat(fieldValue)
1142
+ const numValue = Number.parseFloat(fieldValue);
914
1143
  if (!isNaN(numValue)) {
915
- processedFormData[field.id] = numValue
1144
+ processedFormData[field.id] = numValue;
916
1145
  }
917
1146
  }
918
1147
  }
919
1148
  }
920
- })
1149
+ });
921
1150
 
922
- const msg = this.msg ?? {}
923
- msg.payload = { formData: processedFormData, userTask: this.userTask }
1151
+ const msg = this.msg ?? {};
1152
+ msg.payload = { formData: processedFormData, userTask: this.userTask };
924
1153
  this.send(
925
1154
  msg,
926
1155
  this.actions.findIndex((element) => element.label === action.label) +
927
- (this.isConfirmDialog ? this.props.options.length : 0)
928
- )
1156
+ (this.isConfirmDialog ? this.props.options.length : 0)
1157
+ );
929
1158
  // TODO: mm - end
930
1159
  } else {
931
- this.showError(action.errorMessage)
1160
+ this.showError(action.errorMessage);
932
1161
  }
933
1162
  },
934
1163
  checkCondition(condition) {
935
- if (condition === '') return true
1164
+ if (condition === '') return true;
936
1165
  try {
937
1166
  // eslint-disable-next-line no-new-func
938
- const func = Function('fields', 'userTask', 'msg', '"use strict"; return (' + condition + ')')
939
- const result = func(this.formData, this.userTask, this.msg)
940
- return Boolean(result)
1167
+ const func = Function('fields', 'userTask', 'msg', '"use strict"; return (' + condition + ')');
1168
+ const result = func(this.formData, this.userTask, this.msg);
1169
+ return Boolean(result);
941
1170
  } catch (err) {
942
- console.error('Error while evaluating condition: ' + err)
943
- return false
1171
+ console.error('Error while evaluating condition: ' + err);
1172
+ return false;
944
1173
  }
945
1174
  },
946
1175
  showError(errMsg) {
947
- this.errorMsg = errMsg
1176
+ this.errorMsg = errMsg;
948
1177
  },
949
1178
  focusFirstFormField() {
950
1179
  if (this.collapsed || !this.hasUserTask) {
951
- return
1180
+ return;
952
1181
  }
953
1182
 
954
1183
  if (this.firstFormFieldRef) {
955
- let inputElement = null
1184
+ let inputElement = null;
956
1185
 
957
1186
  if (this.firstFormFieldRef.node && this.firstFormFieldRef.node.input instanceof HTMLElement) {
958
- inputElement = this.firstFormFieldRef.node.input
1187
+ inputElement = this.firstFormFieldRef.node.input;
959
1188
  } else if (this.firstFormFieldRef.$el instanceof HTMLElement) {
960
1189
  if (['INPUT', 'TEXTAREA', 'SELECT'].includes(this.firstFormFieldRef.$el.tagName)) {
961
- inputElement = this.firstFormFieldRef.$el
1190
+ inputElement = this.firstFormFieldRef.$el;
962
1191
  } else {
963
- inputElement = this.firstFormFieldRef.$el.querySelector('input:not([type="hidden"]), textarea, select')
1192
+ inputElement = this.firstFormFieldRef.$el.querySelector(
1193
+ 'input:not([type="hidden"]), textarea, select'
1194
+ );
964
1195
  }
965
1196
  }
966
1197
 
967
1198
  if (inputElement) {
968
- inputElement.focus()
1199
+ inputElement.focus();
969
1200
  } else {
970
- console.warn('Could not find a focusable input element for the first form field.')
1201
+ console.warn('Could not find a focusable input element for the first form field.');
971
1202
  }
972
1203
  }
973
- }
974
- }
975
- }
1204
+ },
1205
+ },
1206
+ };
976
1207
 
977
1208
  function mapItems(type, field) {
978
1209
  if (type === 'enum') {
979
1210
  return field.enumValues.map((enumValue) => ({
980
1211
  title: enumValue.name,
981
- value: enumValue.id
982
- }))
1212
+ value: enumValue.id,
1213
+ }));
983
1214
  } else {
984
- return null
1215
+ return null;
985
1216
  }
986
1217
  }
987
1218
  </script>