Skip to content

Commit f916e48

Browse files
committed
focus on new button-bar widget
1 parent 988a84f commit f916e48

File tree

6 files changed

+189
-37
lines changed

6 files changed

+189
-37
lines changed

README.md

+7-31
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,10 @@
1-
node-red-fd-testbutton
2-
===============================
1+
node-red-fd-testnodes
2+
======================
33

4-
A few Node-RED dashboard FlexDash nodes to test simple UI nodes implemented with FlexDash.
4+
A collection of Node-RED test nodes for FlexDash in various states of working and non-working...
55

6-
## How it works...
6+
## Installation
77

8-
When a Node-RED FlexDash UI node is instantiated it calls `initWidget`, which establishes a 1-1 relationship with a widget shown in the dashboard.
9-
The first time the node is instantiated the widget has to be created, but later, e.g. on redeploy
10-
or Node-RED restarts, the existing widget is located.
11-
This happens by storing the ID of the "owning" node in the widget's config.
12-
13-
FlexDash widgets have a bunch of params (equivalent to config fields), for example, title, color,
14-
text, value, etc.
15-
Each param can be given a so-called static value or a dynamically linked value:
16-
17-
- A static value is stored in the FlexDash config json and is part of what is sent to a new
18-
browser that connects so it can initialize the widgets to be shown.
19-
- A dynamic value is stored in a JSON data tree that is sent to the browser and is updated
20-
as values in the tree are changing (kind'of a reactive observer paradigm across server and browser).
21-
If a param is to receive a dynamic value, then a link into the JSON data tree is stored in
22-
the config.
23-
24-
The way FlexDash UI nodes can deal with config and values is more flexible than standard UI nodes.
25-
This is because FlexDash does not distinguish between config and data. Everything is a param.
26-
Many params are simple values, such a title or color, but some params may contain complex data
27-
structures, such as the data to be plotted in a chart.
28-
29-
Each widget param can be given a static value in the NR flow editor's config panel.
30-
This is stored in the config.
31-
But an input message can override any of the params too. In that case, the config is altered to point
32-
"somewhere" in the JSON data tree and the value in the message is stored there.
33-
Unless the input message's value is null, in that case the link in the config is removed and the
34-
widget will use the static value again.
8+
Some nodes rely on the node generator in `node-red-flexdash`, which is expected to be
9+
in a sibling directory.
10+
After that `./generate.sh` should do the trick...

button-bar.html

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<!-- Node-RED editor code for the FlexDash ButtonBar widget -->
2+
3+
<script type="module">
4+
// fetch the widget info: a bunch of JSON produced by gen-widget-node.js
5+
import widgetInfo from './resources/@flexdash/node-red-fd-testnodes/button-bar-info.js'
6+
7+
// construct defaults for the node, setting all but bool to null so widget defaults happen
8+
var widgetDefaults = Object.fromEntries(Object.values(widgetInfo.props).map(p =>
9+
[p.name, {value: p.default}]
10+
))
11+
//console.log(widgetInfo.name, "widgetDefaults", widgetDefaults)
12+
13+
// construct the palette label
14+
var paletteLabel = widgetInfo.name_text.toLocaleLowerCase()
15+
16+
// update the text for the help sidebar
17+
var helpHtml = flexdash.load_help_html(widgetInfo, paletteLabel)
18+
19+
flexdash.insert_general_edit("fd-button-bar", "fd-tab-general")
20+
21+
22+
RED.nodes.registerType(widgetInfo.type_kebab, {
23+
// properties of the NR node used by NR itself
24+
category: 'flexdash',
25+
color: "#F0E4B8",
26+
inputs: 1,
27+
outputs: widgetInfo.output ? 1 : 0,
28+
icon: "font-awesome/fa-rocket", // icon in flow editor
29+
paletteLabel,
30+
31+
// node config properties that the user can edit in the flow editor
32+
defaults: {
33+
// required fields for FlexDash Widget nodes
34+
fd_container: {
35+
type: "flexdash container", value: "", required: true, // grid/panel
36+
validate: flexdash.validate_widget_container },
37+
fd_cols: { value: 1, validate(v) { return v>0 && v<=20 } }, // widget width
38+
fd_rows: { value: 1, validate(v) { return v>0 && v<=100 } }, // widget height
39+
fd_array: { value: false }, // create array of this widget
40+
fd_array_max: { value: 10, validate(v) { return v>0 && v<1000} }, // max array size
41+
// name field of Node-RED node (standard)
42+
name: { value: '' },
43+
44+
// widget fields, i.e. values being transmitted to the FlexDash Widget as props
45+
...widgetDefaults,
46+
},
47+
48+
// Node-RED node label
49+
label() {
50+
const lbl = this.name || paletteLabel
51+
return this.fd_array ? lbl+'[ ]' : lbl // indicate whether this is an array-widget
52+
},
53+
labelStyle() { return this.name ? "node_label_italic" : "" },
54+
55+
oneditprepare() {
56+
// fetch the html to edit the widget props and insert into DOM
57+
flexdash.load_props_edit(this, widgetInfo, "fd-tab-props")
58+
// select last-used container
59+
flexdash.select_last_container()
60+
61+
// manage tabs
62+
const tabs = RED.tabs.create({id: "fd-tabs", onchange: function(tab) {
63+
$("#fd-tabs-content").children().hide()
64+
$("#" + tab.id).show()
65+
RED.tray.resize()
66+
}})
67+
tabs.addTab({ id: "fd-tab-general", label: "General" })
68+
tabs.addTab({ id: "fd-tab-props", label: "Widget props" })
69+
tabs.activateTab(this.fd_container ? "fd-tab-props" : "fd-tab-general")
70+
},
71+
})
72+
</script>
73+
74+
<script type="text/html" data-template-name="fd-button-bar">
75+
<div class="form-row">
76+
<ul id="fd-tabs" style="min-width: 600px; margin-bottom: 20px;"></ul>
77+
</div>
78+
<div id="fd-tabs-content" style="min-height: calc(100% - 95px);">
79+
<!-- general properties tab -->
80+
<div id="fd-tab-general" style="display:none"><!-- dynamically loaded --></div>
81+
<!-- widget props -->
82+
<div id="fd-tab-props" style="display:none"><!-- dynamically loaded --></div>
83+
</div>
84+
<!-- non-tab alternative, remove all above, remove 'manage tabs' code, and use this:
85+
<div id="fd-tab-general" style="display:contents"></div>
86+
<div id="fd-tab-props" style="display:contents"></div>
87+
-->
88+
</script>
89+
90+
<script type="text/markdown" data-help-name="fd-button-bar">
91+
<p id="fd-button-bar-help-text"><i>Ooops, this should have filled-in dynamically...</i></p>
92+
</script>

generate.sh

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#! /bin/bash -e
2+
for f in $(cd widgets; echo *.vue); do
3+
b=$(basename $f .vue)
4+
rm -f resources/$b-* $b.js $b.html
5+
done
6+
node ../node-red-flexdash/gen-widget-nodes.js

package.json

+8-5
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,17 @@
2020
"url": "git+https://github.com/flexdash/node-red-fd-testnodes.git"
2121
},
2222
"node-red": {
23-
"version": "^2.0.0",
23+
"version": ">2.0.0",
2424
"nodes": {
25-
"flexdash testbutton": "testbutton.js",
26-
"flexdash testgauge": "testgauge.js",
27-
"flexdash testcustom": "testcustom.js",
28-
"foo": "inputtype.js"
25+
"flexdash button-bar": "button-bar.js"
2926
}
3027
},
28+
"node-red-unused": {
29+
"flexdash testbutton": "testbutton.js",
30+
"flexdash testgauge": "testgauge.js",
31+
"flexdash testcustom": "testcustom.js",
32+
"foo": "inputtype.js"
33+
},
3134
"engines": {
3235
"node": ">=12.0.0"
3336
},

widgets/button-bar.vue

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<!-- PushButton - Simple button that sends a message when clicked
2+
Copyright ©2021 Thorsten von Eicken, MIT license, see LICENSE file
3+
-->
4+
<template>
5+
<div class="button-bar d-flex align-center justify-center">
6+
<v-defaults-provider :defaults="density">
7+
<v-btn-toggle :model-value="toggle" @update:modelValue="change" class="ma-auto" max-width="95%" divided
8+
variant="outlined" density="default">
9+
<v-btn v-for="(btn,ix) in buttons" v-bind:key="ix" v-bind="btn_bindings[ix]">
10+
<v-icon v-if="btn.icon" :size="btn.label ? 'default' : 'large'" class="mr-1" :icon="btn.icon" />
11+
<div class="d-flex flex-column">
12+
<span v-if="btn.label">{{ btn.label }}</span>
13+
<span v-if="btn.line2 && !btn.line2_color">{{ btn.line2 }}</span>
14+
<v-chip v-if="btn.line2 && btn.line2_color">{{ btn.line2 }}</v-chip>
15+
</div>
16+
</v-btn>
17+
</v-btn-toggle>
18+
</v-defaults-provider>
19+
</div>
20+
</template>
21+
22+
<style scoped>
23+
.button-bar { height: 100%; }
24+
</style>
25+
26+
<script scoped>
27+
export default {
28+
name: 'ButtonBar',
29+
30+
help: `Bar of buttons.
31+
Pressing a button sends a message with a specified payload.
32+
Each button may contain an icon and/or a title string and is centered in the widget.
33+
Icon names can be found at https://materialdesignicons.com.`,
34+
35+
props: {
36+
value: { default: null, tip: "index or value of current selected button" },
37+
buttons: { default: [{label:"button 1"}],
38+
tip: "array of buttons with icon, label, color, line2, line2_color, disabled, value" },
39+
},
40+
41+
output: { default: null },
42+
43+
computed: {
44+
45+
// actual bindings passed into v-btn
46+
btn_bindings() {
47+
return this.buttons.map(b => ({
48+
disabled: !!b.disabled,
49+
style: "background-color: " + (b.color || "primary"),
50+
}))
51+
},
52+
53+
density() {
54+
return {
55+
VBtnToggle: {
56+
density: this.buttons.some(b => b.line2) ? "default" : "compact",
57+
},
58+
}
59+
},
60+
61+
},
62+
63+
methods: {
64+
change(ix) {
65+
const btn = this.buttons[ix]
66+
if (btn && 'value' in btn) {
67+
this.$emit('send', btn.value)
68+
} else {
69+
this.$emit('send', ix)
70+
}
71+
},
72+
},
73+
74+
}
75+
</script>

widgets/test-button.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<template>
55
<div class="pushbutton d-flex align-center justify-center">
66
<v-btn large dense class="ma-auto" max-width="95%" v-bind="bindings" @click="clickEv($event)">
7-
<v-icon :large="!title" v-if="icon">mdi-{{icon}}</v-icon> <span>{{ title }}</span>
7+
<v-icon :large="!title" v-if="icon">{{icon}}</v-icon> <span>{{ title }}</span>
88
</v-btn>
99
</div>
1010
</template>

0 commit comments

Comments
 (0)