@5minds/node-red-dashboard-2-processcube-dynamic-form 1.0.8 → 1.0.9-fix-getting-data-in-form-82dd65-lxufdix0

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.
@@ -6,10 +6,15 @@
6
6
  name: { value: "" },
7
7
  group: { type: 'ui-group', required: true },
8
8
  order: { value: 0 },
9
+ options: {
10
+ value: [{ label: ''}],
11
+ validate: function (v) {
12
+ const unique = new Set(v.map(function (o) { return o.label }));
13
+ return v.length === unique.size;
14
+ }
15
+ },
9
16
  waiting_title: { value: "Waiting for Warten auf den Usertask..." },
10
17
  waiting_info: { value: "Der Usertask wird automatisch angezeigt, wenn ein entsprechender Task vorhanden ist." },
11
- submit_title: { value: "Submit your task" },
12
- cancel_title: { value: "Cancel your task" },
13
18
  width: {
14
19
  value: 0,
15
20
  validate: function (v) {
@@ -21,10 +26,14 @@
21
26
  return valid
22
27
  }
23
28
  },
24
- height: { value: 0 }
29
+ height: { value: 0 },
30
+ outputs: { value: 1}
25
31
  },
26
32
  inputs: 1,
27
33
  outputs: 1,
34
+ outputLabels: function(index) {
35
+ return this.options[index].label
36
+ },
28
37
  icon: "file.svg",
29
38
  label: function() {
30
39
  return this.name || "dynamic-form";
@@ -35,7 +44,81 @@
35
44
  height: '#node-input-height',
36
45
  group: '#node-input-group'
37
46
  });
38
- }
47
+
48
+ function generateOption(i, option) {
49
+ const container = $('<li/>', {
50
+ style: 'background: var(--red-ui-secondary-background, #fff); margin:0; padding:8px 0px 0px;'
51
+ });
52
+
53
+ // Create input fields for value and label
54
+ const row = $('<div/>').appendTo(container);
55
+ $('<input/>', {
56
+ class: 'node-input-option-label',
57
+ type: 'text',
58
+ style: 'margin-left:7px; width:calc(100% - 48px);',
59
+ placeholder: 'Label',
60
+ value: option.label
61
+ }).appendTo(row).typedInput({
62
+ type: 'str', types: ['str']
63
+ });
64
+
65
+ // $('<input/>', {
66
+ // class: 'node-input-option-condition',
67
+ // type: 'text',
68
+ // style: 'margin-left:7px; width:calc(50% - 32px);',
69
+ // placeholder: 'Condition',
70
+ // value: option.condition
71
+ // }).appendTo(row).typedInput({
72
+ // type: 'str', types: ['str']
73
+ // });
74
+
75
+ // Create delete button for the option
76
+ const finalSpan = $('<span/>', { style: 'float:right; margin-right:8px;' }).appendTo(row);
77
+ const deleteButton = $('<a/>', { href: '#', class: 'editor-button editor-button-small', style: 'margin-top:7px; margin-left:5px;' }).appendTo(finalSpan);
78
+ $('<i/>', { class: 'fa fa-remove' }).appendTo(deleteButton);
79
+
80
+ deleteButton.click(function () {
81
+ container.css({ background: 'var(--red-ui-secondary-background-inactive, #fee)' });
82
+ container.fadeOut(300, function () {
83
+ $(this).remove();
84
+ });
85
+ });
86
+
87
+ $('#node-input-option-container').append(container);
88
+ }
89
+
90
+ $('#node-input-add-option').click(function () {
91
+ generateOption($('#node-input-option-container').children().length + 1, {});
92
+ $('#node-input-option-container-div').scrollTop($('#node-input-option-container-div').get(0).scrollHeight);
93
+ });
94
+
95
+ for (let i = 0; i < this.options.length; i++) {
96
+ const option = this.options[i];
97
+ generateOption(i + 1, option);
98
+ }
99
+
100
+ $('#node-input-option-container').sortable({
101
+ axis: 'y',
102
+ handle: '.node-input-option-handle',
103
+ cursor: 'move'
104
+ });
105
+ },
106
+ oneditsave: function () {
107
+ const options = $('#node-input-option-container').children();
108
+ const node = this;
109
+ node.options = [];
110
+ options.each(function (i) {
111
+ const option = $(this);
112
+ const o = {
113
+ label: option.find('.node-input-option-label').val(),
114
+ // condition: option.find('.node-input-option-condition').val()
115
+ };
116
+
117
+ node.options.push(o);
118
+ });
119
+
120
+ this.outputs = node.options.length || 1
121
+ },
39
122
  });
40
123
  </script>
41
124
 
@@ -54,6 +137,26 @@
54
137
  <input type="hidden" id="node-input-height">
55
138
  <button class="editor-button" id="node-input-size"></button>
56
139
  </div>
140
+
141
+ <div class="form-row form-row-flex node-input-option-container-row" style="margin-bottom: 0px;width: 100%">
142
+ <label for="node-input-width" style="vertical-align:top"><i class="fa fa-list-alt"></i> Actions</label>
143
+ <div id="node-input-option-container-div" style="box-sizing:border-box; border-radius:5px; height:257px; padding:5px; border:1px solid var(--red-ui-form-input-border-color, #ccc); overflow-y:scroll; display:inline-block; width: 70%;">
144
+ <span id="valWarning" style="color: var(--red-ui-text-color-error, #910000)"><b>All Values must be unique.</b></span>
145
+ <ol id="node-input-option-container" style="list-style-type:none; margin:0;"></ol>
146
+ </div>
147
+ <a
148
+ data-html="true"
149
+ title="Dynamic Property: Send 'msg.options' in order to override this property."
150
+ class="red-ui-button ui-node-popover-title"
151
+ style="margin-left: 4px; cursor: help; font-size: 0.625rem; border-radius: 50%; width: 24px; height: 24px; display: inline-flex; justify-content: center; align-items: center;">
152
+ <i style="font-family: ui-serif;">fx</i>
153
+ </a>
154
+ </div>
155
+ <!-- Add Option Button -->
156
+ <div class="form-row">
157
+ <a href="#" class="editor-button editor-button-small" id="node-input-add-option" style="margin-top:4px; margin-left:103px;"><i class="fa fa-plus"></i> <span>action</span></a>
158
+ </div>
159
+
57
160
  <div class="form-row">
58
161
  <label for="node-input-waiting_title"><i class="fa fa-hand"></i>Title for waiting text.</label>
59
162
  <input type="text" id="node-input-waiting_title">
@@ -62,12 +165,4 @@
62
165
  <label for="node-input-waiting_info"><i class="fa fa-hand"></i>Text for waiting text.</label>
63
166
  <input type="text" id="node-input-waiting_info">
64
167
  </div>
65
- <div class="form-row">
66
- <label for="node-input-submit_title"><i class="fa fa-hand"></i>Text for submit button</label>
67
- <input type="text" id="node-input-submit_title">
68
- </div>
69
- <div class="form-row">
70
- <label for="node-input-cancel_title"><i class="fa fa-hand"></i>Text for cancel button</label>
71
- <input type="text" id="node-input-cancel_title">
72
- </div>
73
- </script>
168
+ </script>
@@ -37,5 +37,12 @@ module.exports = function (RED) {
37
37
  }
38
38
  }
39
39
 
40
- RED.nodes.registerType('dynamic-form', DynamicFormNode)
40
+ RED.nodes.registerType('dynamic-form', DynamicFormNode, {
41
+ defaults: {
42
+ outputs: { value: 1 }
43
+ },
44
+ outputs: function(config) {
45
+ return config.outputs || 1;
46
+ }
47
+ })
41
48
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@5minds/node-red-dashboard-2-processcube-dynamic-form",
3
- "version": "1.0.8",
3
+ "version": "1.0.9-fix-getting-data-in-form-82dd65-lxufdix0",
4
4
  "description": "The ui component for the ProcessCube dynamic-form",
5
5
  "keywords": [
6
6
  "processcube",
@@ -1,3 +1,2 @@
1
- (function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode(".dynamic-form-wrapper[data-v-19b58e9c]{padding:10px;margin:10px;border:1px solid black}.dynamic-form-class[data-v-19b58e9c]{color:green;font-weight:700}h1[data-v-19b58e9c]{margin-bottom:10px}h2[data-v-19b58e9c]{margin-top:1.5rem;margin-bottom:.75rem}h3[data-v-19b58e9c]{margin-top:1rem}p[data-v-19b58e9c]{margin-bottom:5px}ul li[data-v-19b58e9c]{list-style-type:circle;list-style-position:inside;margin-left:15px}pre[data-v-19b58e9c]{padding:12px;margin:12px;background-color:#eee}code[data-v-19b58e9c]{font-size:.825rem;color:#ae0000}")),document.head.appendChild(e)}}catch(a){console.error("vite-plugin-css-injected-by-js",a)}})();
2
- (function(i,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue"),require("vuex")):typeof define=="function"&&define.amd?define(["exports","vue","vuex"],e):(i=typeof globalThis<"u"?globalThis:i||self,e(i["dynamic-form"]={},i.Vue,i.vuex))})(this,function(i,e,u){"use strict";const f=(t,o)=>{const s=t.__vccOpts||t;for(const[a,c]of o)s[a]=c;return s},p={name:"DynamicForm",inject:["$socket"],props:{id:{type:String,required:!0},props:{type:Object,default:()=>({})},state:{type:Object,default:()=>({enabled:!1,visible:!1})}},setup(t){console.info("DynamicForm setup with:",t),console.debug("Vue function loaded correctly",e.markRaw)},data(){return{form:{},formData:{}}},computed:{...u.mapState("data",["messages"]),waiting_title(){return this.props.waiting_title||"Warten auf den Usertask..."},waiting_info(){return this.props.waiting_info||"Der Usertask wird automatisch angezeigt, wenn ein entsprechender Task vorhanden ist."},submit_title(){return this.props.submit_title||"Submit your task"},cancel_title(){return this.props.cancel_title||"Cancel your task"}},mounted(){this.$socket.on("widget-load:"+this.id,t=>{this.$store.commit("data/bind",{widgetId:this.id,msg:t})}),this.$socket.on("msg-input:"+this.id,t=>{if(t.payload.formFields){const o=t.payload.formFields.reduce((s,a)=>(console.info("field:",a),console.info("field.id:",a.id),console.info("field.value:",a.value),console.info("field.defaultValue:",a.defaultValue),s[a.id]=a.value||a.defaultValue||"",s),{});console.info("formData: ",o),console.info("this.formData (1):",this.formData),this.formData=o,console.info("this.formData (2):",this.formData)}this.$store.commit("data/bind",{widgetId:this.id,msg:t})}),this.$socket.emit("widget-load",this.id)},unmounted(){var t,o;(t=this.$socket)==null||t.off("widget-load"+this.id),(o=this.$socket)==null||o.off("msg-input:"+this.id)},methods:{hasUserTask(){return this.messages&&this.messages[this.id]&&this.messages[this.id].payload.userTask},userTask(){return this.hasUserTask()?this.messages[this.id].payload.userTask:{}},fields(){return(this.hasUserTask()?this.userTask().userTaskConfig.formFields:[]).map(s=>({...s,component:k(s.type),items:h(s.type,s)}))},hasFields(){return this.hasUserTask&&this.userTask.userTaskConfig.formFields.length>0},send(t){this.$socket.emit("widget-action",this.id,t)},submit(){this.send({payload:{formData:this.formData,userTask:this.userTask(),action:"submit"}})},cancel(){this.send({payload:{formData:this.formData,userTask:this.userTask(),action:"cancel"}})}}};function h(t,o){return t==="enum"?o.enumValues.map(s=>({title:s.name,value:s.id})):null}function k(t){switch(t){case"string":return"v-text-field";case"long":case"date":return"v-text-field";case"enum":return"v-select";case"boolean":return"v-checkbox";case"text":return"v-text-field";case"select":return"v-select";case"checkbox":return"v-checkbox";case"radio":return"v-radio";case"switch":return"v-switch";case"slider":return"v-slider";case"time":return"v-time-picker";case"datetime":return"v-datetime-picker";case"color":return"v-color-picker";case"file":return"v-file-input";case"textarea":return"v-textarea";case"password":return"v-text-field";case"number":return"v-text-field";case"email":return"v-text-field";case"tel":return"v-text-field";case"url":return"v-text-field";default:return"v-text-field"}}const _={className:"dynamic-form-wrapper"},y={key:0},b={key:1};function x(t,o,s,a,c,n){const l=e.resolveComponent("v-col"),d=e.resolveComponent("v-row"),m=e.resolveComponent("v-btn"),g=e.resolveComponent("v-form"),V=e.resolveComponent("v-alert");return e.openBlock(),e.createElementBlock(e.Fragment,null,[e.createCommentVNode(" Component must be wrapped in a block so props such as className and style can be passed in from parent "),e.createElementVNode("div",_,[n.hasFields?(e.openBlock(),e.createElementBlock("p",y,[e.createVNode(g,{ref:"form",modelValue:c.form,"onUpdate:modelValue":o[0]||(o[0]=r=>c.form=r)},{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(n.fields(),(r,C)=>(e.openBlock(),e.createBlock(d,{key:C},{default:e.withCtx(()=>[e.createVNode(l,{cols:"12"},{default:e.withCtx(()=>[(e.openBlock(),e.createBlock(e.resolveDynamicComponent(r.component),{id:r.id,modelValue:c.formData[r.id],"onUpdate:modelValue":D=>c.formData[r.id]=D,required:r.required,items:r.items,label:r.label},null,8,["id","modelValue","onUpdate:modelValue","required","items","label"]))]),_:2},1024)]),_:2},1024))),128)),e.createVNode(d,null,{default:e.withCtx(()=>[e.createVNode(l,{cols:"6"},{default:e.withCtx(()=>[e.createVNode(m,{variant:"outlined",block:"",onClick:n.cancel},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(n.cancel_title),1)]),_:1},8,["onClick"])]),_:1}),e.createVNode(l,{cols:"6"},{default:e.withCtx(()=>[e.createVNode(m,{block:"",onClick:n.submit},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(n.submit_title),1)]),_:1},8,["onClick"])]),_:1})]),_:1})]),_:1},8,["modelValue"])])):(e.openBlock(),e.createElementBlock("p",b,[e.createVNode(V,{text:n.waiting_info,title:n.waiting_title},null,8,["text","title"])]))])],2112)}const w=f(p,[["render",x],["__scopeId","data-v-19b58e9c"],["__file","/Users/moellenbeck/projects/5minds/reps/node-red-dashboard-2-processcube-dynamic-form/ui/components/DynamicForm.vue"]]);i.DynamicForm=w,Object.defineProperty(i,Symbol.toStringTag,{value:"Module"})});
3
- //# sourceMappingURL=dynamic-form.umd.js.map
1
+ (function(){"use strict";try{if(typeof document<"u"){var a=document.createElement("style");a.appendChild(document.createTextNode(".dynamic-form-wrapper[data-v-5ca4b3d4]{padding:10px;margin:10px;border:1px solid black}.dynamic-form-class[data-v-5ca4b3d4]{color:green;font-weight:700}h1[data-v-5ca4b3d4]{margin-bottom:10px}h2[data-v-5ca4b3d4]{margin-top:1.5rem;margin-bottom:.75rem}h3[data-v-5ca4b3d4]{margin-top:1rem}p[data-v-5ca4b3d4]{margin-bottom:5px}ul li[data-v-5ca4b3d4]{list-style-type:circle;list-style-position:inside;margin-left:15px}pre[data-v-5ca4b3d4]{padding:12px;margin:12px;background-color:#eee}code[data-v-5ca4b3d4]{font-size:.825rem;color:#ae0000}")),document.head.appendChild(a)}}catch(e){console.error("vite-plugin-css-injected-by-js",e)}})();
2
+ (function(n,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue"),require("vuex")):typeof define=="function"&&define.amd?define(["exports","vue","vuex"],e):(n=typeof globalThis<"u"?globalThis:n||self,e(n["dynamic-form"]={},n.Vue,n.vuex))})(this,function(n,e,p){"use strict";const u=(t,o)=>{const s=t.__vccOpts||t;for(const[l,r]of o)s[l]=r;return s},f={name:"DynamicForm",inject:["$socket"],props:{id:{type:String,required:!0},props:{type:Object,default:()=>({})},state:{type:Object,default:()=>({enabled:!1,visible:!1})}},setup(t){console.info("DynamicForm setup with:",t),console.debug("Vue function loaded correctly",e.markRaw)},data(){return{actions:[],form:{},formData:{}}},computed:{...p.mapState("data",["messages"]),waiting_title(){return this.props.waiting_title||"Warten auf den Usertask..."},waiting_info(){return this.props.waiting_info||"Der Usertask wird automatisch angezeigt, wenn ein entsprechender Task vorhanden ist."}},mounted(){this.$socket.on("widget-load:"+this.id,t=>{this.init(),this.$store.commit("data/bind",{widgetId:this.id,msg:t})}),this.$socket.on("msg-input:"+this.id,t=>{this.init(),t.payload&&t.payload.userTask&&t.payload.userTask.startToken&&t.payload.userTask.startToken.formData&&(this.formData={...t.payload.userTask.startToken.formData},console.info(this.formData)),this.$store.commit("data/bind",{widgetId:this.id,msg:t})}),this.$socket.emit("widget-load",this.id)},unmounted(){var t,o;(t=this.$socket)==null||t.off("widget-load"+this.id),(o=this.$socket)==null||o.off("msg-input:"+this.id)},methods:{hasUserTask(){return this.messages&&this.messages[this.id]&&this.messages[this.id].payload.userTask},userTask(){return this.hasUserTask()?this.messages[this.id].payload.userTask:{}},fields(){return(this.hasUserTask()?this.userTask().userTaskConfig.formFields:[]).map(s=>({...s,component:k(s.type),items:h(s.type,s)}))},hasFields(){return this.messages&&this.messages[this.id]&&this.messages[this.id].payload.userTask!==void 0},send(t,o){const s=[];s[o]=t,console.info(s),this.$socket.emit("widget-action",this.id,s)},init(){this.actions=this.props.options},actionFn(t){this.send({payload:{formData:this.formData,userTask:this.userTask()}},this.actions.findIndex(o=>o.label===t))}}};function h(t,o){return t==="enum"?o.enumValues.map(s=>({title:s.name,value:s.id})):null}function k(t){switch(t){case"string":return"v-text-field";case"long":case"date":return"v-text-field";case"enum":return"v-select";case"boolean":return"v-checkbox";case"text":return"v-text-field";case"select":return"v-select";case"checkbox":return"v-checkbox";case"radio":return"v-radio";case"switch":return"v-switch";case"slider":return"v-slider";case"time":return"v-time-picker";case"datetime":return"v-datetime-picker";case"color":return"v-color-picker";case"file":return"v-file-input";case"textarea":return"v-textarea";case"password":return"v-text-field";case"number":return"v-text-field";case"email":return"v-text-field";case"tel":return"v-text-field";case"url":return"v-text-field";default:return"v-text-field"}}const _={className:"dynamic-form-wrapper"},y={key:0},x={key:1};function g(t,o,s,l,r,a){const T=e.resolveComponent("v-col"),d=e.resolveComponent("v-row"),b=e.resolveComponent("v-btn"),B=e.resolveComponent("v-form"),D=e.resolveComponent("v-alert");return e.openBlock(),e.createElementBlock("div",_,[a.hasFields()?(e.openBlock(),e.createElementBlock("p",y,[e.createVNode(B,{ref:"form",modelValue:r.form,"onUpdate:modelValue":o[0]||(o[0]=i=>r.form=i)},{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(a.fields(),(i,c)=>(e.openBlock(),e.createBlock(d,{key:c},{default:e.withCtx(()=>[e.createVNode(T,{cols:"12"},{default:e.withCtx(()=>[(e.openBlock(),e.createBlock(e.resolveDynamicComponent(i.component),{id:i.id,modelValue:r.formData[i.id],"onUpdate:modelValue":m=>r.formData[i.id]=m,required:i.required,items:i.items,label:i.label},null,8,["id","modelValue","onUpdate:modelValue","required","items","label"]))]),_:2},1024)]),_:2},1024))),128)),e.createVNode(d,{style:{display:"flex",gap:"8px",padding:"12px"}},{default:e.withCtx(()=>[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(r.actions,(i,c)=>(e.openBlock(),e.createElementBlock("div",{key:c,style:{"flex-grow":"1"}},[(e.openBlock(),e.createBlock(b,{key:c,style:{width:"100%"},onClick:m=>a.actionFn(i.label)},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(i.label),1)]),_:2},1032,["onClick"]))]))),128))]),_:1})]),_:1},8,["modelValue"])])):(e.openBlock(),e.createElementBlock("p",x,[e.createVNode(D,{text:a.waiting_info,title:a.waiting_title},null,8,["text","title"])]))])}const w=u(f,[["render",g],["__scopeId","data-v-5ca4b3d4"]]);n.DynamicForm=w,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})});
@@ -1,214 +1,222 @@
1
1
  <template>
2
- <!-- Component must be wrapped in a block so props such as className and style can be passed in from parent -->
3
- <div className="dynamic-form-wrapper">
4
- <p v-if="hasFields">
5
- <v-form ref="form" v-model="form">
6
- <v-row v-for="(field, index) in fields()" :key="index">
7
- <v-col cols="12">
8
- <component
9
- :is="field.component"
10
- :id="field.id"
11
- v-model="formData[field.id]"
12
- :required="field.required"
13
- :items="field.items"
14
- :label="field.label"
15
- />
16
- </v-col>
17
- </v-row>
2
+ <!-- Component must be wrapped in a block so props such as className and style can be passed in from parent -->
3
+ <div className="dynamic-form-wrapper">
4
+ <p v-if="hasFields()">
5
+ <v-form ref="form" v-model="form">
6
+ <v-row v-for="(field, index) in fields()" :key="index">
7
+ <v-col cols="12">
8
+ <component
9
+ :is="field.component"
10
+ :id="field.id"
11
+ v-model="formData[field.id]"
12
+ :required="field.required"
13
+ :items="field.items"
14
+ :label="field.label" />
15
+ </v-col>
16
+ </v-row>
18
17
 
19
- <v-row>
20
- <v-col cols="6">
21
- <v-btn variant="outlined" block @click="cancel">{{ cancel_title }}</v-btn>
22
- </v-col>
23
- <v-col cols="6">
24
- <v-btn block @click="submit">{{ submit_title }}</v-btn>
25
- </v-col>
26
- </v-row>
27
- </v-form>
28
- </p>
29
- <p v-else>
30
- <v-alert :text="waiting_info" :title="waiting_title" />
31
- </p>
32
- </div>
18
+ <v-row style="display: flex; gap: 8px; padding: 12px">
19
+ <div
20
+ v-for="(action, index) in actions"
21
+ :key="index"
22
+ style="flex-grow: 1">
23
+ <v-btn
24
+ :key="index"
25
+ style="width: 100%"
26
+ @click="actionFn(action.label)">
27
+ {{ action.label }}
28
+ </v-btn>
29
+ </div>
30
+ </v-row>
31
+ </v-form>
32
+ </p>
33
+ <p v-else>
34
+ <v-alert :text="waiting_info" :title="waiting_title" />
35
+ </p>
36
+ </div>
33
37
  </template>
34
38
 
35
39
  <script>
36
- import { markRaw } from 'vue'
37
- import { mapState } from 'vuex'
40
+ import { markRaw } from "vue";
41
+ import { mapState } from "vuex";
38
42
 
39
43
  export default {
40
- name: 'DynamicForm',
41
- inject: ['$socket'],
42
- props: {
43
- /* do not remove entries from this - Dashboard's Layout Manager's will pass this data to your component */
44
- id: { type: String, required: true },
45
- props: { type: Object, default: () => ({}) },
46
- state: { type: Object, default: () => ({ enabled: false, visible: false }) }
44
+ name: "DynamicForm",
45
+ inject: ["$socket"],
46
+ props: {
47
+ /* do not remove entries from this - Dashboard's Layout Manager's will pass this data to your component */
48
+ id: { type: String, required: true },
49
+ props: { type: Object, default: () => ({}) },
50
+ state: {
51
+ type: Object,
52
+ default: () => ({ enabled: false, visible: false }),
47
53
  },
48
- setup (props) {
49
- console.info('DynamicForm setup with:', props)
50
- console.debug('Vue function loaded correctly', markRaw)
54
+ },
55
+ setup(props) {
56
+ console.info("DynamicForm setup with:", props);
57
+ console.debug("Vue function loaded correctly", markRaw);
58
+ },
59
+ data() {
60
+ return {
61
+ actions: [],
62
+ form: {},
63
+ formData: {},
64
+ };
65
+ },
66
+ computed: {
67
+ ...mapState("data", ["messages"]),
68
+ waiting_title() {
69
+ return this.props.waiting_title || "Warten auf den Usertask...";
51
70
  },
52
- data () {
53
- return {
54
- form: {},
55
- formData: {}
56
- }
71
+ waiting_info() {
72
+ return (
73
+ this.props.waiting_info ||
74
+ "Der Usertask wird automatisch angezeigt, wenn ein entsprechender Task vorhanden ist."
75
+ );
57
76
  },
58
- computed: {
59
- ...mapState('data', ['messages']),
60
- waiting_title () {
61
- return this.props.waiting_title || 'Warten auf den Usertask...'
62
- },
63
- waiting_info () {
64
- return this.props.waiting_info || 'Der Usertask wird automatisch angezeigt, wenn ein entsprechender Task vorhanden ist.'
65
- },
66
- submit_title () {
67
- return this.props.submit_title || 'Submit your task'
68
- },
69
- cancel_title () {
70
- return this.props.cancel_title || 'Cancel your task'
71
- }
72
- },
73
- mounted () {
74
- this.$socket.on('widget-load:' + this.id, (msg) => {
75
- // load the latest message from the Node-RED datastore when this widget is loaded
76
- // storing it in our vuex store so that we have it saved as we navigate around
77
- this.$store.commit('data/bind', {
78
- widgetId: this.id,
79
- msg
80
- })
81
- })
82
- this.$socket.on('msg-input:' + this.id, (msg) => {
83
- // store the latest message in our client-side vuex store when we receive a new message
84
-
85
- if (msg.payload.formFields) {
86
- const formData = msg.payload.formFields.reduce((acc, field) => {
87
- console.info('field:', field)
88
- console.info('field.id:', field.id)
89
- console.info('field.value:', field.value)
90
- console.info('field.defaultValue:', field.defaultValue)
91
-
92
- acc[field.id] = field.value || field.defaultValue || ''
93
- return acc
94
- }, {})
77
+ },
78
+ mounted() {
79
+ this.$socket.on("widget-load:" + this.id, (msg) => {
80
+ this.init()
95
81
 
96
- console.info('formData: ', formData)
97
- console.info('this.formData (1):', this.formData)
82
+ this.$store.commit("data/bind", {
83
+ widgetId: this.id,
84
+ msg,
85
+ });
86
+ });
87
+ this.$socket.on("msg-input:" + this.id, (msg) => {
88
+ // store the latest message in our client-side vuex store when we receive a new message
89
+ this.init()
98
90
 
99
- this.formData = formData
100
- console.info('this.formData (2):', this.formData)
101
- }
91
+ if (msg.payload && msg.payload.userTask && msg.payload.userTask.startToken && msg.payload.userTask.startToken.formData) {
92
+ this.formData = { ...msg.payload.userTask.startToken.formData };
93
+ console.info(this.formData)
94
+ }
102
95
 
103
- this.$store.commit('data/bind', {
104
- widgetId: this.id,
105
- msg
106
- })
107
- })
108
- // tell Node-RED that we're loading a new instance of this widget
109
- this.$socket.emit('widget-load', this.id)
96
+ this.$store.commit("data/bind", {
97
+ widgetId: this.id,
98
+ msg,
99
+ });
100
+ });
101
+ // tell Node-RED that we're loading a new instance of this widget
102
+ this.$socket.emit("widget-load", this.id);
103
+ },
104
+ unmounted() {
105
+ /* Make sure, any events you subscribe to on SocketIO are unsubscribed to here */
106
+ this.$socket?.off("widget-load" + this.id);
107
+ this.$socket?.off("msg-input:" + this.id);
108
+ },
109
+ methods: {
110
+ hasUserTask() {
111
+ return (
112
+ this.messages &&
113
+ this.messages[this.id] &&
114
+ this.messages[this.id].payload.userTask
115
+ );
110
116
  },
111
- unmounted () {
112
- /* Make sure, any events you subscribe to on SocketIO are unsubscribed to here */
113
- this.$socket?.off('widget-load' + this.id)
114
- this.$socket?.off('msg-input:' + this.id)
117
+ userTask() {
118
+ return this.hasUserTask() ? this.messages[this.id].payload.userTask : {};
115
119
  },
116
- methods: {
117
- hasUserTask () {
118
- return this.messages && this.messages[this.id] && this.messages[this.id].payload.userTask
119
- },
120
- userTask () {
121
- return this.hasUserTask() ? this.messages[this.id].payload.userTask : {}
122
- },
123
- fields () {
124
- const aFields = this.hasUserTask() ? this.userTask().userTaskConfig.formFields : []
120
+ fields() {
121
+ const aFields = this.hasUserTask()
122
+ ? this.userTask().userTaskConfig.formFields
123
+ : [];
125
124
 
126
- const fieldMap = aFields.map(field => ({
127
- ...field,
128
- component: mapFieldTypes(field.type),
129
- items: mapItems(field.type, field)
130
- }))
125
+ const fieldMap = aFields.map((field) => ({
126
+ ...field,
127
+ component: mapFieldTypes(field.type),
128
+ items: mapItems(field.type, field),
129
+ }));
131
130
 
132
- return fieldMap
133
- },
134
- hasFields () {
135
- return this.hasUserTask && this.userTask.userTaskConfig.formFields.length > 0
136
- },
137
- /*
131
+ return fieldMap;
132
+ },
133
+ hasFields() {
134
+ return (
135
+ this.messages &&
136
+ this.messages[this.id] &&
137
+ this.messages[this.id].payload.userTask !== undefined
138
+ );
139
+ },
140
+ /*
138
141
  widget-action just sends a msg to Node-RED, it does not store the msg state server-side
139
142
  alternatively, you can use widget-change, which will also store the msg in the Node's datastore
140
143
  */
141
- send (msg) {
142
- this.$socket.emit('widget-action', this.id, msg)
143
- },
144
- submit () {
145
- this.send({ payload: { formData: this.formData, userTask: this.userTask(), action: 'submit' } })
146
- },
147
- cancel () {
148
- this.send({ payload: { formData: this.formData, userTask: this.userTask(), action: 'cancel' } })
149
- }
150
- }
151
- }
144
+ send(msg, index) {
145
+ const msgArr = [];
146
+ msgArr[index] = msg;
147
+ console.info(msgArr);
148
+ this.$socket.emit("widget-action", this.id, msgArr);
149
+ },
150
+ init() {
151
+ this.actions = this.props.options;
152
+ },
153
+ actionFn(action) {
154
+ this.send(
155
+ { payload: { formData: this.formData, userTask: this.userTask() } },
156
+ this.actions.findIndex((element) => element.label === action)
157
+ );
158
+ },
159
+ },
160
+ };
152
161
 
153
- function mapItems (type, field) {
154
- if (type === 'enum') {
155
- return field.enumValues.map(enumValue => ({
156
- title: enumValue.name,
157
- value: enumValue.id
158
- }))
159
- } else {
160
- return null
161
- }
162
+ function mapItems(type, field) {
163
+ if (type === "enum") {
164
+ return field.enumValues.map((enumValue) => ({
165
+ title: enumValue.name,
166
+ value: enumValue.id,
167
+ }));
168
+ } else {
169
+ return null;
170
+ }
162
171
  }
163
172
 
164
- function mapFieldTypes (fieldType) {
165
- switch (fieldType) {
166
- case 'string':
167
- return 'v-text-field'
168
- case 'long':
169
- case 'date':
170
- return 'v-text-field'
171
- case 'enum':
172
- return 'v-select'
173
- case 'boolean':
174
- return 'v-checkbox'
175
- case 'text':
176
- return 'v-text-field'
177
- case 'select':
178
- return 'v-select'
179
- case 'checkbox':
180
- return 'v-checkbox'
181
- case 'radio':
182
- return 'v-radio'
183
- case 'switch':
184
- return 'v-switch'
185
- case 'slider':
186
- return 'v-slider'
187
- case 'time':
188
- return 'v-time-picker'
189
- case 'datetime':
190
- return 'v-datetime-picker'
191
- case 'color':
192
- return 'v-color-picker'
193
- case 'file':
194
- return 'v-file-input'
195
- case 'textarea':
196
- return 'v-textarea'
197
- case 'password':
198
- return 'v-text-field'
199
- case 'number':
200
- return 'v-text-field'
201
- case 'email':
202
- return 'v-text-field'
203
- case 'tel':
204
- return 'v-text-field'
205
- case 'url':
206
- return 'v-text-field'
173
+ function mapFieldTypes(fieldType) {
174
+ switch (fieldType) {
175
+ case "string":
176
+ return "v-text-field";
177
+ case "long":
178
+ case "date":
179
+ return "v-text-field";
180
+ case "enum":
181
+ return "v-select";
182
+ case "boolean":
183
+ return "v-checkbox";
184
+ case "text":
185
+ return "v-text-field";
186
+ case "select":
187
+ return "v-select";
188
+ case "checkbox":
189
+ return "v-checkbox";
190
+ case "radio":
191
+ return "v-radio";
192
+ case "switch":
193
+ return "v-switch";
194
+ case "slider":
195
+ return "v-slider";
196
+ case "time":
197
+ return "v-time-picker";
198
+ case "datetime":
199
+ return "v-datetime-picker";
200
+ case "color":
201
+ return "v-color-picker";
202
+ case "file":
203
+ return "v-file-input";
204
+ case "textarea":
205
+ return "v-textarea";
206
+ case "password":
207
+ return "v-text-field";
208
+ case "number":
209
+ return "v-text-field";
210
+ case "email":
211
+ return "v-text-field";
212
+ case "tel":
213
+ return "v-text-field";
214
+ case "url":
215
+ return "v-text-field";
207
216
  default:
208
- return 'v-text-field'
209
- }
217
+ return "v-text-field";
218
+ }
210
219
  }
211
-
212
220
  </script>
213
221
 
214
222
  <style scoped>
@@ -1 +0,0 @@
1
- {"version":3,"file":"dynamic-form.umd.js","sources":["../ui/components/DynamicForm.vue"],"sourcesContent":["<template>\n <!-- Component must be wrapped in a block so props such as className and style can be passed in from parent -->\n <div className=\"dynamic-form-wrapper\">\n <p v-if=\"hasFields\">\n <v-form ref=\"form\" v-model=\"form\">\n <v-row v-for=\"(field, index) in fields()\" :key=\"index\">\n <v-col cols=\"12\">\n <component\n :is=\"field.component\"\n :id=\"field.id\"\n v-model=\"formData[field.id]\"\n :required=\"field.required\"\n :items=\"field.items\"\n :label=\"field.label\"\n />\n </v-col>\n </v-row>\n\n <v-row>\n <v-col cols=\"6\">\n <v-btn variant=\"outlined\" block @click=\"cancel\">{{ cancel_title }}</v-btn>\n </v-col>\n <v-col cols=\"6\">\n <v-btn block @click=\"submit\">{{ submit_title }}</v-btn>\n </v-col>\n </v-row>\n </v-form>\n </p>\n <p v-else>\n <v-alert :text=\"waiting_info\" :title=\"waiting_title\" />\n </p>\n </div>\n</template>\n\n<script>\nimport { markRaw } from 'vue'\nimport { mapState } from 'vuex'\n\nexport default {\n name: 'DynamicForm',\n inject: ['$socket'],\n props: {\n /* do not remove entries from this - Dashboard's Layout Manager's will pass this data to your component */\n id: { type: String, required: true },\n props: { type: Object, default: () => ({}) },\n state: { type: Object, default: () => ({ enabled: false, visible: false }) }\n },\n setup (props) {\n console.info('DynamicForm setup with:', props)\n console.debug('Vue function loaded correctly', markRaw)\n },\n data () {\n return {\n form: {},\n formData: {}\n }\n },\n computed: {\n ...mapState('data', ['messages']),\n waiting_title () {\n return this.props.waiting_title || 'Warten auf den Usertask...'\n },\n waiting_info () {\n return this.props.waiting_info || 'Der Usertask wird automatisch angezeigt, wenn ein entsprechender Task vorhanden ist.'\n },\n submit_title () {\n return this.props.submit_title || 'Submit your task'\n },\n cancel_title () {\n return this.props.cancel_title || 'Cancel your task'\n }\n },\n mounted () {\n this.$socket.on('widget-load:' + this.id, (msg) => {\n // load the latest message from the Node-RED datastore when this widget is loaded\n // storing it in our vuex store so that we have it saved as we navigate around\n this.$store.commit('data/bind', {\n widgetId: this.id,\n msg\n })\n })\n this.$socket.on('msg-input:' + this.id, (msg) => {\n // store the latest message in our client-side vuex store when we receive a new message\n\n if (msg.payload.formFields) {\n const formData = msg.payload.formFields.reduce((acc, field) => {\n console.info('field:', field)\n console.info('field.id:', field.id)\n console.info('field.value:', field.value)\n console.info('field.defaultValue:', field.defaultValue)\n\n acc[field.id] = field.value || field.defaultValue || ''\n return acc\n }, {})\n\n console.info('formData: ', formData)\n console.info('this.formData (1):', this.formData)\n\n this.formData = formData\n console.info('this.formData (2):', this.formData)\n }\n\n this.$store.commit('data/bind', {\n widgetId: this.id,\n msg\n })\n })\n // tell Node-RED that we're loading a new instance of this widget\n this.$socket.emit('widget-load', this.id)\n },\n unmounted () {\n /* Make sure, any events you subscribe to on SocketIO are unsubscribed to here */\n this.$socket?.off('widget-load' + this.id)\n this.$socket?.off('msg-input:' + this.id)\n },\n methods: {\n hasUserTask () {\n return this.messages && this.messages[this.id] && this.messages[this.id].payload.userTask\n },\n userTask () {\n return this.hasUserTask() ? this.messages[this.id].payload.userTask : {}\n },\n fields () {\n const aFields = this.hasUserTask() ? this.userTask().userTaskConfig.formFields : []\n\n const fieldMap = aFields.map(field => ({\n ...field,\n component: mapFieldTypes(field.type),\n items: mapItems(field.type, field)\n }))\n\n return fieldMap\n },\n hasFields () {\n return this.hasUserTask && this.userTask.userTaskConfig.formFields.length > 0\n },\n /*\n widget-action just sends a msg to Node-RED, it does not store the msg state server-side\n alternatively, you can use widget-change, which will also store the msg in the Node's datastore\n */\n send (msg) {\n this.$socket.emit('widget-action', this.id, msg)\n },\n submit () {\n this.send({ payload: { formData: this.formData, userTask: this.userTask(), action: 'submit' } })\n },\n cancel () {\n this.send({ payload: { formData: this.formData, userTask: this.userTask(), action: 'cancel' } })\n }\n }\n}\n\nfunction mapItems (type, field) {\n if (type === 'enum') {\n return field.enumValues.map(enumValue => ({\n title: enumValue.name,\n value: enumValue.id\n }))\n } else {\n return null\n }\n}\n\nfunction mapFieldTypes (fieldType) {\n switch (fieldType) {\n case 'string':\n return 'v-text-field'\n case 'long':\n case 'date':\n return 'v-text-field'\n case 'enum':\n return 'v-select'\n case 'boolean':\n return 'v-checkbox'\n case 'text':\n return 'v-text-field'\n case 'select':\n return 'v-select'\n case 'checkbox':\n return 'v-checkbox'\n case 'radio':\n return 'v-radio'\n case 'switch':\n return 'v-switch'\n case 'slider':\n return 'v-slider'\n case 'time':\n return 'v-time-picker'\n case 'datetime':\n return 'v-datetime-picker'\n case 'color':\n return 'v-color-picker'\n case 'file':\n return 'v-file-input'\n case 'textarea':\n return 'v-textarea'\n case 'password':\n return 'v-text-field'\n case 'number':\n return 'v-text-field'\n case 'email':\n return 'v-text-field'\n case 'tel':\n return 'v-text-field'\n case 'url':\n return 'v-text-field'\n default:\n return 'v-text-field'\n }\n}\n\n</script>\n\n<style scoped>\n/* CSS is auto scoped, but using named classes is still recommended */\n@import \"../stylesheets/dynamic-form.css\";\n</style>\n"],"names":["_sfc_main","props","markRaw","mapState","msg","formData","acc","field","_a","_b","mapFieldTypes","mapItems","type","enumValue","fieldType","_hoisted_1","_openBlock","_createElementBlock","_Fragment","_createCommentVNode","_createElementVNode","$options","_hoisted_2","_createVNode","_component_v_form","$data","_cache","$event","_withCtx","_renderList","index","_createBlock","_component_v_row","_component_v_col","_resolveDynamicComponent","_component_v_btn","_createTextVNode","_toDisplayString","_hoisted_3","_component_v_alert"],"mappings":"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"}