@5minds/node-red-dashboard-2-processcube-dynamic-form 2.0.3 → 2.0.4-feature-e6de27-mcerd06i

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,50 @@
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
5
- v-if="props.title_style === 'outside' && hasUserTask"
6
- :style="props.title_style"
7
- :title="props.title_text"
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
- />
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" />
14
8
  <div className="ui-dynamic-form-wrapper">
15
- <p v-if="hasUserTask" style="margin-bottom: 0px;">
9
+ <p v-if="hasUserTask" style="margin-bottom: 0px">
16
10
  <v-form ref="form" v-model="form" :class="dynamicClass">
17
- <UIDynamicFormTitleText
18
- v-if="props.title_style != 'outside'"
19
- :style="props.title_style"
20
- :title="props.title_text"
21
- :customStyles="props.title_custom_text_styling"
11
+ <UIDynamicFormTitleText v-if="props.title_style != 'outside'" :style="props.title_style"
12
+ :title="effectiveTitle" :customStyles="props.title_custom_text_styling"
22
13
  :titleIcon="props.title_icon"
23
14
  :collapsible="props.collapsible || (props.collapse_when_finished && formIsFinished)"
24
- :collapsed="collapsed"
25
- :toggleCollapse="toggleCollapse"
26
- />
15
+ :collapsed="collapsed" :toggleCollapse="toggleCollapse" />
27
16
  <Transition name="cardCollapse">
28
17
  <div v-if="!collapsed">
29
- <div className="ui-dynamic-form-formfield-positioner">
18
+ <div className="ui-dynamic-form-formfield-positioner" :style="props.inner_card_styling">
30
19
  <FormKit id="form" type="group">
31
- <v-row v-for="(field, index) in fields()" :key="field" :style="getRowWidthStyling(field, index)">
20
+ <v-row v-for="(field, index) in fields()" :key="field"
21
+ :style="getRowWidthStyling(field, index)">
32
22
  <v-col cols="12">
33
- <component
34
- :is="createComponent(field).type"
23
+ <component :is="createComponent(field).type"
35
24
  v-if="createComponent(field).innerText"
36
- v-bind="createComponent(field).props"
37
- v-model="formData[field.id]"
38
- >
25
+ v-bind="createComponent(field).props" :ref="(el) => {
26
+ if (index === 0) firstFormFieldRef = el;
27
+ }
28
+ " v-model="formData[field.id]">
39
29
  {{ createComponent(field).innerText }}
40
30
  </component>
41
31
  <div v-else-if="createComponent(field).type == 'v-slider'">
42
32
  <p class="formkit-label">{{ field.label }}</p>
43
- <component
44
- :is="createComponent(field).type"
45
- v-bind="createComponent(field).props"
46
- v-model="field.defaultValue"
47
- />
33
+ <component :is="createComponent(field).type"
34
+ v-bind="createComponent(field).props" :ref="(el) => {
35
+ if (index === 0) firstFormFieldRef = el;
36
+ }
37
+ " v-model="field.defaultValue" />
48
38
  <p class="formkit-help">
49
- {{ field.customForm ? JSON.parse(field.customForm).hint : undefined }}
39
+ {{ field.customForm ? JSON.parse(field.customForm).hint : undefined
40
+ }}
50
41
  </p>
51
42
  </div>
52
- <component
53
- :is="createComponent(field).type"
54
- v-else
55
- v-bind="createComponent(field).props"
56
- v-model="formData[field.id]"
57
- />
43
+ <component :is="createComponent(field).type" v-else
44
+ v-bind="createComponent(field).props" :ref="(el) => {
45
+ if (index === 0) firstFormFieldRef = el;
46
+ }
47
+ " v-model="formData[field.id]" />
58
48
  </v-col>
59
49
  </v-row>
60
50
  </FormKit>
@@ -63,17 +53,20 @@
63
53
  <v-row v-if="errorMsg.length > 0" style="padding: 12px">
64
54
  <v-alert type="error">Error: {{ errorMsg }}</v-alert>
65
55
  </v-row>
66
- <UIDynamicFormFooterAction v-if="props.actions_inside_card && actions.length > 0" :actions="actions" :actionCallback="actionFn" :formIsFinished="formIsFinished" style="padding: 16px; padding-top: 0px;" />
56
+ <UIDynamicFormFooterAction v-if="props.actions_inside_card && actions.length > 0"
57
+ :actions="actions" :actionCallback="actionFn" :formIsFinished="formIsFinished"
58
+ style="padding: 16px; padding-top: 0px" />
67
59
  </v-row>
68
60
  </div>
69
61
  </Transition>
70
62
  </v-form>
71
63
  </p>
72
64
  <p v-else>
73
- <v-alert v-if="props.waiting_info.length > 0 || props.waiting_title.length > 0" :text="props.waiting_info" :title="props.waiting_title" />
65
+ <v-alert v-if="props.waiting_info.length > 0 || props.waiting_title.length > 0"
66
+ :text="props.waiting_info" :title="props.waiting_title" />
74
67
  </p>
75
68
  </div>
76
- <div v-if="!props.actions_inside_card && actions.length > 0 && hasUserTask" style="padding-top: 32px;">
69
+ <div v-if="!props.actions_inside_card && actions.length > 0 && hasUserTask" style="padding-top: 32px">
77
70
  <UIDynamicFormFooterAction :actions="actions" :actionCallback="actionFn" />
78
71
  </div>
79
72
  </div>
@@ -81,22 +74,39 @@
81
74
 
82
75
  <!-- eslint-disable no-case-declarations -->
83
76
  <script>
77
+ import { de } from '@formkit/i18n'
84
78
  import { FormKit, defaultConfig, plugin } from '@formkit/vue'
85
- import { getCurrentInstance, markRaw } from 'vue'
79
+ import { getCurrentInstance, markRaw, nextTick } from 'vue'
86
80
 
87
81
  // eslint-disable-next-line import/no-unresolved
88
82
  import '@formkit/themes/genesis'
89
83
  import UIDynamicFormFooterAction from './FooterActions.vue'
90
84
  import UIDynamicFormTitleText from './TitleText.vue'
91
85
 
86
+ // eslint-disable-next-line no-unused-vars
87
+ function requiredIf({ value }, [targetField, expectedValue], node) {
88
+ console.debug(arguments)
89
+
90
+ const actual = node?.root?.value?.[targetField]
91
+ const isEmpty = value === '' || value === null || value === undefined
92
+
93
+ if (actual === expectedValue && isEmpty) {
94
+ return false // oder: `return "Dieses Feld ist erforderlich."`
95
+ }
96
+
97
+ return true
98
+ }
99
+
92
100
  export default {
93
101
  name: 'UIDynamicForm',
94
102
  components: {
95
- FormKit, UIDynamicFormFooterAction, UIDynamicFormTitleText
103
+ FormKit,
104
+ UIDynamicFormFooterAction,
105
+ UIDynamicFormTitleText
96
106
  },
97
107
  inject: ['$socket'],
98
108
  props: {
99
- /* do not remove entries from this - Dashboard's Layout Manager's will pass this data to your component */
109
+ /* do not remove entries from this - Dashboard's Layout Manager's will pass this data to your component */
100
110
  id: { type: String, required: true },
101
111
  props: { type: Object, default: () => ({}) },
102
112
  state: {
@@ -104,7 +114,7 @@ export default {
104
114
  default: () => ({ enabled: false, visible: false })
105
115
  }
106
116
  },
107
- setup (props) {
117
+ setup(props) {
108
118
  console.info('UIDynamicForm setup with:', props)
109
119
  console.debug('Vue function loaded correctly', markRaw)
110
120
 
@@ -113,24 +123,14 @@ export default {
113
123
 
114
124
  const formkitConfig = defaultConfig({
115
125
  theme: 'genesis',
116
- rules: {
117
- requiredIf: ({ value, name }, [targetField, expectedValue], node) => {
118
- const actual = node?.root?.value?.[targetField]
119
- if (actual === expectedValue && (!value || value === '')) {
120
- return `Feld ${name} ist erforderlich, wenn ${targetField} = ${expectedValue}`
121
- }
122
- return true
123
- }
124
- },
125
- messages: {
126
- de: {
127
- requiredIf: 'Dieses Feld ist erforderlich.'
128
- }
129
- }
126
+ locales: { de },
127
+ locale: 'de',
128
+ // eslint-disable-next-line object-shorthand
129
+ rules: { requiredIf: requiredIf }
130
130
  })
131
131
  app.use(plugin, formkitConfig)
132
132
  },
133
- data () {
133
+ data() {
134
134
  return {
135
135
  actions: [],
136
136
  formData: {},
@@ -139,38 +139,66 @@ export default {
139
139
  errorMsg: '',
140
140
  formIsFinished: false,
141
141
  msg: null,
142
- collapsed: false
142
+ collapsed: false,
143
+ firstFormFieldRef: null
143
144
  }
144
145
  },
145
146
  computed: {
146
- dynamicClass () {
147
+ dynamicClass() {
147
148
  return `ui-dynamic-form-${this.theme} ui-dynamic-form-common`
148
149
  },
149
- dynamicFooterClass () {
150
+ dynamicFooterClass() {
150
151
  return `ui-dynamic-form-footer-${this.theme} ui-dynamic-form-footer-common`
151
152
  },
152
- hasUserTask () {
153
+ hasUserTask() {
153
154
  return !!this.userTask
154
155
  },
155
- totalOutputs () {
156
- return this.props.options.length + (this.props.handle_confirmation_dialogs ? 2 : 0) + (this.props.trigger_on_change ? 1 : 0)
156
+ totalOutputs() {
157
+ return (
158
+ this.props.options.length +
159
+ (this.props.handle_confirmation_dialogs ? 2 : 0) +
160
+ (this.props.trigger_on_change ? 1 : 0)
161
+ )
162
+ },
163
+ isConfirmDialog() {
164
+ return this.userTask.userTaskConfig.formFields.some((field) => field.type === 'confirm')
157
165
  },
158
- isConfirmDialog () {
159
- return this.userTask.userTaskConfig.formFields.some(field => field.type === 'confirm')
166
+ effectiveTitle() {
167
+ if (this.props.title_text_type === 'str') {
168
+ return this.props.title_text
169
+ } else if (this.props.title_text_type === 'msg') {
170
+ return this.msg.dynamicTitle
171
+ } else {
172
+ return ''
173
+ }
160
174
  }
161
175
  },
162
176
  watch: {
163
177
  formData: {
164
- handler (newData, oldData) {
178
+ handler(newData, oldData) {
165
179
  if (this.props.trigger_on_change) {
166
180
  const res = { payload: { formData: newData, userTask: this.userTask } }
167
181
  this.send(res, this.totalOutputs - 1)
168
182
  }
169
183
  },
184
+ collapsed(newVal) {
185
+ if (!newVal && this.hasUserTask) {
186
+ nextTick(() => {
187
+ this.focusFirstFormField()
188
+ })
189
+ }
190
+ },
191
+ userTask(newVal) {
192
+ if (newVal && !this.collapsed) {
193
+ nextTick(() => {
194
+ this.focusFirstFormField()
195
+ })
196
+ }
197
+ },
170
198
  deep: true
171
199
  }
172
200
  },
173
- created () {
201
+ created() {
174
202
  const currentPath = window.location.pathname
175
203
  const lastPart = currentPath.substring(currentPath.lastIndexOf('/'))
176
204
 
@@ -190,7 +218,7 @@ export default {
190
218
  }
191
219
  }
192
220
  },
193
- mounted () {
221
+ mounted() {
194
222
  const elements = document.querySelectorAll('.formkit-input')
195
223
 
196
224
  elements.forEach((element) => {
@@ -207,537 +235,541 @@ export default {
207
235
  // tell Node-RED that we're loading a new instance of this widget
208
236
  this.$socket.emit('widget-load', this.id)
209
237
  },
210
- unmounted () {
238
+ unmounted() {
211
239
  /* Make sure, any events you subscribe to on SocketIO are unsubscribed to here */
212
240
  this.$socket?.off('widget-load' + this.id)
213
241
  this.$socket?.off('msg-input:' + this.id)
214
242
  },
215
243
  methods: {
216
- createComponent (field) {
244
+ createComponent(field) {
217
245
  const customForm = field.customForm ? JSON.parse(field.customForm) : {}
218
246
  const hint = customForm.hint
219
247
  const placeholder = customForm.placeholder
220
248
  const validation = customForm.validation
221
249
  const name = field.id
222
250
  const customProperties = customForm.customProperties ?? []
223
- const isReadOnly = (
224
- this.props.readonly || this.formIsFinished || customProperties.find(entry => ['readOnly', 'readonly'].includes(entry.name) && entry.value === 'true'))
225
- ? 'true'
226
- : undefined
251
+ const isReadOnly =
252
+ this.props.readonly ||
253
+ this.formIsFinished ||
254
+ customProperties.find((entry) => ['readOnly', 'readonly'].includes(entry.name) && entry.value === 'true')
255
+ ? 'true'
256
+ : undefined
227
257
  switch (field.type) {
228
- case 'long':
229
- return {
230
- type: 'FormKit',
231
- props: {
232
- type: 'number',
233
- id: field.id,
234
- name,
235
- label: field.label,
236
- required: field.required,
237
- value: this.formData[field.id],
238
- number: 'integer',
239
- help: hint,
240
- wrapperClass: '$remove:formkit-wrapper',
241
- labelClass: 'ui-dynamic-form-input-label',
242
- inputClass: `input-${this.theme}`,
243
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
244
- readonly: isReadOnly,
245
- validation,
246
- validationVisibility: 'live'
258
+ case 'long':
259
+ return {
260
+ type: 'FormKit',
261
+ props: {
262
+ type: 'number',
263
+ id: field.id,
264
+ name,
265
+ label: field.label,
266
+ required: field.required,
267
+ value: this.formData[field.id],
268
+ number: 'integer',
269
+ help: hint,
270
+ wrapperClass: '$remove:formkit-wrapper',
271
+ labelClass: 'ui-dynamic-form-input-label',
272
+ inputClass: `input-${this.theme}`,
273
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
274
+ readonly: isReadOnly,
275
+ validation,
276
+ validationVisibility: 'live'
277
+ }
247
278
  }
248
- }
249
- case 'number':
250
- const step = field.customForm ? JSON.parse(field.customForm).step : undefined
251
- return {
252
- type: 'FormKit',
253
- props: {
254
- type: 'number',
255
- id: field.id,
256
- name,
257
- label: field.label,
258
- required: field.required,
259
- value: this.formData[field.id],
260
- step,
261
- help: hint,
262
- wrapperClass: '$remove:formkit-wrapper',
263
- labelClass: 'ui-dynamic-form-input-label',
264
- inputClass: `input-${this.theme}`,
265
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
266
- readonly: isReadOnly,
267
- validation,
268
- validationVisibility: 'live'
279
+ case 'number':
280
+ const step = field.customForm ? JSON.parse(field.customForm).step : undefined
281
+ return {
282
+ type: 'FormKit',
283
+ props: {
284
+ type: 'number',
285
+ id: field.id,
286
+ name,
287
+ label: field.label,
288
+ required: field.required,
289
+ value: this.formData[field.id],
290
+ step,
291
+ help: hint,
292
+ wrapperClass: '$remove:formkit-wrapper',
293
+ labelClass: 'ui-dynamic-form-input-label',
294
+ inputClass: `input-${this.theme}`,
295
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
296
+ readonly: isReadOnly,
297
+ validation,
298
+ validationVisibility: 'live'
299
+ }
269
300
  }
270
- }
271
- case 'date':
272
- return {
273
- type: 'FormKit',
274
- props: {
275
- type: 'date',
276
- id: field.id,
277
- name,
278
- label: field.label,
279
- required: field.required,
280
- value: this.formData[field.id],
281
- help: hint,
282
- wrapperClass: '$remove:formkit-wrapper',
283
- labelClass: 'ui-dynamic-form-input-label',
284
- inputClass: `input-${this.theme}`,
285
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
286
- readonly: isReadOnly,
287
- validation,
288
- validationVisibility: 'live'
301
+ case 'date':
302
+ return {
303
+ type: 'FormKit',
304
+ props: {
305
+ type: 'date',
306
+ id: field.id,
307
+ name,
308
+ label: field.label,
309
+ required: field.required,
310
+ value: this.formData[field.id],
311
+ help: hint,
312
+ wrapperClass: '$remove:formkit-wrapper',
313
+ labelClass: 'ui-dynamic-form-input-label',
314
+ inputClass: `input-${this.theme}`,
315
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
316
+ readonly: isReadOnly,
317
+ validation,
318
+ validationVisibility: 'live'
319
+ }
289
320
  }
290
- }
291
- case 'enum':
292
- const enums = field.enumValues.map((obj) => {
293
- return { value: obj.id, label: obj.name }
294
- })
295
- return {
296
- type: 'FormKit',
297
- props: {
298
- type: 'select', // JSON.parse(field.customForm).displayAs
299
- id: field.id,
300
- name,
301
- label: field.label,
302
- required: field.required,
303
- value: this.formData[field.id],
304
- options: enums,
305
- help: hint,
306
- wrapperClass: '$remove:formkit-wrapper',
307
- labelClass: 'ui-dynamic-form-input-label',
308
- inputClass: `input-${this.theme}`,
309
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
310
- readonly: isReadOnly,
311
- disabled: isReadOnly,
312
- validation,
313
- validationVisibility: 'live'
321
+ case 'enum':
322
+ const enums = field.enumValues.map((obj) => {
323
+ return { value: obj.id, label: obj.name }
324
+ })
325
+ return {
326
+ type: 'FormKit',
327
+ props: {
328
+ type: 'select', // JSON.parse(field.customForm).displayAs
329
+ id: field.id,
330
+ name,
331
+ label: field.label,
332
+ required: field.required,
333
+ value: this.formData[field.id],
334
+ options: enums,
335
+ help: hint,
336
+ wrapperClass: '$remove:formkit-wrapper',
337
+ labelClass: 'ui-dynamic-form-input-label',
338
+ inputClass: `input-${this.theme}`,
339
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
340
+ readonly: isReadOnly,
341
+ disabled: isReadOnly,
342
+ validation,
343
+ validationVisibility: 'live'
344
+ }
314
345
  }
315
- }
316
- case 'select':
317
- const selections = JSON.parse(field.customForm).entries.map((obj) => {
318
- return { value: obj.key, label: obj.value }
319
- })
320
- return {
321
- type: 'FormKit',
322
- props: {
323
- type: 'select', // JSON.parse(field.customForm).displayAs
324
- id: field.id,
325
- name,
326
- label: field.label,
327
- required: field.required,
328
- value: this.formData[field.id],
329
- options: selections,
330
- placeholder,
331
- help: hint,
332
- wrapperClass: '$remove:formkit-wrapper',
333
- labelClass: 'ui-dynamic-form-input-label',
334
- inputClass: `input-${this.theme}`,
335
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
336
- readonly: isReadOnly,
337
- disabled: isReadOnly,
338
- validation,
339
- validationVisibility: 'live'
346
+ case 'select':
347
+ const selections = JSON.parse(field.customForm).entries.map((obj) => {
348
+ return { value: obj.key, label: obj.value }
349
+ })
350
+ return {
351
+ type: 'FormKit',
352
+ props: {
353
+ type: 'select', // JSON.parse(field.customForm).displayAs
354
+ id: field.id,
355
+ name,
356
+ label: field.label,
357
+ required: field.required,
358
+ value: this.formData[field.id],
359
+ options: selections,
360
+ placeholder,
361
+ help: hint,
362
+ wrapperClass: '$remove:formkit-wrapper',
363
+ labelClass: 'ui-dynamic-form-input-label',
364
+ inputClass: `input-${this.theme}`,
365
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
366
+ readonly: isReadOnly,
367
+ disabled: isReadOnly,
368
+ validation,
369
+ validationVisibility: 'live'
370
+ }
340
371
  }
341
- }
342
- case 'string':
343
- return {
344
- type: 'FormKit',
345
- props: {
346
- type: 'text',
347
- id: field.id,
348
- name,
349
- label: field.label,
350
- required: field.required,
351
- value: this.formData[field.id],
352
- help: hint,
353
- placeholder,
354
- wrapperClass: '$remove:formkit-wrapper',
355
- labelClass: 'ui-dynamic-form-input-label',
356
- inputClass: `input-${this.theme}`,
357
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
358
- readonly: isReadOnly,
359
- validation,
360
- validationVisibility: 'live'
372
+ case 'string':
373
+ return {
374
+ type: 'FormKit',
375
+ props: {
376
+ type: 'text',
377
+ id: field.id,
378
+ name,
379
+ label: field.label,
380
+ required: field.required,
381
+ value: this.formData[field.id],
382
+ help: hint,
383
+ placeholder,
384
+ wrapperClass: '$remove:formkit-wrapper',
385
+ labelClass: 'ui-dynamic-form-input-label',
386
+ inputClass: `input-${this.theme}`,
387
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
388
+ readonly: isReadOnly,
389
+ validation,
390
+ validationVisibility: 'live'
391
+ }
361
392
  }
362
- }
363
- case 'confirm':
364
- return {
365
- type: 'h3',
366
- innerText: field.label
367
- }
368
- case 'boolean':
369
- return {
370
- type: 'FormKit',
371
- props: {
372
- type: 'checkbox',
373
- id: field.id,
374
- name,
375
- label: field.label,
376
- required: field.required,
377
- value: this.formData[field.id],
378
- help: hint,
379
- labelClass: 'ui-dynamic-form-input-label',
380
- inputClass: `input-${this.theme}`,
381
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
382
- readonly: isReadOnly,
383
- disabled: isReadOnly,
384
- validation,
385
- validationVisibility: 'live'
393
+ case 'confirm':
394
+ return {
395
+ type: 'h3',
396
+ innerText: field.label
386
397
  }
387
- }
388
- case 'file':
389
- return {
390
- type: 'FormKit',
391
- props: {
392
- type: 'file',
393
- id: field.id,
394
- name,
395
- label: field.label,
396
- required: field.required,
397
- value: this.formData[field.id],
398
- help: hint,
399
- innerClass: 'reset-background',
400
- wrapperClass: '$remove:formkit-wrapper',
401
- labelClass: 'ui-dynamic-form-input-label',
402
- inputClass: `input-${this.theme}`,
403
- // innerClass: ui-dynamic-form-input-outlines `${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
404
- readonly: isReadOnly,
405
- disabled: isReadOnly,
406
- validation,
407
- validationVisibility: 'live'
398
+ case 'boolean':
399
+ return {
400
+ type: 'FormKit',
401
+ props: {
402
+ type: 'checkbox',
403
+ id: field.id,
404
+ name,
405
+ label: field.label,
406
+ required: field.required,
407
+ value: this.formData[field.id],
408
+ help: hint,
409
+ labelClass: 'ui-dynamic-form-input-label',
410
+ inputClass: `input-${this.theme}`,
411
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
412
+ readonly: isReadOnly,
413
+ disabled: isReadOnly,
414
+ validation,
415
+ validationVisibility: 'live'
416
+ }
408
417
  }
409
- }
410
- case 'checkbox':
411
- const options = JSON.parse(field.customForm).entries.map((obj) => {
412
- return { value: obj.key, label: obj.value }
413
- })
414
- return {
415
- type: 'FormKit',
416
- props: {
417
- type: 'checkbox',
418
- id: field.id,
419
- name,
420
- label: field.label,
421
- required: field.required,
422
- value: this.formData[field.id],
423
- options,
424
- help: hint,
425
- fieldsetClass: 'custom-fieldset',
426
- labelClass: 'ui-dynamic-form-input-label',
427
- inputClass: `input-${this.theme}`,
428
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
429
- readonly: isReadOnly,
430
- disabled: isReadOnly,
431
- validation,
432
- validationVisibility: 'live'
418
+ case 'file':
419
+ const multiple = field.customForm ? JSON.parse(field.customForm).multiple === 'true' : false
420
+ return {
421
+ type: 'FormKit',
422
+ props: {
423
+ type: 'file',
424
+ id: field.id,
425
+ name,
426
+ label: field.label,
427
+ required: field.required,
428
+ value: this.formData[field.id],
429
+ help: hint,
430
+ innerClass: 'reset-background',
431
+ wrapperClass: '$remove:formkit-wrapper',
432
+ labelClass: 'ui-dynamic-form-input-label',
433
+ inputClass: `input-${this.theme}`,
434
+ // innerClass: ui-dynamic-form-input-outlines `${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
435
+ readonly: isReadOnly,
436
+ disabled: isReadOnly,
437
+ multiple,
438
+ validation,
439
+ validationVisibility: 'live'
440
+ }
433
441
  }
434
- }
435
- case 'color':
436
- return {
437
- type: 'FormKit',
438
- props: {
439
- type: 'color',
440
- id: field.id,
441
- name,
442
- label: field.label,
443
- required: field.required,
444
- value: this.formData[field.id],
445
- help: hint,
446
- readonly: isReadOnly,
447
- disabled: isReadOnly,
448
- validation,
449
- validationVisibility: 'live'
442
+ case 'checkbox':
443
+ const options = JSON.parse(field.customForm).entries.map((obj) => {
444
+ return { value: obj.key, label: obj.value }
445
+ })
446
+ return {
447
+ type: 'FormKit',
448
+ props: {
449
+ type: 'checkbox',
450
+ id: field.id,
451
+ name,
452
+ label: field.label,
453
+ required: field.required,
454
+ value: this.formData[field.id],
455
+ options,
456
+ help: hint,
457
+ fieldsetClass: 'custom-fieldset',
458
+ labelClass: 'ui-dynamic-form-input-label',
459
+ inputClass: `input-${this.theme}`,
460
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
461
+ readonly: isReadOnly,
462
+ disabled: isReadOnly,
463
+ validation,
464
+ validationVisibility: 'live'
465
+ }
450
466
  }
451
- }
452
- case 'datetime-local':
453
- return {
454
- type: 'FormKit',
455
- props: {
456
- type: 'datetime-local',
457
- id: field.id,
458
- name,
459
- label: field.label,
460
- required: field.required,
461
- value: this.formData[field.id],
462
- help: hint,
463
- wrapperClass: '$remove:formkit-wrapper',
464
- labelClass: 'ui-dynamic-form-input-label',
465
- inputClass: `input-${this.theme}`,
466
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
467
- readonly: isReadOnly,
468
- validation,
469
- validationVisibility: 'live'
467
+ case 'color':
468
+ return {
469
+ type: 'FormKit',
470
+ props: {
471
+ type: 'color',
472
+ id: field.id,
473
+ name,
474
+ label: field.label,
475
+ required: field.required,
476
+ value: this.formData[field.id],
477
+ help: hint,
478
+ readonly: isReadOnly,
479
+ disabled: isReadOnly,
480
+ validation,
481
+ validationVisibility: 'live'
482
+ }
470
483
  }
471
- }
472
- case 'email':
473
- return {
474
- type: 'FormKit',
475
- props: {
476
- type: 'email',
477
- id: field.id,
478
- name,
479
- label: field.label,
480
- required: field.required,
481
- value: this.formData[field.id],
482
- help: hint,
483
- placeholder,
484
- wrapperClass: '$remove:formkit-wrapper',
485
- labelClass: 'ui-dynamic-form-input-label',
486
- inputClass: `input-${this.theme}`,
487
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
488
- readonly: isReadOnly,
489
- validation,
490
- validationVisibility: 'live'
484
+ case 'datetime-local':
485
+ return {
486
+ type: 'FormKit',
487
+ props: {
488
+ type: 'datetime-local',
489
+ id: field.id,
490
+ name,
491
+ label: field.label,
492
+ required: field.required,
493
+ value: this.formData[field.id],
494
+ help: hint,
495
+ wrapperClass: '$remove:formkit-wrapper',
496
+ labelClass: 'ui-dynamic-form-input-label',
497
+ inputClass: `input-${this.theme}`,
498
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
499
+ readonly: isReadOnly,
500
+ validation,
501
+ validationVisibility: 'live'
502
+ }
491
503
  }
492
- }
493
- case 'header':
494
- let typeToUse = 'h1'
495
- if (field.customForm && JSON.parse(field.customForm).style === 'heading_2') {
496
- typeToUse = 'h2'
497
- }
498
- if (field.customForm && JSON.parse(field.customForm).style === 'heading_3') {
499
- typeToUse = 'h3'
500
- }
501
- return {
502
- type: typeToUse,
503
- innerText: this.formData[field.id]
504
- }
505
- case 'hidden':
506
- return {
507
- type: 'input',
508
- props: {
509
- type: 'hidden',
510
- value: this.formData[field.id]
504
+ case 'email':
505
+ return {
506
+ type: 'FormKit',
507
+ props: {
508
+ type: 'email',
509
+ id: field.id,
510
+ name,
511
+ label: field.label,
512
+ required: field.required,
513
+ value: this.formData[field.id],
514
+ help: hint,
515
+ placeholder,
516
+ wrapperClass: '$remove:formkit-wrapper',
517
+ labelClass: 'ui-dynamic-form-input-label',
518
+ inputClass: `input-${this.theme}`,
519
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
520
+ readonly: isReadOnly,
521
+ validation,
522
+ validationVisibility: 'live'
523
+ }
511
524
  }
512
- }
513
- case 'month':
514
- return {
515
- type: 'FormKit',
516
- props: {
517
- type: 'month',
518
- id: field.id,
519
- name,
520
- label: field.label,
521
- required: field.required,
522
- value: this.formData[field.id],
523
- help: hint,
524
- wrapperClass: '$remove:formkit-wrapper',
525
- labelClass: 'ui-dynamic-form-input-label',
526
- inputClass: `input-${this.theme}`,
527
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
528
- readonly: isReadOnly,
529
- validation,
530
- validationVisibility: 'live'
525
+ case 'header':
526
+ let typeToUse = 'h1'
527
+ if (field.customForm && JSON.parse(field.customForm).style === 'heading_2') {
528
+ typeToUse = 'h2'
531
529
  }
532
- }
533
- case 'paragraph':
534
- return {
535
- type: 'p',
536
- innerText: this.formData[field.id]
537
- }
538
- case 'password':
539
- return {
540
- type: 'FormKit',
541
- props: {
542
- type: 'password',
543
- id: field.id,
544
- name,
545
- label: field.label,
546
- required: field.required,
547
- value: this.formData[field.id],
548
- help: hint,
549
- placeholder,
550
- wrapperClass: '$remove:formkit-wrapper',
551
- labelClass: 'ui-dynamic-form-input-label',
552
- inputClass: `input-${this.theme}`,
553
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
554
- readonly: isReadOnly,
555
- validation,
556
- validationVisibility: 'live'
530
+ if (field.customForm && JSON.parse(field.customForm).style === 'heading_3') {
531
+ typeToUse = 'h3'
557
532
  }
558
- }
559
- case 'radio':
560
- const radioOptions = JSON.parse(field.customForm).entries.map((obj) => {
561
- return { value: obj.key, label: obj.value }
562
- })
563
- return {
564
- type: 'FormKit',
565
- props: {
566
- type: 'radio',
567
- id: field.id,
568
- name,
569
- label: field.label,
570
- required: field.required,
571
- value: this.formData[field.id],
572
- options: radioOptions,
573
- help: hint,
574
- fieldsetClass: 'custom-fieldset',
575
- labelClass: 'ui-dynamic-form-input-label',
576
- inputClass: `input-${this.theme}`,
577
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
578
- readonly: isReadOnly,
579
- disabled: isReadOnly,
580
- validation,
581
- validationVisibility: 'live'
533
+ return {
534
+ type: typeToUse,
535
+ innerText: this.formData[field.id]
582
536
  }
583
- }
584
- case 'range':
585
- const customForm = JSON.parse(field.customForm)
586
- return {
587
- type: 'v-slider',
588
- props: {
589
- id: field.id,
590
- name,
591
- // label: field.label,
592
- required: field.required,
593
- // value: this.formData[field.id],
594
- // help: hint,
595
- min: customForm.min,
596
- max: customForm.max,
597
- step: customForm.step,
598
- thumbLabel: true,
599
- // wrapperClass: '$remove:formkit-wrapper',
600
- labelClass: 'ui-dynamic-form-input-label',
601
- // inputClass: `input-${this.theme}`,
602
- // innerClass: ui-dynamic-form-input-outlines `${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
603
- readonly: isReadOnly,
604
- disabled: isReadOnly,
605
- validation,
606
- validationVisibility: 'live'
537
+ case 'hidden':
538
+ return {
539
+ type: 'input',
540
+ props: {
541
+ type: 'hidden',
542
+ value: this.formData[field.id]
543
+ }
607
544
  }
608
- }
609
- case 'tel':
610
- return {
611
- type: 'FormKit',
612
- props: {
613
- type: 'tel' /* with pro component mask more good */,
614
- id: field.id,
615
- name,
616
- label: field.label,
617
- required: field.required,
618
- value: this.formData[field.id],
619
- help: hint,
620
- placeholder,
621
- wrapperClass: '$remove:formkit-wrapper',
622
- labelClass: 'ui-dynamic-form-input-label',
623
- inputClass: `input-${this.theme}`,
624
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
625
- readonly: isReadOnly,
626
- validation,
627
- validationVisibility: 'live'
545
+ case 'month':
546
+ return {
547
+ type: 'FormKit',
548
+ props: {
549
+ type: 'month',
550
+ id: field.id,
551
+ name,
552
+ label: field.label,
553
+ required: field.required,
554
+ value: this.formData[field.id],
555
+ help: hint,
556
+ wrapperClass: '$remove:formkit-wrapper',
557
+ labelClass: 'ui-dynamic-form-input-label',
558
+ inputClass: `input-${this.theme}`,
559
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
560
+ readonly: isReadOnly,
561
+ validation,
562
+ validationVisibility: 'live'
563
+ }
628
564
  }
629
- }
630
- case 'textarea':
631
- const rows = field.customForm ? JSON.parse(field.customForm).rows : undefined
632
- return {
633
- type: 'FormKit',
634
- props: {
635
- type: 'textarea' /* with pro component mask more good */,
636
- id: field.id,
637
- name,
638
- label: field.label,
639
- required: field.required,
640
- value: this.formData[field.id],
641
- rows,
642
- help: hint,
643
- placeholder,
644
- wrapperClass: '$remove:formkit-wrapper',
645
- labelClass: 'ui-dynamic-form-input-label',
646
- inputClass: `input-${this.theme}`,
647
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
648
- readonly: isReadOnly,
649
- validation,
650
- validationVisibility: 'live'
565
+ case 'paragraph':
566
+ return {
567
+ type: 'p',
568
+ innerText: this.formData[field.id]
651
569
  }
652
- }
653
- case 'time':
654
- return {
655
- type: 'FormKit',
656
- props: {
657
- type: 'time' /* with pro component mask more good */,
658
- id: field.id,
659
- name,
660
- label: field.label,
661
- required: field.required,
662
- value: this.formData[field.id],
663
- help: hint,
664
- placeholder,
665
- wrapperClass: '$remove:formkit-wrapper',
666
- labelClass: 'ui-dynamic-form-input-label',
667
- inputClass: `input-${this.theme}`,
668
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
669
- readonly: isReadOnly,
670
- validation,
671
- validationVisibility: 'live'
570
+ case 'password':
571
+ return {
572
+ type: 'FormKit',
573
+ props: {
574
+ type: 'password',
575
+ id: field.id,
576
+ name,
577
+ label: field.label,
578
+ required: field.required,
579
+ value: this.formData[field.id],
580
+ help: hint,
581
+ placeholder,
582
+ wrapperClass: '$remove:formkit-wrapper',
583
+ labelClass: 'ui-dynamic-form-input-label',
584
+ inputClass: `input-${this.theme}`,
585
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
586
+ readonly: isReadOnly,
587
+ validation,
588
+ validationVisibility: 'live'
589
+ }
672
590
  }
673
- }
674
- case 'url':
675
- return {
676
- type: 'FormKit',
677
- props: {
678
- type: 'url',
679
- id: field.id,
680
- name,
681
- label: field.label,
682
- required: field.required,
683
- value: this.formData[field.id],
684
- help: hint,
685
- placeholder,
686
- wrapperClass: '$remove:formkit-wrapper',
687
- labelClass: 'ui-dynamic-form-input-label',
688
- inputClass: `input-${this.theme}`,
689
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
690
- readonly: isReadOnly,
691
- validation,
692
- validationVisibility: 'live'
591
+ case 'radio':
592
+ const radioOptions = JSON.parse(field.customForm).entries.map((obj) => {
593
+ return { value: obj.key, label: obj.value }
594
+ })
595
+ return {
596
+ type: 'FormKit',
597
+ props: {
598
+ type: 'radio',
599
+ id: field.id,
600
+ name,
601
+ label: field.label,
602
+ required: field.required,
603
+ value: this.formData[field.id],
604
+ options: radioOptions,
605
+ help: hint,
606
+ fieldsetClass: 'custom-fieldset',
607
+ labelClass: 'ui-dynamic-form-input-label',
608
+ inputClass: `input-${this.theme}`,
609
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
610
+ readonly: isReadOnly,
611
+ disabled: isReadOnly,
612
+ validation,
613
+ validationVisibility: 'live'
614
+ }
693
615
  }
694
- }
695
- case 'week':
696
- return {
697
- type: 'FormKit',
698
- props: {
699
- type: 'week',
700
- id: field.id,
701
- name,
702
- label: field.label,
703
- required: field.required,
704
- value: this.formData[field.id],
705
- help: hint,
706
- placeholder,
707
- wrapperClass: '$remove:formkit-wrapper',
708
- labelClass: 'ui-dynamic-form-input-label',
709
- inputClass: `input-${this.theme}`,
710
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
711
- readonly: isReadOnly,
712
- validation,
713
- validationVisibility: 'live'
616
+ case 'range':
617
+ const customForm = JSON.parse(field.customForm)
618
+ return {
619
+ type: 'v-slider',
620
+ props: {
621
+ id: field.id,
622
+ name,
623
+ // label: field.label,
624
+ required: field.required,
625
+ // value: this.formData[field.id],
626
+ // help: hint,
627
+ min: customForm.min,
628
+ max: customForm.max,
629
+ step: customForm.step,
630
+ thumbLabel: true,
631
+ // wrapperClass: '$remove:formkit-wrapper',
632
+ labelClass: 'ui-dynamic-form-input-label',
633
+ // inputClass: `input-${this.theme}`,
634
+ // innerClass: ui-dynamic-form-input-outlines `${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
635
+ readonly: isReadOnly,
636
+ disabled: isReadOnly,
637
+ validation,
638
+ validationVisibility: 'live'
639
+ }
714
640
  }
715
- }
716
- default:
717
- return {
718
- type: 'FormKit',
719
- props: {
720
- type: field.type,
721
- id: field.id,
722
- name,
723
- label: field.label,
724
- required: field.required,
725
- value: this.formData[field.id],
726
- help: hint,
727
- labelClass: 'ui-dynamic-form-input-label',
728
- inputClass: `input-${this.theme}`,
729
- innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
730
- readonly: isReadOnly,
731
- validation,
732
- validationVisibility: 'live'
641
+ case 'tel':
642
+ return {
643
+ type: 'FormKit',
644
+ props: {
645
+ type: 'tel' /* with pro component mask more good */,
646
+ id: field.id,
647
+ name,
648
+ label: field.label,
649
+ required: field.required,
650
+ value: this.formData[field.id],
651
+ help: hint,
652
+ placeholder,
653
+ wrapperClass: '$remove:formkit-wrapper',
654
+ labelClass: 'ui-dynamic-form-input-label',
655
+ inputClass: `input-${this.theme}`,
656
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
657
+ readonly: isReadOnly,
658
+ validation,
659
+ validationVisibility: 'live'
660
+ }
661
+ }
662
+ case 'textarea':
663
+ const rows = field.customForm ? JSON.parse(field.customForm).rows : undefined
664
+ return {
665
+ type: 'FormKit',
666
+ props: {
667
+ type: 'textarea' /* with pro component mask more good */,
668
+ id: field.id,
669
+ name,
670
+ label: field.label,
671
+ required: field.required,
672
+ value: this.formData[field.id],
673
+ rows,
674
+ help: hint,
675
+ placeholder,
676
+ wrapperClass: '$remove:formkit-wrapper',
677
+ labelClass: 'ui-dynamic-form-input-label',
678
+ inputClass: `input-${this.theme}`,
679
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
680
+ readonly: isReadOnly,
681
+ validation,
682
+ validationVisibility: 'live'
683
+ }
684
+ }
685
+ case 'time':
686
+ return {
687
+ type: 'FormKit',
688
+ props: {
689
+ type: 'time' /* with pro component mask more good */,
690
+ id: field.id,
691
+ name,
692
+ label: field.label,
693
+ required: field.required,
694
+ value: this.formData[field.id],
695
+ help: hint,
696
+ placeholder,
697
+ wrapperClass: '$remove:formkit-wrapper',
698
+ labelClass: 'ui-dynamic-form-input-label',
699
+ inputClass: `input-${this.theme}`,
700
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
701
+ readonly: isReadOnly,
702
+ validation,
703
+ validationVisibility: 'live'
704
+ }
705
+ }
706
+ case 'url':
707
+ return {
708
+ type: 'FormKit',
709
+ props: {
710
+ type: 'url',
711
+ id: field.id,
712
+ name,
713
+ label: field.label,
714
+ required: field.required,
715
+ value: this.formData[field.id],
716
+ help: hint,
717
+ placeholder,
718
+ wrapperClass: '$remove:formkit-wrapper',
719
+ labelClass: 'ui-dynamic-form-input-label',
720
+ inputClass: `input-${this.theme}`,
721
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
722
+ readonly: isReadOnly,
723
+ validation,
724
+ validationVisibility: 'live'
725
+ }
726
+ }
727
+ case 'week':
728
+ return {
729
+ type: 'FormKit',
730
+ props: {
731
+ type: 'week',
732
+ id: field.id,
733
+ name,
734
+ label: field.label,
735
+ required: field.required,
736
+ value: this.formData[field.id],
737
+ help: hint,
738
+ placeholder,
739
+ wrapperClass: '$remove:formkit-wrapper',
740
+ labelClass: 'ui-dynamic-form-input-label',
741
+ inputClass: `input-${this.theme}`,
742
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
743
+ readonly: isReadOnly,
744
+ validation,
745
+ validationVisibility: 'live'
746
+ }
747
+ }
748
+ default:
749
+ return {
750
+ type: 'FormKit',
751
+ props: {
752
+ type: field.type,
753
+ id: field.id,
754
+ name,
755
+ label: field.label,
756
+ required: field.required,
757
+ value: this.formData[field.id],
758
+ help: hint,
759
+ labelClass: 'ui-dynamic-form-input-label',
760
+ inputClass: `input-${this.theme}`,
761
+ innerClass: `ui-dynamic-form-input-outlines ${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
762
+ readonly: isReadOnly,
763
+ validation,
764
+ validationVisibility: 'live'
765
+ }
733
766
  }
734
- }
735
767
  }
736
768
  },
737
- toggleCollapse () {
769
+ toggleCollapse() {
738
770
  this.collapsed = !this.collapsed
739
771
  },
740
- getRowWidthStyling (field, index) {
772
+ getRowWidthStyling(field, index) {
741
773
  let style = ''
742
774
  if (index === 0) {
743
775
  style += 'margin-top: 12px;'
@@ -745,11 +777,11 @@ export default {
745
777
  if (field.type === 'header') {
746
778
  style += 'flex-basis: 100%;'
747
779
  } else {
748
- style += `flex-basis: ${1 / this.props.form_columns * 100}%;`
780
+ style += `flex-basis: ${(1 / this.props.form_columns) * 100}%;`
749
781
  }
750
782
  return style
751
783
  },
752
- fields () {
784
+ fields() {
753
785
  const aFields = this.userTask.userTaskConfig?.formFields ?? []
754
786
  const fieldMap = aFields.map((field) => ({
755
787
  ...field,
@@ -759,15 +791,15 @@ export default {
759
791
  return fieldMap
760
792
  },
761
793
  /*
762
- widget-action just sends a msg to Node-RED, it does not store the msg state server-side
763
- alternatively, you can use widget-change, which will also store the msg in the Node's datastore
764
- */
765
- send (msg, index) {
794
+ widget-action just sends a msg to Node-RED, it does not store the msg state server-side
795
+ alternatively, you can use widget-change, which will also store the msg in the Node's datastore
796
+ */
797
+ send(msg, index) {
766
798
  const msgArr = []
767
799
  msgArr[index] = msg
768
800
  this.$socket.emit('widget-action', this.id, msgArr)
769
801
  },
770
- init (msg) {
802
+ init(msg) {
771
803
  this.msg = msg
772
804
  if (!msg) {
773
805
  return
@@ -786,7 +818,7 @@ export default {
786
818
  }
787
819
 
788
820
  const formFields = this.userTask.userTaskConfig.formFields
789
- const formFieldIds = formFields.map(ff => ff.id)
821
+ const formFieldIds = formFields.map((ff) => ff.id)
790
822
  const initialValues = this.userTask.startToken
791
823
  const finishedFormData = msg.payload.formData
792
824
  this.formIsFinished = !!msg.payload.formData
@@ -802,34 +834,45 @@ export default {
802
834
  const customForm = field.customForm ? JSON.parse(field.customForm) : {}
803
835
  const confirmText = customForm.confirmButtonText ?? 'Confirm'
804
836
  const declineText = customForm.declineButtonText ?? 'Decline'
805
- this.actions = [{
806
- alignment: 'right',
807
- primary: 'false',
808
- label: declineText,
809
- condition: ''
810
- }, {
811
- alignment: 'right',
812
- primary: 'true',
813
- label: confirmText,
814
- condition: ''
815
- }]
837
+ this.actions = [
838
+ {
839
+ alignment: 'right',
840
+ primary: 'false',
841
+ label: declineText,
842
+ condition: ''
843
+ },
844
+ {
845
+ alignment: 'right',
846
+ primary: 'true',
847
+ label: confirmText,
848
+ condition: ''
849
+ }
850
+ ]
816
851
  }
817
852
  })
818
853
  }
819
854
 
820
855
  if (initialValues) {
821
- Object.keys(initialValues).filter(key => formFieldIds.includes(key)).forEach((key) => {
822
- this.formData[key] = initialValues[key]
823
- })
856
+ Object.keys(initialValues)
857
+ .filter((key) => formFieldIds.includes(key))
858
+ .forEach((key) => {
859
+ this.formData[key] = initialValues[key]
860
+ })
824
861
  }
825
862
 
826
863
  if (this.formIsFinished) {
827
- Object.keys(finishedFormData).filter(key => formFieldIds.includes(key)).forEach(key => {
828
- this.formData[key] = finishedFormData[key]
829
- })
864
+ Object.keys(finishedFormData)
865
+ .filter((key) => formFieldIds.includes(key))
866
+ .forEach((key) => {
867
+ this.formData[key] = finishedFormData[key]
868
+ })
830
869
  }
870
+
871
+ nextTick(() => {
872
+ this.focusFirstFormField()
873
+ })
831
874
  },
832
- actionFn (action) {
875
+ actionFn(action) {
833
876
  if (action.label === 'Speichern' || action.label === 'Speichern und nächster') {
834
877
  const formkitInputs = this.$refs.form.$el.querySelectorAll('.formkit-outer')
835
878
  let allComplete = true
@@ -857,14 +900,15 @@ export default {
857
900
  msg.payload = { formData: this.formData, userTask: this.userTask }
858
901
  this.send(
859
902
  msg,
860
- this.actions.findIndex((element) => element.label === action.label) + (this.isConfirmDialog ? this.props.options.length : 0)
903
+ this.actions.findIndex((element) => element.label === action.label) +
904
+ (this.isConfirmDialog ? this.props.options.length : 0)
861
905
  )
862
906
  // TODO: mm - end
863
907
  } else {
864
908
  this.showError(action.errorMessage)
865
909
  }
866
910
  },
867
- checkCondition (condition) {
911
+ checkCondition(condition) {
868
912
  if (condition === '') return true
869
913
  try {
870
914
  // eslint-disable-next-line no-new-func
@@ -876,13 +920,38 @@ export default {
876
920
  return false
877
921
  }
878
922
  },
879
- showError (errMsg) {
923
+ showError(errMsg) {
880
924
  this.errorMsg = errMsg
925
+ },
926
+ focusFirstFormField() {
927
+ if (this.collapsed || !this.hasUserTask) {
928
+ return
929
+ }
930
+
931
+ if (this.firstFormFieldRef) {
932
+ let inputElement = null
933
+
934
+ if (this.firstFormFieldRef.node && this.firstFormFieldRef.node.input instanceof HTMLElement) {
935
+ inputElement = this.firstFormFieldRef.node.input
936
+ } else if (this.firstFormFieldRef.$el instanceof HTMLElement) {
937
+ if (['INPUT', 'TEXTAREA', 'SELECT'].includes(this.firstFormFieldRef.$el.tagName)) {
938
+ inputElement = this.firstFormFieldRef.$el
939
+ } else {
940
+ inputElement = this.firstFormFieldRef.$el.querySelector('input:not([type="hidden"]), textarea, select')
941
+ }
942
+ }
943
+
944
+ if (inputElement) {
945
+ inputElement.focus()
946
+ } else {
947
+ console.warn('Could not find a focusable input element for the first form field.')
948
+ }
949
+ }
881
950
  }
882
951
  }
883
952
  }
884
953
 
885
- function mapItems (type, field) {
954
+ function mapItems(type, field) {
886
955
  if (type === 'enum') {
887
956
  return field.enumValues.map((enumValue) => ({
888
957
  title: enumValue.name,