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