@5minds/node-red-dashboard-2-processcube-dynamic-form 2.1.0-develop-7297c2-mdiwcn5m → 2.1.0-develop-9ecbf6-mdsqooof

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.
@@ -23,7 +23,15 @@
23
23
  :style="getRowWidthStyling(field, index)">
24
24
  <v-col cols="12">
25
25
  <component :is="createComponent(field).type"
26
- v-if="createComponent(field).innerText"
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"
27
35
  v-bind="createComponent(field).props" :ref="(el) => {
28
36
  if (index === 0) firstFormFieldRef = el;
29
37
  }
@@ -74,29 +82,60 @@
74
82
  </div>
75
83
  </template>
76
84
 
77
- <!-- eslint-disable no-case-declarations -->
78
85
  <script>
79
- import { de } from '@formkit/i18n'
80
- import { FormKit, defaultConfig, plugin } from '@formkit/vue'
81
- import { getCurrentInstance, markRaw, nextTick } from 'vue'
86
+ import { de } from '@formkit/i18n';
87
+ import { FormKit, defaultConfig, plugin } from '@formkit/vue';
88
+ import { getCurrentInstance, markRaw, nextTick } from 'vue';
89
+ import { marked } from 'marked';
90
+ import DOMPurify from 'dompurify';
82
91
 
83
- // eslint-disable-next-line import/no-unresolved
84
- import '@formkit/themes/genesis'
85
- import UIDynamicFormFooterAction from './FooterActions.vue'
86
- import UIDynamicFormTitleText from './TitleText.vue'
92
+ import '@formkit/themes/genesis';
93
+ import UIDynamicFormFooterAction from './FooterActions.vue';
94
+ import UIDynamicFormTitleText from './TitleText.vue';
87
95
 
88
- // eslint-disable-next-line no-unused-vars
89
96
  function requiredIf({ value }, [targetField, expectedValue], node) {
90
- console.debug(arguments)
97
+ console.debug(arguments);
91
98
 
92
- const actual = node?.root?.value?.[targetField]
93
- const isEmpty = value === '' || value === null || value === undefined
99
+ const actual = node?.root?.value?.[targetField];
100
+ const isEmpty = value === '' || value === null || value === undefined;
94
101
 
95
102
  if (actual === expectedValue && isEmpty) {
96
- return false // oder: `return "Dieses Feld ist erforderlich."`
103
+ return false;
97
104
  }
98
105
 
99
- return true
106
+ return true;
107
+ }
108
+
109
+ class MarkdownRenderer extends marked.Renderer {
110
+ link(params) {
111
+ const link = super.link(params);
112
+ return link.replace('<a', "<a target='_blank'");
113
+ }
114
+
115
+ html(params) {
116
+ const result = super.html(params);
117
+ if (result.startsWith('<a ') && !result.includes('target=')) {
118
+ return result.replace('<a ', `<a target="_blank" `);
119
+ }
120
+ return result;
121
+ }
122
+ }
123
+
124
+ class MarkedHooks extends marked.Hooks {
125
+ postprocess(html) {
126
+ return DOMPurify.sanitize(html, { ADD_ATTR: ['target'] });
127
+ }
128
+ }
129
+
130
+ function processMarkdown(content) {
131
+ if (!content) return '';
132
+
133
+ const html = marked.parse(content.toString(), {
134
+ renderer: new MarkdownRenderer(),
135
+ hooks: new MarkedHooks(),
136
+ });
137
+
138
+ return html;
100
139
  }
101
140
 
102
141
  export default {
@@ -104,7 +143,7 @@ export default {
104
143
  components: {
105
144
  FormKit,
106
145
  UIDynamicFormFooterAction,
107
- UIDynamicFormTitleText
146
+ UIDynamicFormTitleText,
108
147
  },
109
148
  inject: ['$socket'],
110
149
  props: {
@@ -113,24 +152,24 @@ export default {
113
152
  props: { type: Object, default: () => ({}) },
114
153
  state: {
115
154
  type: Object,
116
- default: () => ({ enabled: false, visible: false })
117
- }
155
+ default: () => ({ enabled: false, visible: false }),
156
+ },
118
157
  },
119
158
  setup(props) {
120
- console.info('UIDynamicForm setup with:', props)
121
- console.debug('Vue function loaded correctly', markRaw)
159
+ console.info('UIDynamicForm setup with:', props);
160
+ console.debug('Vue function loaded correctly', markRaw);
122
161
 
123
- const instance = getCurrentInstance()
124
- const app = instance.appContext.app
162
+ const instance = getCurrentInstance();
163
+ const app = instance.appContext.app;
125
164
 
126
165
  const formkitConfig = defaultConfig({
127
166
  theme: 'genesis',
128
167
  locales: { de },
129
168
  locale: 'de',
130
169
  // eslint-disable-next-line object-shorthand
131
- rules: { requiredIf: requiredIf }
132
- })
133
- app.use(plugin, formkitConfig)
170
+ rules: { requiredIf: requiredIf },
171
+ });
172
+ app.use(plugin, formkitConfig);
134
173
  },
135
174
  data() {
136
175
  return {
@@ -142,120 +181,121 @@ export default {
142
181
  formIsFinished: false,
143
182
  msg: null,
144
183
  collapsed: false,
145
- firstFormFieldRef: null
146
- }
184
+ firstFormFieldRef: null,
185
+ };
147
186
  },
148
187
  computed: {
149
188
  dynamicClass() {
150
- return `ui-dynamic-form-${this.theme} ui-dynamic-form-common`
189
+ return `ui-dynamic-form-${this.theme} ui-dynamic-form-common`;
151
190
  },
152
191
  dynamicFooterClass() {
153
- return `ui-dynamic-form-footer-${this.theme} ui-dynamic-form-footer-common`
192
+ return `ui-dynamic-form-footer-${this.theme} ui-dynamic-form-footer-common`;
154
193
  },
155
194
  hasUserTask() {
156
- return !!this.userTask
195
+ return !!this.userTask;
157
196
  },
158
197
  totalOutputs() {
159
198
  return (
160
199
  this.props.options.length +
161
200
  (this.props.handle_confirmation_dialogs ? 2 : 0) +
162
201
  (this.props.trigger_on_change ? 1 : 0)
163
- )
202
+ );
164
203
  },
165
204
  isConfirmDialog() {
166
- return this.userTask.userTaskConfig.formFields.some((field) => field.type === 'confirm')
205
+ return this.userTask.userTaskConfig.formFields.some((field) => field.type === 'confirm');
167
206
  },
168
207
  effectiveTitle() {
169
208
  if (this.props.title_text_type === 'str') {
170
- return this.props.title_text
209
+ return this.props.title_text;
171
210
  } else if (this.props.title_text_type === 'msg') {
172
- return this.msg.dynamicTitle
211
+ return this.msg.dynamicTitle;
173
212
  } else {
174
- return ''
213
+ return '';
175
214
  }
176
- }
215
+ },
177
216
  },
178
217
  watch: {
179
218
  formData: {
180
219
  handler(newData, oldData) {
181
220
  if (this.props.trigger_on_change) {
182
- const res = { payload: { formData: newData, userTask: this.userTask } }
183
- this.send(res, this.totalOutputs - 1)
221
+ const res = { payload: { formData: newData, userTask: this.userTask } };
222
+ this.send(res, this.totalOutputs - 1);
184
223
  }
185
224
  },
186
225
  collapsed(newVal) {
187
226
  if (!newVal && this.hasUserTask) {
188
227
  nextTick(() => {
189
- this.focusFirstFormField()
190
- })
228
+ this.focusFirstFormField();
229
+ });
191
230
  }
192
231
  },
193
232
  userTask(newVal) {
194
233
  if (newVal && !this.collapsed) {
195
234
  nextTick(() => {
196
- this.focusFirstFormField()
197
- })
235
+ this.focusFirstFormField();
236
+ });
198
237
  }
199
238
  },
200
- deep: true
201
- }
239
+ deep: true,
240
+ },
202
241
  },
203
242
  created() {
204
- const currentPath = window.location.pathname
205
- const lastPart = currentPath.substring(currentPath.lastIndexOf('/'))
243
+ const currentPath = window.location.pathname;
244
+ const lastPart = currentPath.substring(currentPath.lastIndexOf('/'));
206
245
 
207
- const store = this.$store.state
246
+ const store = this.$store.state;
208
247
 
209
248
  for (const key in store.ui.pages) {
210
249
  if (store.ui.pages[key].path === lastPart) {
211
- const theme = store.ui.pages[key].theme
250
+ const theme = store.ui.pages[key].theme;
212
251
  if (store.ui.themes[theme].name === 'ProcessCube Lightmode') {
213
- this.theme = 'light'
252
+ this.theme = 'light';
214
253
  } else if (store.ui.themes[theme].name === 'ProcessCube Darkmode') {
215
- this.theme = 'dark'
254
+ this.theme = 'dark';
216
255
  } else {
217
- this.theme = 'default'
256
+ this.theme = 'default';
218
257
  }
219
- break
258
+ break;
220
259
  }
221
260
  }
222
261
  },
223
262
  mounted() {
224
- const elements = document.querySelectorAll('.formkit-input')
263
+ const elements = document.querySelectorAll('.formkit-input');
225
264
 
226
265
  elements.forEach((element) => {
227
- element.classList.add('test')
228
- })
266
+ element.classList.add('test');
267
+ });
229
268
 
230
269
  this.$socket.on('widget-load:' + this.id, (msg) => {
231
- this.init(msg)
232
- })
270
+ this.init(msg);
271
+ });
233
272
  this.$socket.on('msg-input:' + this.id, (msg) => {
234
273
  // store the latest message in our client-side vuex store when we receive a new message
235
- this.init(msg)
236
- })
274
+ this.init(msg);
275
+ });
237
276
  // tell Node-RED that we're loading a new instance of this widget
238
- this.$socket.emit('widget-load', this.id)
277
+ this.$socket.emit('widget-load', this.id);
239
278
  },
240
279
  unmounted() {
241
280
  /* 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)
281
+ this.$socket?.off('widget-load' + this.id);
282
+ this.$socket?.off('msg-input:' + this.id);
244
283
  },
245
284
  methods: {
246
285
  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 ?? []
286
+ console.debug('Creating component for field:', field);
287
+ const customForm = field.customForm ? JSON.parse(field.customForm) : {};
288
+ const hint = customForm.hint;
289
+ const placeholder = customForm.placeholder;
290
+ const validation = customForm.validation;
291
+ const name = field.id;
292
+ const customProperties = customForm.customProperties ?? [];
253
293
  const isReadOnly =
254
294
  this.props.readonly ||
255
295
  this.formIsFinished ||
256
296
  customProperties.find((entry) => ['readOnly', 'readonly'].includes(entry.name) && entry.value === 'true')
257
297
  ? 'true'
258
- : undefined
298
+ : undefined;
259
299
  switch (field.type) {
260
300
  case 'long':
261
301
  return {
@@ -276,11 +316,11 @@ export default {
276
316
  inputClass: `input-${this.theme}`,
277
317
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
278
318
  readonly: isReadOnly,
279
- validationVisibility: 'live'
280
- }
281
- }
319
+ validationVisibility: 'live',
320
+ },
321
+ };
282
322
  case 'number':
283
- const step = field.customForm ? JSON.parse(field.customForm).step : undefined
323
+ const step = field.customForm ? JSON.parse(field.customForm).step : undefined;
284
324
  return {
285
325
  type: 'FormKit',
286
326
  props: {
@@ -299,9 +339,9 @@ export default {
299
339
  inputClass: `input-${this.theme}`,
300
340
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
301
341
  readonly: isReadOnly,
302
- validationVisibility: 'live'
303
- }
304
- }
342
+ validationVisibility: 'live',
343
+ },
344
+ };
305
345
  case 'date':
306
346
  return {
307
347
  type: 'FormKit',
@@ -319,13 +359,13 @@ export default {
319
359
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
320
360
  readonly: isReadOnly,
321
361
  validation,
322
- validationVisibility: 'live'
323
- }
324
- }
362
+ validationVisibility: 'live',
363
+ },
364
+ };
325
365
  case 'enum':
326
366
  const enums = field.enumValues.map((obj) => {
327
- return { value: obj.id, label: obj.name }
328
- })
367
+ return { value: obj.id, label: obj.name };
368
+ });
329
369
  return {
330
370
  type: 'FormKit',
331
371
  props: {
@@ -344,13 +384,13 @@ export default {
344
384
  readonly: isReadOnly,
345
385
  disabled: isReadOnly,
346
386
  validation,
347
- validationVisibility: 'live'
348
- }
349
- }
387
+ validationVisibility: 'live',
388
+ },
389
+ };
350
390
  case 'select':
351
391
  const selections = JSON.parse(field.customForm).entries.map((obj) => {
352
- return { value: obj.key, label: obj.value }
353
- })
392
+ return { value: obj.key, label: obj.value };
393
+ });
354
394
  return {
355
395
  type: 'FormKit',
356
396
  props: {
@@ -370,9 +410,9 @@ export default {
370
410
  readonly: isReadOnly,
371
411
  disabled: isReadOnly,
372
412
  validation,
373
- validationVisibility: 'live'
374
- }
375
- }
413
+ validationVisibility: 'live',
414
+ },
415
+ };
376
416
  case 'string':
377
417
  return {
378
418
  type: 'FormKit',
@@ -391,14 +431,14 @@ export default {
391
431
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
392
432
  readonly: isReadOnly,
393
433
  validation,
394
- validationVisibility: 'live'
395
- }
396
- }
434
+ validationVisibility: 'live',
435
+ },
436
+ };
397
437
  case 'confirm':
398
438
  return {
399
439
  type: 'h3',
400
- innerText: field.label
401
- }
440
+ innerText: field.label,
441
+ };
402
442
  case 'boolean':
403
443
  return {
404
444
  type: 'FormKit',
@@ -416,11 +456,11 @@ export default {
416
456
  readonly: isReadOnly,
417
457
  disabled: isReadOnly,
418
458
  validation,
419
- validationVisibility: 'live'
420
- }
421
- }
459
+ validationVisibility: 'live',
460
+ },
461
+ };
422
462
  case 'file':
423
- const multiple = field.customForm ? JSON.parse(field.customForm).multiple === 'true' : false
463
+ const multiple = field.customForm ? JSON.parse(field.customForm).multiple === 'true' : false;
424
464
  return {
425
465
  type: 'FormKit',
426
466
  props: {
@@ -440,13 +480,13 @@ export default {
440
480
  disabled: isReadOnly,
441
481
  multiple,
442
482
  validation,
443
- validationVisibility: 'live'
444
- }
445
- }
483
+ validationVisibility: 'live',
484
+ },
485
+ };
446
486
  case 'checkbox':
447
487
  const options = JSON.parse(field.customForm).entries.map((obj) => {
448
- return { value: obj.key, label: obj.value }
449
- })
488
+ return { value: obj.key, label: obj.value };
489
+ });
450
490
  return {
451
491
  type: 'FormKit',
452
492
  props: {
@@ -465,9 +505,9 @@ export default {
465
505
  readonly: isReadOnly,
466
506
  disabled: isReadOnly,
467
507
  validation,
468
- validationVisibility: 'live'
469
- }
470
- }
508
+ validationVisibility: 'live',
509
+ },
510
+ };
471
511
  case 'color':
472
512
  return {
473
513
  type: 'FormKit',
@@ -482,9 +522,9 @@ export default {
482
522
  readonly: isReadOnly,
483
523
  disabled: isReadOnly,
484
524
  validation,
485
- validationVisibility: 'live'
486
- }
487
- }
525
+ validationVisibility: 'live',
526
+ },
527
+ };
488
528
  case 'datetime-local':
489
529
  return {
490
530
  type: 'FormKit',
@@ -502,9 +542,9 @@ export default {
502
542
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
503
543
  readonly: isReadOnly,
504
544
  validation,
505
- validationVisibility: 'live'
506
- }
507
- }
545
+ validationVisibility: 'live',
546
+ },
547
+ };
508
548
  case 'email':
509
549
  return {
510
550
  type: 'FormKit',
@@ -523,29 +563,29 @@ export default {
523
563
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
524
564
  readonly: isReadOnly,
525
565
  validation,
526
- validationVisibility: 'live'
527
- }
528
- }
566
+ validationVisibility: 'live',
567
+ },
568
+ };
529
569
  case 'header':
530
- let typeToUse = 'h1'
570
+ let typeToUse = 'h1';
531
571
  if (field.customForm && JSON.parse(field.customForm).style === 'heading_2') {
532
- typeToUse = 'h2'
572
+ typeToUse = 'h2';
533
573
  }
534
574
  if (field.customForm && JSON.parse(field.customForm).style === 'heading_3') {
535
- typeToUse = 'h3'
575
+ typeToUse = 'h3';
536
576
  }
537
577
  return {
538
578
  type: typeToUse,
539
- innerText: this.formData[field.id]
540
- }
579
+ innerText: this.formData[field.id],
580
+ };
541
581
  case 'hidden':
542
582
  return {
543
583
  type: 'input',
544
584
  props: {
545
585
  type: 'hidden',
546
- value: this.formData[field.id]
547
- }
548
- }
586
+ value: this.formData[field.id],
587
+ },
588
+ };
549
589
  case 'month':
550
590
  return {
551
591
  type: 'FormKit',
@@ -563,14 +603,17 @@ export default {
563
603
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
564
604
  readonly: isReadOnly,
565
605
  validation,
566
- validationVisibility: 'live'
567
- }
568
- }
606
+ validationVisibility: 'live',
607
+ },
608
+ };
569
609
  case 'paragraph':
610
+ const paragraphContent = this.formData[field.id] || field.defaultValue || field.label || '';
611
+ const processedHtml = processMarkdown(paragraphContent);
570
612
  return {
571
- type: 'p',
572
- innerText: this.formData[field.id]
573
- }
613
+ type: 'div',
614
+ innerHTML: processedHtml,
615
+ class: 'ui-dynamic-form-paragraph',
616
+ };
574
617
  case 'password':
575
618
  return {
576
619
  type: 'FormKit',
@@ -589,13 +632,13 @@ export default {
589
632
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
590
633
  readonly: isReadOnly,
591
634
  validation,
592
- validationVisibility: 'live'
593
- }
594
- }
635
+ validationVisibility: 'live',
636
+ },
637
+ };
595
638
  case 'radio':
596
639
  const radioOptions = JSON.parse(field.customForm).entries.map((obj) => {
597
- return { value: obj.key, label: obj.value }
598
- })
640
+ return { value: obj.key, label: obj.value };
641
+ });
599
642
  return {
600
643
  type: 'FormKit',
601
644
  props: {
@@ -614,11 +657,11 @@ export default {
614
657
  readonly: isReadOnly,
615
658
  disabled: isReadOnly,
616
659
  validation,
617
- validationVisibility: 'live'
618
- }
619
- }
660
+ validationVisibility: 'live',
661
+ },
662
+ };
620
663
  case 'range':
621
- const customForm = JSON.parse(field.customForm)
664
+ const customForm = JSON.parse(field.customForm);
622
665
  return {
623
666
  type: 'v-slider',
624
667
  props: {
@@ -639,9 +682,9 @@ export default {
639
682
  readonly: isReadOnly,
640
683
  disabled: isReadOnly,
641
684
  validation,
642
- validationVisibility: 'live'
643
- }
644
- }
685
+ validationVisibility: 'live',
686
+ },
687
+ };
645
688
  case 'tel':
646
689
  return {
647
690
  type: 'FormKit',
@@ -660,11 +703,11 @@ export default {
660
703
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
661
704
  readonly: isReadOnly,
662
705
  validation,
663
- validationVisibility: 'live'
664
- }
665
- }
706
+ validationVisibility: 'live',
707
+ },
708
+ };
666
709
  case 'textarea':
667
- const rows = field.customForm ? JSON.parse(field.customForm).rows : undefined
710
+ const rows = field.customForm ? JSON.parse(field.customForm).rows : undefined;
668
711
  return {
669
712
  type: 'FormKit',
670
713
  props: {
@@ -683,9 +726,9 @@ export default {
683
726
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
684
727
  readonly: isReadOnly,
685
728
  validation,
686
- validationVisibility: 'live'
687
- }
688
- }
729
+ validationVisibility: 'live',
730
+ },
731
+ };
689
732
  case 'time':
690
733
  return {
691
734
  type: 'FormKit',
@@ -704,9 +747,9 @@ export default {
704
747
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
705
748
  readonly: isReadOnly,
706
749
  validation,
707
- validationVisibility: 'live'
708
- }
709
- }
750
+ validationVisibility: 'live',
751
+ },
752
+ };
710
753
  case 'url':
711
754
  return {
712
755
  type: 'FormKit',
@@ -725,9 +768,9 @@ export default {
725
768
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
726
769
  readonly: isReadOnly,
727
770
  validation,
728
- validationVisibility: 'live'
729
- }
730
- }
771
+ validationVisibility: 'live',
772
+ },
773
+ };
731
774
  case 'week':
732
775
  return {
733
776
  type: 'FormKit',
@@ -746,9 +789,9 @@ export default {
746
789
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
747
790
  readonly: isReadOnly,
748
791
  validation,
749
- validationVisibility: 'live'
750
- }
751
- }
792
+ validationVisibility: 'live',
793
+ },
794
+ };
752
795
  default:
753
796
  return {
754
797
  type: 'FormKit',
@@ -765,223 +808,223 @@ export default {
765
808
  innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
766
809
  readonly: isReadOnly,
767
810
  validation,
768
- validationVisibility: 'live'
769
- }
770
- }
811
+ validationVisibility: 'live',
812
+ },
813
+ };
771
814
  }
772
815
  },
773
816
  toggleCollapse() {
774
- this.collapsed = !this.collapsed
817
+ this.collapsed = !this.collapsed;
775
818
  },
776
819
  getRowWidthStyling(field, index) {
777
- let style = ''
820
+ let style = '';
778
821
  if (index === 0) {
779
- style += 'margin-top: 12px;'
822
+ style += 'margin-top: 12px;';
780
823
  }
781
824
  if (field.type === 'header') {
782
- style += 'flex-basis: 100%;'
825
+ style += 'flex-basis: 100%;';
783
826
  } else {
784
- style += `flex-basis: 100%;`
827
+ style += `flex-basis: 100%;`;
785
828
  }
786
- return style
829
+ return style;
787
830
  },
788
831
  fields() {
789
- const aFields = this.userTask.userTaskConfig?.formFields ?? []
832
+ const aFields = this.userTask.userTaskConfig?.formFields ?? [];
790
833
  const fieldMap = aFields.map((field) => ({
791
834
  ...field,
792
- items: mapItems(field.type, field)
793
- }))
835
+ items: mapItems(field.type, field),
836
+ }));
794
837
 
795
- return fieldMap
838
+ return fieldMap;
796
839
  },
797
840
  /*
798
- widget-action just sends a msg to Node-RED, it does not store the msg state server-side
799
- alternatively, you can use widget-change, which will also store the msg in the Node's datastore
800
- */
841
+ widget-action just sends a msg to Node-RED, it does not store the msg state server-side
842
+ alternatively, you can use widget-change, which will also store the msg in the Node's datastore
843
+ */
801
844
  send(msg, index) {
802
- const msgArr = []
803
- msgArr[index] = msg
804
- this.$socket.emit('widget-action', this.id, msgArr)
845
+ const msgArr = [];
846
+ msgArr[index] = msg;
847
+ this.$socket.emit('widget-action', this.id, msgArr);
805
848
  },
806
849
  init(msg) {
807
- this.msg = msg
850
+ this.msg = msg;
808
851
  if (!msg) {
809
- return
852
+ return;
810
853
  }
811
854
 
812
- this.actions = this.props.options
855
+ this.actions = this.props.options;
813
856
 
814
- const hasTask = msg.payload && msg.payload.userTask
857
+ const hasTask = msg.payload && msg.payload.userTask;
815
858
 
816
859
  if (hasTask) {
817
- this.userTask = msg.payload.userTask
860
+ this.userTask = msg.payload.userTask;
818
861
  } else {
819
- this.userTask = null
820
- this.formData = {}
821
- return
862
+ this.userTask = null;
863
+ this.formData = {};
864
+ return;
822
865
  }
823
866
 
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
867
+ const formFields = this.userTask.userTaskConfig.formFields;
868
+ const formFieldIds = formFields.map((ff) => ff.id);
869
+ const initialValues = this.userTask.startToken;
870
+ const finishedFormData = msg.payload.formData;
871
+ this.formIsFinished = !!msg.payload.formData;
829
872
  if (this.formIsFinished) {
830
- this.collapsed = this.props.collapse_when_finished
873
+ this.collapsed = this.props.collapse_when_finished;
831
874
  }
832
875
 
833
876
  if (formFields) {
834
877
  formFields.forEach((field) => {
835
- this.formData[field.id] = field.defaultValue
878
+ this.formData[field.id] = field.defaultValue;
836
879
 
837
880
  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'
881
+ const customForm = field.customForm ? JSON.parse(field.customForm) : {};
882
+ const confirmText = customForm.confirmButtonText ?? 'Confirm';
883
+ const declineText = customForm.declineButtonText ?? 'Decline';
841
884
  this.actions = [
842
885
  {
843
886
  alignment: 'right',
844
887
  primary: 'false',
845
888
  label: declineText,
846
- condition: ''
889
+ condition: '',
847
890
  },
848
891
  {
849
892
  alignment: 'right',
850
893
  primary: 'true',
851
894
  label: confirmText,
852
- condition: ''
853
- }
854
- ]
895
+ condition: '',
896
+ },
897
+ ];
855
898
  }
856
- })
899
+ });
857
900
  }
858
901
 
859
902
  if (initialValues) {
860
903
  Object.keys(initialValues)
861
904
  .filter((key) => formFieldIds.includes(key))
862
905
  .forEach((key) => {
863
- this.formData[key] = initialValues[key]
864
- })
906
+ this.formData[key] = initialValues[key];
907
+ });
865
908
  }
866
909
 
867
910
  if (this.formIsFinished) {
868
911
  Object.keys(finishedFormData)
869
912
  .filter((key) => formFieldIds.includes(key))
870
913
  .forEach((key) => {
871
- this.formData[key] = finishedFormData[key]
872
- })
914
+ this.formData[key] = finishedFormData[key];
915
+ });
873
916
  }
874
917
 
875
918
  nextTick(() => {
876
- this.focusFirstFormField()
877
- })
919
+ this.focusFirstFormField();
920
+ });
878
921
  },
879
922
  actionFn(action) {
880
923
  if (action.label === 'Speichern' || action.label === 'Speichern und nächster') {
881
- const formkitInputs = this.$refs.form.$el.querySelectorAll('.formkit-outer')
882
- let allComplete = true
924
+ const formkitInputs = this.$refs.form.$el.querySelectorAll('.formkit-outer');
925
+ let allComplete = true;
883
926
 
884
927
  formkitInputs.forEach((input) => {
885
- const dataComplete = input.getAttribute('data-complete')
886
- const dataInvalid = input.getAttribute('data-invalid')
928
+ const dataComplete = input.getAttribute('data-complete');
929
+ const dataInvalid = input.getAttribute('data-invalid');
887
930
 
888
931
  if (dataComplete == null && dataInvalid === 'true') {
889
- allComplete = false
932
+ allComplete = false;
890
933
  }
891
- })
934
+ });
892
935
 
893
- if (!allComplete) return
936
+ if (!allComplete) return;
894
937
  }
895
938
 
896
939
  if (this.checkCondition(action.condition)) {
897
- this.showError('')
940
+ this.showError('');
898
941
 
899
- const processedFormData = { ...this.formData }
900
- const formFields = this.userTask.userTaskConfig.formFields
942
+ const processedFormData = { ...this.formData };
943
+ const formFields = this.userTask.userTaskConfig.formFields;
901
944
 
902
- formFields.forEach(field => {
903
- const fieldValue = processedFormData[field.id]
945
+ formFields.forEach((field) => {
946
+ const fieldValue = processedFormData[field.id];
904
947
 
905
948
  if (field.type === 'number' || field.type === 'long') {
906
949
  if (fieldValue !== null && fieldValue !== undefined && fieldValue !== '') {
907
950
  if (field.type === 'long') {
908
- const intValue = Number.parseInt(fieldValue, 10)
951
+ const intValue = Number.parseInt(fieldValue, 10);
909
952
  if (!isNaN(intValue)) {
910
- processedFormData[field.id] = intValue
953
+ processedFormData[field.id] = intValue;
911
954
  }
912
955
  } else {
913
- const numValue = Number.parseFloat(fieldValue)
956
+ const numValue = Number.parseFloat(fieldValue);
914
957
  if (!isNaN(numValue)) {
915
- processedFormData[field.id] = numValue
958
+ processedFormData[field.id] = numValue;
916
959
  }
917
960
  }
918
961
  }
919
962
  }
920
- })
963
+ });
921
964
 
922
- const msg = this.msg ?? {}
923
- msg.payload = { formData: processedFormData, userTask: this.userTask }
965
+ const msg = this.msg ?? {};
966
+ msg.payload = { formData: processedFormData, userTask: this.userTask };
924
967
  this.send(
925
968
  msg,
926
969
  this.actions.findIndex((element) => element.label === action.label) +
927
970
  (this.isConfirmDialog ? this.props.options.length : 0)
928
- )
971
+ );
929
972
  // TODO: mm - end
930
973
  } else {
931
- this.showError(action.errorMessage)
974
+ this.showError(action.errorMessage);
932
975
  }
933
976
  },
934
977
  checkCondition(condition) {
935
- if (condition === '') return true
978
+ if (condition === '') return true;
936
979
  try {
937
980
  // 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)
981
+ const func = Function('fields', 'userTask', 'msg', '"use strict"; return (' + condition + ')');
982
+ const result = func(this.formData, this.userTask, this.msg);
983
+ return Boolean(result);
941
984
  } catch (err) {
942
- console.error('Error while evaluating condition: ' + err)
943
- return false
985
+ console.error('Error while evaluating condition: ' + err);
986
+ return false;
944
987
  }
945
988
  },
946
989
  showError(errMsg) {
947
- this.errorMsg = errMsg
990
+ this.errorMsg = errMsg;
948
991
  },
949
992
  focusFirstFormField() {
950
993
  if (this.collapsed || !this.hasUserTask) {
951
- return
994
+ return;
952
995
  }
953
996
 
954
997
  if (this.firstFormFieldRef) {
955
- let inputElement = null
998
+ let inputElement = null;
956
999
 
957
1000
  if (this.firstFormFieldRef.node && this.firstFormFieldRef.node.input instanceof HTMLElement) {
958
- inputElement = this.firstFormFieldRef.node.input
1001
+ inputElement = this.firstFormFieldRef.node.input;
959
1002
  } else if (this.firstFormFieldRef.$el instanceof HTMLElement) {
960
1003
  if (['INPUT', 'TEXTAREA', 'SELECT'].includes(this.firstFormFieldRef.$el.tagName)) {
961
- inputElement = this.firstFormFieldRef.$el
1004
+ inputElement = this.firstFormFieldRef.$el;
962
1005
  } else {
963
- inputElement = this.firstFormFieldRef.$el.querySelector('input:not([type="hidden"]), textarea, select')
1006
+ inputElement = this.firstFormFieldRef.$el.querySelector('input:not([type="hidden"]), textarea, select');
964
1007
  }
965
1008
  }
966
1009
 
967
1010
  if (inputElement) {
968
- inputElement.focus()
1011
+ inputElement.focus();
969
1012
  } else {
970
- console.warn('Could not find a focusable input element for the first form field.')
1013
+ console.warn('Could not find a focusable input element for the first form field.');
971
1014
  }
972
1015
  }
973
- }
974
- }
975
- }
1016
+ },
1017
+ },
1018
+ };
976
1019
 
977
1020
  function mapItems(type, field) {
978
1021
  if (type === 'enum') {
979
1022
  return field.enumValues.map((enumValue) => ({
980
1023
  title: enumValue.name,
981
- value: enumValue.id
982
- }))
1024
+ value: enumValue.id,
1025
+ }));
983
1026
  } else {
984
- return null
1027
+ return null;
985
1028
  }
986
1029
  }
987
1030
  </script>