@5minds/node-red-dashboard-2-processcube-dynamic-form 2.0.3-feature-7ea503-mcdeghjp → 2.0.3
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.
|
@@ -2,37 +2,39 @@
|
|
|
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
4
|
<UIDynamicFormTitleText
|
|
5
|
-
v-if="props.title_style === 'outside' && hasUserTask"
|
|
6
|
-
:
|
|
7
|
-
:
|
|
5
|
+
v-if="props.title_style === 'outside' && hasUserTask"
|
|
6
|
+
:style="props.title_style"
|
|
7
|
+
:title="props.title_text"
|
|
8
|
+
:customStyles="props.title_custom_text_styling"
|
|
9
|
+
:titleIcon="props.title_icon"
|
|
10
|
+
:collapsible="props.collapsible || (props.collapse_when_finished && formIsFinished)"
|
|
11
|
+
:collapsed="collapsed"
|
|
8
12
|
:toggleCollapse="toggleCollapse"
|
|
9
13
|
/>
|
|
10
14
|
<div className="ui-dynamic-form-wrapper">
|
|
11
|
-
<p v-if="hasUserTask" style="margin-bottom: 0px">
|
|
15
|
+
<p v-if="hasUserTask" style="margin-bottom: 0px;">
|
|
12
16
|
<v-form ref="form" v-model="form" :class="dynamicClass">
|
|
13
17
|
<UIDynamicFormTitleText
|
|
14
|
-
v-if="props.title_style != 'outside'"
|
|
15
|
-
:
|
|
18
|
+
v-if="props.title_style != 'outside'"
|
|
19
|
+
:style="props.title_style"
|
|
20
|
+
:title="props.title_text"
|
|
21
|
+
:customStyles="props.title_custom_text_styling"
|
|
16
22
|
:titleIcon="props.title_icon"
|
|
17
23
|
:collapsible="props.collapsible || (props.collapse_when_finished && formIsFinished)"
|
|
18
|
-
:collapsed="collapsed"
|
|
24
|
+
:collapsed="collapsed"
|
|
25
|
+
:toggleCollapse="toggleCollapse"
|
|
19
26
|
/>
|
|
20
27
|
<Transition name="cardCollapse">
|
|
21
28
|
<div v-if="!collapsed">
|
|
22
|
-
<div className="ui-dynamic-form-formfield-positioner"
|
|
29
|
+
<div className="ui-dynamic-form-formfield-positioner">
|
|
23
30
|
<FormKit id="form" type="group">
|
|
24
|
-
<v-row
|
|
25
|
-
v-for="(field, index) in fields()" :key="field"
|
|
26
|
-
:style="getRowWidthStyling(field, index)"
|
|
27
|
-
>
|
|
31
|
+
<v-row v-for="(field, index) in fields()" :key="field" :style="getRowWidthStyling(field, index)">
|
|
28
32
|
<v-col cols="12">
|
|
29
33
|
<component
|
|
30
34
|
:is="createComponent(field).type"
|
|
31
35
|
v-if="createComponent(field).innerText"
|
|
32
|
-
v-bind="createComponent(field).props"
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
" v-model="formData[field.id]"
|
|
36
|
+
v-bind="createComponent(field).props"
|
|
37
|
+
v-model="formData[field.id]"
|
|
36
38
|
>
|
|
37
39
|
{{ createComponent(field).innerText }}
|
|
38
40
|
</component>
|
|
@@ -40,22 +42,18 @@
|
|
|
40
42
|
<p class="formkit-label">{{ field.label }}</p>
|
|
41
43
|
<component
|
|
42
44
|
:is="createComponent(field).type"
|
|
43
|
-
v-bind="createComponent(field).props"
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
" v-model="field.defaultValue"
|
|
45
|
+
v-bind="createComponent(field).props"
|
|
46
|
+
v-model="field.defaultValue"
|
|
47
47
|
/>
|
|
48
48
|
<p class="formkit-help">
|
|
49
|
-
{{ field.customForm ? JSON.parse(field.customForm).hint : undefined
|
|
50
|
-
}}
|
|
49
|
+
{{ field.customForm ? JSON.parse(field.customForm).hint : undefined }}
|
|
51
50
|
</p>
|
|
52
51
|
</div>
|
|
53
52
|
<component
|
|
54
|
-
:is="createComponent(field).type"
|
|
55
|
-
v-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
" v-model="formData[field.id]"
|
|
53
|
+
:is="createComponent(field).type"
|
|
54
|
+
v-else
|
|
55
|
+
v-bind="createComponent(field).props"
|
|
56
|
+
v-model="formData[field.id]"
|
|
59
57
|
/>
|
|
60
58
|
</v-col>
|
|
61
59
|
</v-row>
|
|
@@ -65,24 +63,17 @@
|
|
|
65
63
|
<v-row v-if="errorMsg.length > 0" style="padding: 12px">
|
|
66
64
|
<v-alert type="error">Error: {{ errorMsg }}</v-alert>
|
|
67
65
|
</v-row>
|
|
68
|
-
<UIDynamicFormFooterAction
|
|
69
|
-
v-if="props.actions_inside_card && actions.length > 0"
|
|
70
|
-
:actions="actions" :actionCallback="actionFn" :formIsFinished="formIsFinished"
|
|
71
|
-
style="padding: 16px; padding-top: 0px"
|
|
72
|
-
/>
|
|
66
|
+
<UIDynamicFormFooterAction v-if="props.actions_inside_card && actions.length > 0" :actions="actions" :actionCallback="actionFn" :formIsFinished="formIsFinished" style="padding: 16px; padding-top: 0px;" />
|
|
73
67
|
</v-row>
|
|
74
68
|
</div>
|
|
75
69
|
</Transition>
|
|
76
70
|
</v-form>
|
|
77
71
|
</p>
|
|
78
72
|
<p v-else>
|
|
79
|
-
<v-alert
|
|
80
|
-
v-if="props.waiting_info.length > 0 || props.waiting_title.length > 0"
|
|
81
|
-
:text="props.waiting_info" :title="props.waiting_title"
|
|
82
|
-
/>
|
|
73
|
+
<v-alert v-if="props.waiting_info.length > 0 || props.waiting_title.length > 0" :text="props.waiting_info" :title="props.waiting_title" />
|
|
83
74
|
</p>
|
|
84
75
|
</div>
|
|
85
|
-
<div v-if="!props.actions_inside_card && actions.length > 0 && hasUserTask" style="padding-top: 32px">
|
|
76
|
+
<div v-if="!props.actions_inside_card && actions.length > 0 && hasUserTask" style="padding-top: 32px;">
|
|
86
77
|
<UIDynamicFormFooterAction :actions="actions" :actionCallback="actionFn" />
|
|
87
78
|
</div>
|
|
88
79
|
</div>
|
|
@@ -90,39 +81,22 @@
|
|
|
90
81
|
|
|
91
82
|
<!-- eslint-disable no-case-declarations -->
|
|
92
83
|
<script>
|
|
93
|
-
import { de } from '@formkit/i18n'
|
|
94
84
|
import { FormKit, defaultConfig, plugin } from '@formkit/vue'
|
|
95
|
-
import { getCurrentInstance, markRaw
|
|
85
|
+
import { getCurrentInstance, markRaw } from 'vue'
|
|
96
86
|
|
|
97
87
|
// eslint-disable-next-line import/no-unresolved
|
|
98
88
|
import '@formkit/themes/genesis'
|
|
99
89
|
import UIDynamicFormFooterAction from './FooterActions.vue'
|
|
100
90
|
import UIDynamicFormTitleText from './TitleText.vue'
|
|
101
91
|
|
|
102
|
-
// eslint-disable-next-line no-unused-vars
|
|
103
|
-
function requiredIf ({ value }, [targetField, expectedValue], node) {
|
|
104
|
-
console.debug(arguments)
|
|
105
|
-
|
|
106
|
-
const actual = node?.root?.value?.[targetField]
|
|
107
|
-
const isEmpty = value === '' || value === null || value === undefined
|
|
108
|
-
|
|
109
|
-
if (actual === expectedValue && isEmpty) {
|
|
110
|
-
return false // oder: `return "Dieses Feld ist erforderlich."`
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return true
|
|
114
|
-
}
|
|
115
|
-
|
|
116
92
|
export default {
|
|
117
93
|
name: 'UIDynamicForm',
|
|
118
94
|
components: {
|
|
119
|
-
FormKit,
|
|
120
|
-
UIDynamicFormFooterAction,
|
|
121
|
-
UIDynamicFormTitleText
|
|
95
|
+
FormKit, UIDynamicFormFooterAction, UIDynamicFormTitleText
|
|
122
96
|
},
|
|
123
97
|
inject: ['$socket'],
|
|
124
98
|
props: {
|
|
125
|
-
|
|
99
|
+
/* do not remove entries from this - Dashboard's Layout Manager's will pass this data to your component */
|
|
126
100
|
id: { type: String, required: true },
|
|
127
101
|
props: { type: Object, default: () => ({}) },
|
|
128
102
|
state: {
|
|
@@ -139,10 +113,20 @@ export default {
|
|
|
139
113
|
|
|
140
114
|
const formkitConfig = defaultConfig({
|
|
141
115
|
theme: 'genesis',
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
116
|
+
rules: {
|
|
117
|
+
requiredIf: ({ value, name }, [targetField, expectedValue], node) => {
|
|
118
|
+
const actual = node?.root?.value?.[targetField]
|
|
119
|
+
if (actual === expectedValue && (!value || value === '')) {
|
|
120
|
+
return `Feld ${name} ist erforderlich, wenn ${targetField} = ${expectedValue}`
|
|
121
|
+
}
|
|
122
|
+
return true
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
messages: {
|
|
126
|
+
de: {
|
|
127
|
+
requiredIf: 'Dieses Feld ist erforderlich.'
|
|
128
|
+
}
|
|
129
|
+
}
|
|
146
130
|
})
|
|
147
131
|
app.use(plugin, formkitConfig)
|
|
148
132
|
},
|
|
@@ -155,8 +139,7 @@ export default {
|
|
|
155
139
|
errorMsg: '',
|
|
156
140
|
formIsFinished: false,
|
|
157
141
|
msg: null,
|
|
158
|
-
collapsed: false
|
|
159
|
-
firstFormFieldRef: null
|
|
142
|
+
collapsed: false
|
|
160
143
|
}
|
|
161
144
|
},
|
|
162
145
|
computed: {
|
|
@@ -170,14 +153,10 @@ export default {
|
|
|
170
153
|
return !!this.userTask
|
|
171
154
|
},
|
|
172
155
|
totalOutputs () {
|
|
173
|
-
return (
|
|
174
|
-
this.props.options.length +
|
|
175
|
-
(this.props.handle_confirmation_dialogs ? 2 : 0) +
|
|
176
|
-
(this.props.trigger_on_change ? 1 : 0)
|
|
177
|
-
)
|
|
156
|
+
return this.props.options.length + (this.props.handle_confirmation_dialogs ? 2 : 0) + (this.props.trigger_on_change ? 1 : 0)
|
|
178
157
|
},
|
|
179
158
|
isConfirmDialog () {
|
|
180
|
-
return this.userTask.userTaskConfig.formFields.some(
|
|
159
|
+
return this.userTask.userTaskConfig.formFields.some(field => field.type === 'confirm')
|
|
181
160
|
}
|
|
182
161
|
},
|
|
183
162
|
watch: {
|
|
@@ -188,20 +167,6 @@ export default {
|
|
|
188
167
|
this.send(res, this.totalOutputs - 1)
|
|
189
168
|
}
|
|
190
169
|
},
|
|
191
|
-
collapsed (newVal) {
|
|
192
|
-
if (!newVal && this.hasUserTask) {
|
|
193
|
-
nextTick(() => {
|
|
194
|
-
this.focusFirstFormField()
|
|
195
|
-
})
|
|
196
|
-
}
|
|
197
|
-
},
|
|
198
|
-
userTask (newVal) {
|
|
199
|
-
if (newVal && !this.collapsed) {
|
|
200
|
-
nextTick(() => {
|
|
201
|
-
this.focusFirstFormField()
|
|
202
|
-
})
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
170
|
deep: true
|
|
206
171
|
}
|
|
207
172
|
},
|
|
@@ -255,12 +220,10 @@ export default {
|
|
|
255
220
|
const validation = customForm.validation
|
|
256
221
|
const name = field.id
|
|
257
222
|
const customProperties = customForm.customProperties ?? []
|
|
258
|
-
const isReadOnly =
|
|
259
|
-
this.props.readonly ||
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
? 'true'
|
|
263
|
-
: undefined
|
|
223
|
+
const isReadOnly = (
|
|
224
|
+
this.props.readonly || this.formIsFinished || customProperties.find(entry => ['readOnly', 'readonly'].includes(entry.name) && entry.value === 'true'))
|
|
225
|
+
? 'true'
|
|
226
|
+
: undefined
|
|
264
227
|
switch (field.type) {
|
|
265
228
|
case 'long':
|
|
266
229
|
return {
|
|
@@ -423,7 +386,6 @@ export default {
|
|
|
423
386
|
}
|
|
424
387
|
}
|
|
425
388
|
case 'file':
|
|
426
|
-
const multiple = field.customForm ? JSON.parse(field.customForm).multiple === 'true' : false
|
|
427
389
|
return {
|
|
428
390
|
type: 'FormKit',
|
|
429
391
|
props: {
|
|
@@ -441,7 +403,6 @@ export default {
|
|
|
441
403
|
// innerClass: ui-dynamic-form-input-outlines `${this.theme === 'dark' ? '$remove:formkit-inner' : ''}`,
|
|
442
404
|
readonly: isReadOnly,
|
|
443
405
|
disabled: isReadOnly,
|
|
444
|
-
multiple,
|
|
445
406
|
validation,
|
|
446
407
|
validationVisibility: 'live'
|
|
447
408
|
}
|
|
@@ -784,7 +745,7 @@ export default {
|
|
|
784
745
|
if (field.type === 'header') {
|
|
785
746
|
style += 'flex-basis: 100%;'
|
|
786
747
|
} else {
|
|
787
|
-
style += `flex-basis: ${
|
|
748
|
+
style += `flex-basis: ${1 / this.props.form_columns * 100}%;`
|
|
788
749
|
}
|
|
789
750
|
return style
|
|
790
751
|
},
|
|
@@ -798,9 +759,9 @@ export default {
|
|
|
798
759
|
return fieldMap
|
|
799
760
|
},
|
|
800
761
|
/*
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
762
|
+
widget-action just sends a msg to Node-RED, it does not store the msg state server-side
|
|
763
|
+
alternatively, you can use widget-change, which will also store the msg in the Node's datastore
|
|
764
|
+
*/
|
|
804
765
|
send (msg, index) {
|
|
805
766
|
const msgArr = []
|
|
806
767
|
msgArr[index] = msg
|
|
@@ -825,7 +786,7 @@ export default {
|
|
|
825
786
|
}
|
|
826
787
|
|
|
827
788
|
const formFields = this.userTask.userTaskConfig.formFields
|
|
828
|
-
const formFieldIds = formFields.map(
|
|
789
|
+
const formFieldIds = formFields.map(ff => ff.id)
|
|
829
790
|
const initialValues = this.userTask.startToken
|
|
830
791
|
const finishedFormData = msg.payload.formData
|
|
831
792
|
this.formIsFinished = !!msg.payload.formData
|
|
@@ -841,43 +802,32 @@ export default {
|
|
|
841
802
|
const customForm = field.customForm ? JSON.parse(field.customForm) : {}
|
|
842
803
|
const confirmText = customForm.confirmButtonText ?? 'Confirm'
|
|
843
804
|
const declineText = customForm.declineButtonText ?? 'Decline'
|
|
844
|
-
this.actions = [
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
condition: ''
|
|
856
|
-
}
|
|
857
|
-
]
|
|
805
|
+
this.actions = [{
|
|
806
|
+
alignment: 'right',
|
|
807
|
+
primary: 'false',
|
|
808
|
+
label: declineText,
|
|
809
|
+
condition: ''
|
|
810
|
+
}, {
|
|
811
|
+
alignment: 'right',
|
|
812
|
+
primary: 'true',
|
|
813
|
+
label: confirmText,
|
|
814
|
+
condition: ''
|
|
815
|
+
}]
|
|
858
816
|
}
|
|
859
817
|
})
|
|
860
818
|
}
|
|
861
819
|
|
|
862
820
|
if (initialValues) {
|
|
863
|
-
Object.keys(initialValues)
|
|
864
|
-
.
|
|
865
|
-
|
|
866
|
-
this.formData[key] = initialValues[key]
|
|
867
|
-
})
|
|
821
|
+
Object.keys(initialValues).filter(key => formFieldIds.includes(key)).forEach((key) => {
|
|
822
|
+
this.formData[key] = initialValues[key]
|
|
823
|
+
})
|
|
868
824
|
}
|
|
869
825
|
|
|
870
826
|
if (this.formIsFinished) {
|
|
871
|
-
Object.keys(finishedFormData)
|
|
872
|
-
.
|
|
873
|
-
|
|
874
|
-
this.formData[key] = finishedFormData[key]
|
|
875
|
-
})
|
|
827
|
+
Object.keys(finishedFormData).filter(key => formFieldIds.includes(key)).forEach(key => {
|
|
828
|
+
this.formData[key] = finishedFormData[key]
|
|
829
|
+
})
|
|
876
830
|
}
|
|
877
|
-
|
|
878
|
-
nextTick(() => {
|
|
879
|
-
this.focusFirstFormField()
|
|
880
|
-
})
|
|
881
831
|
},
|
|
882
832
|
actionFn (action) {
|
|
883
833
|
if (action.label === 'Speichern' || action.label === 'Speichern und nächster') {
|
|
@@ -907,8 +857,7 @@ export default {
|
|
|
907
857
|
msg.payload = { formData: this.formData, userTask: this.userTask }
|
|
908
858
|
this.send(
|
|
909
859
|
msg,
|
|
910
|
-
this.actions.findIndex((element) => element.label === action.label) +
|
|
911
|
-
(this.isConfirmDialog ? this.props.options.length : 0)
|
|
860
|
+
this.actions.findIndex((element) => element.label === action.label) + (this.isConfirmDialog ? this.props.options.length : 0)
|
|
912
861
|
)
|
|
913
862
|
// TODO: mm - end
|
|
914
863
|
} else {
|
|
@@ -929,31 +878,6 @@ export default {
|
|
|
929
878
|
},
|
|
930
879
|
showError (errMsg) {
|
|
931
880
|
this.errorMsg = errMsg
|
|
932
|
-
},
|
|
933
|
-
focusFirstFormField () {
|
|
934
|
-
if (this.collapsed || !this.hasUserTask) {
|
|
935
|
-
return
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
if (this.firstFormFieldRef) {
|
|
939
|
-
let inputElement = null
|
|
940
|
-
|
|
941
|
-
if (this.firstFormFieldRef.node && this.firstFormFieldRef.node.input instanceof HTMLElement) {
|
|
942
|
-
inputElement = this.firstFormFieldRef.node.input
|
|
943
|
-
} else if (this.firstFormFieldRef.$el instanceof HTMLElement) {
|
|
944
|
-
if (['INPUT', 'TEXTAREA', 'SELECT'].includes(this.firstFormFieldRef.$el.tagName)) {
|
|
945
|
-
inputElement = this.firstFormFieldRef.$el
|
|
946
|
-
} else {
|
|
947
|
-
inputElement = this.firstFormFieldRef.$el.querySelector('input:not([type="hidden"]), textarea, select')
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
if (inputElement) {
|
|
952
|
-
inputElement.focus()
|
|
953
|
-
} else {
|
|
954
|
-
console.warn('Could not find a focusable input element for the first form field.')
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
881
|
}
|
|
958
882
|
}
|
|
959
883
|
}
|