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