@flowfuse/node-red-dashboard 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +53 -0
- package/dist/css/app.d047b42b.css +1 -0
- package/dist/css/chunk-vendors.2378ce49.css +24 -0
- package/dist/fonts/materialdesignicons-webfont.3de8526e.woff +0 -0
- package/dist/fonts/materialdesignicons-webfont.477c6ab0.woff2 +0 -0
- package/dist/fonts/materialdesignicons-webfont.48a1ce0c.eot +0 -0
- package/dist/fonts/materialdesignicons-webfont.dfd403cf.ttf +0 -0
- package/dist/index.html +1 -0
- package/dist/js/app.854a8cd5.js +2 -0
- package/dist/js/app.854a8cd5.js.map +1 -0
- package/dist/js/chunk-vendors.174e8921.js +43 -0
- package/dist/js/chunk-vendors.174e8921.js.map +1 -0
- package/nodes/config/locales/en-US/ui_base.json +19 -0
- package/nodes/config/locales/en-US/ui_group.html +4 -0
- package/nodes/config/locales/en-US/ui_group.json +16 -0
- package/nodes/config/ui_base.html +807 -0
- package/nodes/config/ui_base.js +678 -0
- package/nodes/config/ui_group.html +55 -0
- package/nodes/config/ui_group.js +34 -0
- package/nodes/config/ui_page.html +84 -0
- package/nodes/config/ui_page.js +33 -0
- package/nodes/config/ui_theme.html +101 -0
- package/nodes/config/ui_theme.js +15 -0
- package/nodes/store/index.js +34 -0
- package/nodes/utils/index.js +35 -0
- package/nodes/widgets/locales/en-US/ui_button.html +7 -0
- package/nodes/widgets/locales/en-US/ui_button.json +24 -0
- package/nodes/widgets/locales/en-US/ui_chart.html +41 -0
- package/nodes/widgets/locales/en-US/ui_chart.json +17 -0
- package/nodes/widgets/locales/en-US/ui_dropdown.html +24 -0
- package/nodes/widgets/locales/en-US/ui_form.html +16 -0
- package/nodes/widgets/locales/en-US/ui_form.json +36 -0
- package/nodes/widgets/locales/en-US/ui_markdown.html +10 -0
- package/nodes/widgets/locales/en-US/ui_slider.html +9 -0
- package/nodes/widgets/locales/en-US/ui_switch.html +32 -0
- package/nodes/widgets/locales/en-US/ui_template.html +59 -0
- package/nodes/widgets/locales/en-US/ui_template.json +18 -0
- package/nodes/widgets/locales/en-US/ui_text.html +16 -0
- package/nodes/widgets/locales/en-US/ui_text_input.html +19 -0
- package/nodes/widgets/ui_button.html +146 -0
- package/nodes/widgets/ui_button.js +65 -0
- package/nodes/widgets/ui_chart.html +314 -0
- package/nodes/widgets/ui_chart.js +195 -0
- package/nodes/widgets/ui_dropdown.html +199 -0
- package/nodes/widgets/ui_dropdown.js +19 -0
- package/nodes/widgets/ui_form.html +368 -0
- package/nodes/widgets/ui_form.js +18 -0
- package/nodes/widgets/ui_markdown.html +134 -0
- package/nodes/widgets/ui_markdown.js +14 -0
- package/nodes/widgets/ui_notification.html +139 -0
- package/nodes/widgets/ui_notification.js +14 -0
- package/nodes/widgets/ui_radio_group.html +186 -0
- package/nodes/widgets/ui_radio_group.js +20 -0
- package/nodes/widgets/ui_slider.html +162 -0
- package/nodes/widgets/ui_slider.js +31 -0
- package/nodes/widgets/ui_switch.html +194 -0
- package/nodes/widgets/ui_switch.js +98 -0
- package/nodes/widgets/ui_table.html +149 -0
- package/nodes/widgets/ui_table.js +16 -0
- package/nodes/widgets/ui_template.html +283 -0
- package/nodes/widgets/ui_template.js +19 -0
- package/nodes/widgets/ui_text.html +358 -0
- package/nodes/widgets/ui_text.js +98 -0
- package/nodes/widgets/ui_text_input.html +141 -0
- package/nodes/widgets/ui_text_input.js +37 -0
- package/package.json +114 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
(function () {
|
|
3
|
+
function hasProperty (obj, prop) {
|
|
4
|
+
return Object.prototype.hasOwnProperty.call(obj, prop)
|
|
5
|
+
}
|
|
6
|
+
RED.nodes.registerType('ui-switch', {
|
|
7
|
+
category: RED._('@flowforge/node-red-dashboard/ui-base:ui-base.label.category'),
|
|
8
|
+
color: RED._('@flowforge/node-red-dashboard/ui-base:ui-base.colors.light'),
|
|
9
|
+
defaults: {
|
|
10
|
+
name: { value: '' },
|
|
11
|
+
label: { value: 'switch' },
|
|
12
|
+
// tooltip: {value: ''},
|
|
13
|
+
group: { type: 'ui-group', required: true },
|
|
14
|
+
order: { value: 0 },
|
|
15
|
+
width: {
|
|
16
|
+
value: 0,
|
|
17
|
+
validate: function (v) {
|
|
18
|
+
const width = v || 0
|
|
19
|
+
const currentGroup = $('#node-input-group').val() || this.group
|
|
20
|
+
const groupNode = RED.nodes.node(currentGroup)
|
|
21
|
+
const valid = !groupNode || +width <= +groupNode.width
|
|
22
|
+
$('#node-input-size').toggleClass('input-error', !valid)
|
|
23
|
+
return valid
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
height: { value: 0 },
|
|
27
|
+
passthru: { value: false },
|
|
28
|
+
topic: { value: 'topic', validate: (hasProperty(RED.validators, 'typedInput') ? RED.validators.typedInput('topicType') : function (v) { return true }) },
|
|
29
|
+
topicType: { value: 'msg' },
|
|
30
|
+
style: { value: '' },
|
|
31
|
+
className: { value: '' },
|
|
32
|
+
// on state
|
|
33
|
+
onvalue: { value: true, validate: (hasProperty(RED.validators, 'typedInput') ? RED.validators.typedInput('onvalueType') : function (v) { return true }) },
|
|
34
|
+
onvalueType: { value: 'bool' },
|
|
35
|
+
onicon: { value: '' },
|
|
36
|
+
oncolor: { value: '' },
|
|
37
|
+
// off state
|
|
38
|
+
offvalue: { value: false, validate: (hasProperty(RED.validators, 'typedInput') ? RED.validators.typedInput('offvalueType') : function (v) { return true }) },
|
|
39
|
+
offvalueType: { value: 'bool' },
|
|
40
|
+
officon: { value: '' },
|
|
41
|
+
offcolor: { value: '' }
|
|
42
|
+
},
|
|
43
|
+
inputs: 1,
|
|
44
|
+
outputs: 1,
|
|
45
|
+
icon: 'font-awesome/fa-toggle-on',
|
|
46
|
+
paletteLabel: 'switch',
|
|
47
|
+
label: function () { return this.name || (~this.label.indexOf('{' + '{') ? null : this.label) || 'switch' },
|
|
48
|
+
labelStyle: function () { return this.name ? 'node_label_italic' : '' },
|
|
49
|
+
oneditprepare: function () {
|
|
50
|
+
$('#node-input-size').elementSizer({
|
|
51
|
+
width: '#node-input-width',
|
|
52
|
+
height: '#node-input-height',
|
|
53
|
+
group: '#node-input-group'
|
|
54
|
+
})
|
|
55
|
+
$('#node-input-custom-icons').on('change', function () {
|
|
56
|
+
if ($('#node-input-custom-icons').val() === 'default') {
|
|
57
|
+
$('.form-row-custom-icons').hide()
|
|
58
|
+
} else {
|
|
59
|
+
$('.form-row-custom-icons').show()
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
if (this.onicon !== '' || this.oncolor !== '' || this.officon !== '' || this.offcolor !== '') {
|
|
64
|
+
$('#node-input-custom-icons').val('custom')
|
|
65
|
+
} else {
|
|
66
|
+
$('.form-row-custom-icons').hide()
|
|
67
|
+
$('#node-input-custom-icons').change()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
$('#node-input-onvalue').typedInput({
|
|
71
|
+
default: 'str',
|
|
72
|
+
typeField: $('#node-input-onvalueType'),
|
|
73
|
+
types: ['str', 'num', 'bool', 'json', 'bin', 'date', 'flow', 'global']
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
$('#node-input-offvalue').typedInput({
|
|
77
|
+
default: 'str',
|
|
78
|
+
typeField: $('#node-input-offvalueType'),
|
|
79
|
+
types: ['str', 'num', 'bool', 'json', 'bin', 'date', 'flow', 'global']
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
$('#node-input-topic').typedInput({
|
|
83
|
+
default: 'str',
|
|
84
|
+
typeField: $('#node-input-topicType'),
|
|
85
|
+
types: ['str', 'msg', 'flow', 'global']
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
$('#node-input-passthru').on('change', function () {
|
|
89
|
+
if (this.checked) {
|
|
90
|
+
$('.form-row-decouple').hide()
|
|
91
|
+
$('#node-input-decouple').val('false')
|
|
92
|
+
} else {
|
|
93
|
+
$('.form-row-decouple').show()
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
// use jQuery UI tooltip to convert the plain old title attribute to a nice tooltip
|
|
98
|
+
$('.ui-node-popover-title').tooltip({
|
|
99
|
+
show: {
|
|
100
|
+
effect: 'slideDown',
|
|
101
|
+
delay: 150
|
|
102
|
+
}
|
|
103
|
+
})
|
|
104
|
+
},
|
|
105
|
+
oneditsave: function () {
|
|
106
|
+
if ($('#node-input-custom-icons').val() === 'default') {
|
|
107
|
+
$('#node-input-onicon').val('')
|
|
108
|
+
$('#node-input-officon').val('')
|
|
109
|
+
$('#node-input-oncolor').val('')
|
|
110
|
+
$('#node-input-offcolor').val('')
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
})()
|
|
115
|
+
</script>
|
|
116
|
+
|
|
117
|
+
<script type="text/html" data-template-name="ui-switch">
|
|
118
|
+
<div class="form-row">
|
|
119
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
120
|
+
<input type="text" id="node-input-name">
|
|
121
|
+
</div>
|
|
122
|
+
<div class="form-row">
|
|
123
|
+
<label for="node-input-group"><i class="fa fa-table"></i> Group</label>
|
|
124
|
+
<input type="text" id="node-input-group">
|
|
125
|
+
</div>
|
|
126
|
+
<div class="form-row">
|
|
127
|
+
<label><i class="fa fa-object-group"></i> Size</label>
|
|
128
|
+
<input type="hidden" id="node-input-width">
|
|
129
|
+
<input type="hidden" id="node-input-height">
|
|
130
|
+
<button class="editor-button" id="node-input-size"></button>
|
|
131
|
+
</div>
|
|
132
|
+
<div class="form-row">
|
|
133
|
+
<label for="node-input-label"><i class="fa fa-i-cursor"></i> Label</label>
|
|
134
|
+
<input type="text" id="node-input-label">
|
|
135
|
+
</div>
|
|
136
|
+
<div class="form-row">
|
|
137
|
+
<label for="node-input-className"><i class="fa fa-code"></i> Class</label>
|
|
138
|
+
<div style="display: inline;">
|
|
139
|
+
<input style="width: 70%;" type="text" id="node-input-className" placeholder="Optional CSS class name(s)" style="flex-grow: 1;">
|
|
140
|
+
<a
|
|
141
|
+
data-html="true"
|
|
142
|
+
title="Dynamic Property: Class names can also be set by sending a message to the node with a msg.topic of 'ui-property:class' and a payload containing the class name(s) to be applied. NOTE: classes set at runtime will be applied in addition to any class(es) set in the nodes class field."
|
|
143
|
+
class="red-ui-button ui-node-popover-title"
|
|
144
|
+
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;">
|
|
145
|
+
<i style="font-family: ui-serif;">fx</i>
|
|
146
|
+
</a>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
<!--<div class="form-row">
|
|
150
|
+
<label for="node-input-tooltip"><i class="fa fa-info-circle"></i> Tooltip</label>
|
|
151
|
+
<input type="text" id="node-input-tooltip" placeholder="optional tooltip">
|
|
152
|
+
</div>-->
|
|
153
|
+
<div class="form-row">
|
|
154
|
+
<label for="node-input-custom-icons"><i class="fa fa-picture-o"></i> Icon</label>
|
|
155
|
+
<select id="node-input-custom-icons" style="width:35%">
|
|
156
|
+
<option value="default">Default</option>
|
|
157
|
+
<option value="custom">Custom</option>
|
|
158
|
+
</select>
|
|
159
|
+
</div>
|
|
160
|
+
<div class="form-row form-row-custom-icons">
|
|
161
|
+
<label for="node-input-onicon" style="text-align:right;"><i class="fa fa-toggle-on"></i> On Icon</label>
|
|
162
|
+
<input type="text" id="node-input-onicon" style="width:120px">
|
|
163
|
+
<label for="node-input-oncolor" style="width:50px; text-align:right;">Colour</label>
|
|
164
|
+
<input type="text" id="node-input-oncolor" style="width:120px">
|
|
165
|
+
</div>
|
|
166
|
+
<div class="form-row form-row-custom-icons">
|
|
167
|
+
<label for="node-input-officon" style="text-align:right;"><i class="fa fa-toggle-off"></i> Off Icon</label>
|
|
168
|
+
<input type="text" id="node-input-officon" style="width:120px">
|
|
169
|
+
<label for="node-input-offcolor" style="width:50px; text-align:right;">Colour</label>
|
|
170
|
+
<input type="text" id="node-input-offcolor" style="width:120px">
|
|
171
|
+
</div>
|
|
172
|
+
<div class="form-row">
|
|
173
|
+
<label style="width:auto" for="node-input-passthru"><i class="fa fa-arrow-right"></i> If <code>msg</code> arrives on input, pass through to output: </label>
|
|
174
|
+
<input type="checkbox" checked id="node-input-passthru" style="display:inline-block; width:auto; vertical-align:top;">
|
|
175
|
+
</div>
|
|
176
|
+
<div class="form-row">
|
|
177
|
+
<label style="width:auto" for="node-input-onvalue"><i class="fa fa-envelope-o"></i> When clicked, send:</label>
|
|
178
|
+
</div>
|
|
179
|
+
<div class="form-row">
|
|
180
|
+
<label for="node-input-onvalue" style="padding-left:25px; margin-right:-25px">On Payload</label>
|
|
181
|
+
<input type="text" id="node-input-onvalue" style="width:70%">
|
|
182
|
+
<input type="hidden" id="node-input-onvalueType">
|
|
183
|
+
</div>
|
|
184
|
+
<div class="form-row">
|
|
185
|
+
<label for="node-input-offvalue" style="padding-left:25px; margin-right:-25px">Off Payload</label>
|
|
186
|
+
<input type="text" id="node-input-offvalue" style="width:70%">
|
|
187
|
+
<input type="hidden" id="node-input-offvalueType">
|
|
188
|
+
</div>
|
|
189
|
+
<div class="form-row">
|
|
190
|
+
<label for="node-input-topic" style="padding-left:25px; margin-right:-25px">Topic</label>
|
|
191
|
+
<input type="text" id="node-input-topic">
|
|
192
|
+
<input type="hidden" id="node-input-topicType">
|
|
193
|
+
</div>
|
|
194
|
+
</script>
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const datastore = require('../store/index.js')
|
|
2
|
+
const { appendTopic } = require('../utils/index.js')
|
|
3
|
+
|
|
4
|
+
module.exports = function (RED) {
|
|
5
|
+
function SwitchNode (config) {
|
|
6
|
+
// create node in Node-RED
|
|
7
|
+
RED.nodes.createNode(this, config)
|
|
8
|
+
|
|
9
|
+
const node = this
|
|
10
|
+
node.status({})
|
|
11
|
+
|
|
12
|
+
const states = ['off', 'on']
|
|
13
|
+
|
|
14
|
+
// which group are we rendering this widget
|
|
15
|
+
const group = RED.nodes.getNode(config.group)
|
|
16
|
+
|
|
17
|
+
const evts = {
|
|
18
|
+
// runs on UI interaction
|
|
19
|
+
// value = true | false from the ui-switch
|
|
20
|
+
onChange: async function (value) {
|
|
21
|
+
// ensure we have latest instance of the widget's node
|
|
22
|
+
const wNode = RED.nodes.getNode(node.id)
|
|
23
|
+
const msg = datastore.get(node.id) || {}
|
|
24
|
+
|
|
25
|
+
node.status({
|
|
26
|
+
fill: value ? 'green' : 'red',
|
|
27
|
+
shape: 'ring',
|
|
28
|
+
text: value ? states[1] : states[0]
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// retrieve the assigned on/off value
|
|
32
|
+
const on = RED.util.evaluateNodeProperty(config.onvalue, config.onvalueType, wNode)
|
|
33
|
+
const off = RED.util.evaluateNodeProperty(config.offvalue, config.offvalueType, wNode)
|
|
34
|
+
msg.payload = value ? on : off
|
|
35
|
+
|
|
36
|
+
datastore.save(node.id, msg)
|
|
37
|
+
|
|
38
|
+
// simulate Node-RED node receiving an input
|
|
39
|
+
wNode.send(msg)
|
|
40
|
+
},
|
|
41
|
+
onInput: async function (msg, send) {
|
|
42
|
+
let error = null
|
|
43
|
+
// ensure we have latest instance of the widget's node
|
|
44
|
+
const wNode = RED.nodes.getNode(node.id)
|
|
45
|
+
|
|
46
|
+
// retrieve the assigned on/off value
|
|
47
|
+
const on = RED.util.evaluateNodeProperty(config.onvalue, config.onvalueType, wNode)
|
|
48
|
+
const off = RED.util.evaluateNodeProperty(config.offvalue, config.offvalueType, wNode)
|
|
49
|
+
if (msg.payload === true || msg.payload === on) {
|
|
50
|
+
msg.payload = on
|
|
51
|
+
} else if (msg.payload === false || msg.payload === off) {
|
|
52
|
+
msg.payload = off
|
|
53
|
+
} else {
|
|
54
|
+
// throw Node-RED error
|
|
55
|
+
error = 'Invalid payload value'
|
|
56
|
+
}
|
|
57
|
+
if (!error) {
|
|
58
|
+
// store the latest msg passed to node
|
|
59
|
+
datastore.save(node.id, msg)
|
|
60
|
+
|
|
61
|
+
node.status({
|
|
62
|
+
fill: msg.payload ? 'green' : 'red',
|
|
63
|
+
shape: 'ring',
|
|
64
|
+
text: msg.payload ? states[1] : states[0]
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
if (config.passthru) {
|
|
68
|
+
send(msg)
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
const err = new Error(error)
|
|
72
|
+
err.type = 'warn'
|
|
73
|
+
throw err
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
beforeSend: async function (msg) {
|
|
77
|
+
// ensure we have latest instance of the widget's node
|
|
78
|
+
const wNode = RED.nodes.getNode(node.id)
|
|
79
|
+
|
|
80
|
+
msg = await appendTopic(RED, config, wNode, msg)
|
|
81
|
+
return msg
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const on = RED.util.evaluateNodeProperty(config.onvalue, config.onvalueType, node)
|
|
86
|
+
const off = RED.util.evaluateNodeProperty(config.offvalue, config.offvalueType, node)
|
|
87
|
+
|
|
88
|
+
config.evaluated = {
|
|
89
|
+
on,
|
|
90
|
+
off
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// inform the dashboard UI that we are adding this node
|
|
94
|
+
group.register(node, config, evts)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
RED.nodes.registerType('ui-switch', SwitchNode)
|
|
98
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
(function () {
|
|
3
|
+
RED.nodes.registerType('ui-table', {
|
|
4
|
+
category: RED._('@flowforge/node-red-dashboard/ui-base:ui-base.label.category'),
|
|
5
|
+
color: RED._('@flowforge/node-red-dashboard/ui-base:ui-base.colors.medium'),
|
|
6
|
+
defaults: {
|
|
7
|
+
group: { type: 'ui-group', required: true },
|
|
8
|
+
name: { value: '' },
|
|
9
|
+
label: { value: 'text' },
|
|
10
|
+
order: { value: 0 },
|
|
11
|
+
width: {
|
|
12
|
+
value: 0,
|
|
13
|
+
validate: function (v) {
|
|
14
|
+
const width = v || 0
|
|
15
|
+
const currentGroup = $('#node-input-group').val() || this.group
|
|
16
|
+
const groupNode = RED.nodes.node(currentGroup)
|
|
17
|
+
const valid = !groupNode || +width <= +groupNode.width
|
|
18
|
+
$('#node-input-size').toggleClass('input-error', !valid)
|
|
19
|
+
return valid
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
height: { value: 0 },
|
|
23
|
+
maxrows: { value: 0, validate: RED.validators.number() },
|
|
24
|
+
autocols: { value: true },
|
|
25
|
+
columns: {
|
|
26
|
+
// value: [{ key: '', label: '' }]
|
|
27
|
+
value: null
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
inputs: 1,
|
|
31
|
+
outputs: 0,
|
|
32
|
+
outputLabels: function () { return this.mode },
|
|
33
|
+
icon: 'font-awesome/fa-table',
|
|
34
|
+
paletteLabel: 'table',
|
|
35
|
+
label: function () {
|
|
36
|
+
return this.name || 'table'
|
|
37
|
+
},
|
|
38
|
+
oneditprepare: function () {
|
|
39
|
+
$('#node-input-size').elementSizer({
|
|
40
|
+
width: '#node-input-width',
|
|
41
|
+
height: '#node-input-height',
|
|
42
|
+
group: '#node-input-group'
|
|
43
|
+
})
|
|
44
|
+
const autocols = $('#node-input-autocols')
|
|
45
|
+
|
|
46
|
+
$(autocols).change(() => {
|
|
47
|
+
const val = autocols.is(':checked')
|
|
48
|
+
const customColsDiv = $('#node-input-columns-container')
|
|
49
|
+
if (val) {
|
|
50
|
+
customColsDiv.hide()
|
|
51
|
+
} else {
|
|
52
|
+
customColsDiv.show()
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// Columns Editor
|
|
57
|
+
function generateColumn (i, col) {
|
|
58
|
+
const container = $('<li/>', { style: 'background: var(--red-ui-secondary-background, #fff); margin:0; padding:8px 0px 0px; border-bottom: 1px solid var(--red-ui-form-input-border-color, #ccc);' })
|
|
59
|
+
const row = $('<div/>').appendTo(container)
|
|
60
|
+
$('<div/>', { style: 'padding-top:5px; padding-left:175px;' }).appendTo(container)
|
|
61
|
+
$('<div/>', { style: 'padding-top:5px; padding-left:120px;' }).appendTo(container)
|
|
62
|
+
|
|
63
|
+
$('<i style="color: var(--red-ui-form-text-color, #eee); cursor:move; margin-left:3px;" class="node-input-column-handle fa fa-bars"></i>').appendTo(row)
|
|
64
|
+
|
|
65
|
+
$('<input/>', { class: 'node-input-column-key', type: 'text', style: 'margin-left:7px; width:calc(50% - 32px);', placeholder: 'Key', value: col.key }).appendTo(row)
|
|
66
|
+
$('<input/>', { class: 'node-input-column-label', type: 'text', style: 'margin-left:7px; width:calc(50% - 32px);', placeholder: 'Label', value: col.label }).appendTo(row)
|
|
67
|
+
|
|
68
|
+
const finalSpan = $('<span/>', { style: 'float:right; margin-right:8px;' }).appendTo(row)
|
|
69
|
+
const deleteButton = $('<a/>', { href: '#', class: 'editor-button editor-button-small', style: 'margin-top:7px; margin-left:5px;' }).appendTo(finalSpan)
|
|
70
|
+
$('<i/>', { class: 'fa fa-remove' }).appendTo(deleteButton)
|
|
71
|
+
|
|
72
|
+
deleteButton.click(function () {
|
|
73
|
+
container.css({ background: 'var(--red-ui-secondary-background-inactive, #fee)' })
|
|
74
|
+
container.fadeOut(300, function () {
|
|
75
|
+
$(this).remove()
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
$('#node-input-column-container').append(container)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
$('#node-input-add-column').click(function () {
|
|
83
|
+
generateColumn($('#node-input-column-container').children().length + 1, {})
|
|
84
|
+
$('#node-input-column-container-div').scrollTop($('#node-input-column-column-div').get(0).scrollHeight)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
for (let i = 0; i < this.columns?.length; i++) {
|
|
88
|
+
const col = this.columns[i]
|
|
89
|
+
generateColumn(i + 1, col)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
$('#node-input-column-container').sortable({
|
|
93
|
+
axis: 'y',
|
|
94
|
+
handle: '.node-input-column-handle',
|
|
95
|
+
cursor: 'move'
|
|
96
|
+
})
|
|
97
|
+
},
|
|
98
|
+
oneditsave: function () {
|
|
99
|
+
const columns = $('#node-input-column-container').children()
|
|
100
|
+
const node = this
|
|
101
|
+
node.columns = []
|
|
102
|
+
columns.each(function (i) {
|
|
103
|
+
const column = $(this)
|
|
104
|
+
const o = {
|
|
105
|
+
label: column.find('.node-input-column-label').val(),
|
|
106
|
+
key: column.find('.node-input-column-key').val()
|
|
107
|
+
}
|
|
108
|
+
node.columns.push(o)
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
})()
|
|
113
|
+
</script>
|
|
114
|
+
|
|
115
|
+
<script type="text/html" data-template-name="ui-table">
|
|
116
|
+
<div class="form-row">
|
|
117
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
|
|
118
|
+
<input type="text" id="node-input-name">
|
|
119
|
+
</div>
|
|
120
|
+
<div class="form-row">
|
|
121
|
+
<label for="node-input-group"><i class="fa fa-table"></i> Group</label>
|
|
122
|
+
<input type="text" id="node-input-group">
|
|
123
|
+
</div>
|
|
124
|
+
<div class="form-row">
|
|
125
|
+
<label><i class="fa fa-object-group"></i> Size</label>
|
|
126
|
+
<input type="hidden" id="node-input-width">
|
|
127
|
+
<input type="hidden" id="node-input-height">
|
|
128
|
+
<button class="editor-button" id="node-input-size"></button>
|
|
129
|
+
</div>
|
|
130
|
+
<div class="form-row">
|
|
131
|
+
<label for="node-input-maxrows"><i class="fa fa-tag"></i> Max Rows</label>
|
|
132
|
+
<input type="number" id="node-input-maxrows">
|
|
133
|
+
</div>
|
|
134
|
+
<div class="form-row" style="display:flex;">
|
|
135
|
+
<label for="node-input-width" style="vertical-align:top"><i class="fa fa-list-alt"></i> Columns</label>
|
|
136
|
+
<div class="form-row node-input-column-container-row" style="margin-bottom: 0px; width:calc(70% + 15px);">
|
|
137
|
+
<div>
|
|
138
|
+
<input type="checkbox" checked id="node-input-autocols" style="display: inline-block; width: auto; margin: 0px 0px 0px 4px;">
|
|
139
|
+
<label style="width:auto" for="node-input-autocols">Auto Calculate Columns</label>
|
|
140
|
+
</div>
|
|
141
|
+
<div id="node-input-columns-container">
|
|
142
|
+
<div id="node-input-column-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: 100%;">
|
|
143
|
+
<ol id="node-input-column-container" style="list-style-type:none; margin:0;"></ol>
|
|
144
|
+
</div>
|
|
145
|
+
<a href="#" class="editor-button editor-button-small" id="node-input-add-column" style="margin-top:4px;"><i class="fa fa-plus"></i> <span>column</span></a>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</script>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module.exports = function (RED) {
|
|
2
|
+
function TableNode (config) {
|
|
3
|
+
const node = this
|
|
4
|
+
|
|
5
|
+
// create node in Node-RED
|
|
6
|
+
RED.nodes.createNode(this, config)
|
|
7
|
+
|
|
8
|
+
// which group are we rendering this widget
|
|
9
|
+
const group = RED.nodes.getNode(config.group)
|
|
10
|
+
|
|
11
|
+
// inform the dashboard UI that we are adding this node
|
|
12
|
+
group.register(node, config)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
RED.nodes.registerType('ui-table', TableNode)
|
|
16
|
+
}
|