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