|
[ |
|
{ |
|
"id": "1481aa313159afc7", |
|
"type": "group", |
|
"z": "b0c52a6577bbe256", |
|
"style": { |
|
"stroke": "#999999", |
|
"stroke-opacity": "1", |
|
"fill": "none", |
|
"fill-opacity": "1", |
|
"label": true, |
|
"label-position": "nw", |
|
"color": "#a4a4a4" |
|
}, |
|
"nodes": [ |
|
"1e515e41121d4036", |
|
"3946fa1639bbc085", |
|
"1a2a0f38b459d802", |
|
"bf89a96744fe95f3", |
|
"b329004938bdffdb", |
|
"69b5f82f6ed9b287", |
|
"de66560d29e43d1a", |
|
"31476c96241456e6", |
|
"c8bf1233797afdc8", |
|
"31d7b25eacbc4886", |
|
"163c586becda91a3", |
|
"74bd72889704a9e9" |
|
], |
|
"x": 18, |
|
"y": 15, |
|
"w": 1134, |
|
"h": 572 |
|
}, |
|
{ |
|
"id": "1e515e41121d4036", |
|
"type": "uibuilder", |
|
"z": "b0c52a6577bbe256", |
|
"g": "1481aa313159afc7", |
|
"name": "", |
|
"topic": "", |
|
"url": "eg-lights", |
|
"okToGo": true, |
|
"fwdInMessages": false, |
|
"allowScripts": false, |
|
"allowStyles": false, |
|
"copyIndex": true, |
|
"templateFolder": "blank", |
|
"extTemplate": "", |
|
"showfolder": false, |
|
"reload": true, |
|
"sourceFolder": "src", |
|
"deployedVersion": "7.5.0", |
|
"showMsgUib": true, |
|
"title": "", |
|
"descr": "", |
|
"editurl": "vscode://file/src/uibRoot/lights25/?windowId=_blank", |
|
"x": 530, |
|
"y": 180, |
|
"wires": [ |
|
[ |
|
"0e05beed20183be4" |
|
], |
|
[ |
|
"1a2a0f38b459d802" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "3946fa1639bbc085", |
|
"type": "debug", |
|
"z": "b0c52a6577bbe256", |
|
"g": "1481aa313159afc7", |
|
"name": "Std output", |
|
"active": true, |
|
"tosidebar": true, |
|
"console": false, |
|
"tostatus": true, |
|
"complete": "true", |
|
"targetType": "full", |
|
"statusVal": "", |
|
"statusType": "counter", |
|
"x": 895, |
|
"y": 240, |
|
"wires": [], |
|
"l": false |
|
}, |
|
{ |
|
"id": "1a2a0f38b459d802", |
|
"type": "debug", |
|
"z": "b0c52a6577bbe256", |
|
"g": "1481aa313159afc7", |
|
"name": "Ctrl output", |
|
"active": false, |
|
"tosidebar": true, |
|
"console": false, |
|
"tostatus": true, |
|
"complete": "true", |
|
"targetType": "full", |
|
"statusVal": "", |
|
"statusType": "counter", |
|
"x": 675, |
|
"y": 240, |
|
"wires": [], |
|
"l": false |
|
}, |
|
{ |
|
"id": "bf89a96744fe95f3", |
|
"type": "comment", |
|
"z": "b0c52a6577bbe256", |
|
"g": "1481aa313159afc7", |
|
"name": "uibuilder Dynamic SVG - Vanilla HTML, no framework needed. \\n Updated version of old example that used VueJS \\n Read this comment for details. Requires uibuilder v6.1 or above", |
|
"info": "This example demonstrates how to use uibuilder\nwith SVG images to create a dynamic home\nlighting dashboard.\n\nUse a background-image (see index.css) and\nthen clone the included \"bulb\" SVG \n(see index.html `<template>`) and change the\nproperties of each bulb using uibuilder's \nreduced-code functions. Colours, size, position,\netc are all controlled by CSS classes.\n\n## To use the example\n\nAs always deploy your flow after adding a\nuibuilder node, making sure the url setting \nis unique.\n\nThen update the `index.html`, `index.js` and \n`index.css` files with the code provided.\n\nOpen the resulting page and play with\ncontrolling from Node-RED and try clicking\non the bulb symbols on the page to see \nhow all the interactions work.\n\nThis example demonstrates a hybrid way of \nworking with uibuilder to create web pages.\n\nThere is some code and some Node-RED flow.\n\nHopefully, this illustrates how a little code \ncan go a long way and that you are not \nconstrained to use just one approach but can \nmix and match as desired.", |
|
"x": 280, |
|
"y": 80, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "b329004938bdffdb", |
|
"type": "inject", |
|
"z": "b0c52a6577bbe256", |
|
"g": "1481aa313159afc7", |
|
"name": "Toggle Visible Msgs", |
|
"props": [ |
|
{ |
|
"p": "_uib", |
|
"v": "{\"command\":\"showMsg\"}", |
|
"vt": "json" |
|
} |
|
], |
|
"repeat": "", |
|
"crontab": "", |
|
"once": false, |
|
"onceDelay": 0.1, |
|
"topic": "", |
|
"x": 180, |
|
"y": 200, |
|
"wires": [ |
|
[ |
|
"31476c96241456e6" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "69b5f82f6ed9b287", |
|
"type": "debug", |
|
"z": "b0c52a6577bbe256", |
|
"g": "1481aa313159afc7", |
|
"name": "Std output", |
|
"active": true, |
|
"tosidebar": true, |
|
"console": false, |
|
"tostatus": true, |
|
"complete": "true", |
|
"targetType": "full", |
|
"statusVal": "", |
|
"statusType": "counter", |
|
"x": 925, |
|
"y": 80, |
|
"wires": [], |
|
"l": false |
|
}, |
|
{ |
|
"id": "de66560d29e43d1a", |
|
"type": "link in", |
|
"z": "b0c52a6577bbe256", |
|
"g": "1481aa313159afc7", |
|
"name": "link in 10", |
|
"links": [ |
|
"ef04eafde42be240", |
|
"31476c96241456e6" |
|
], |
|
"x": 375, |
|
"y": 180, |
|
"wires": [ |
|
[ |
|
"1e515e41121d4036" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "31476c96241456e6", |
|
"type": "link out", |
|
"z": "b0c52a6577bbe256", |
|
"g": "1481aa313159afc7", |
|
"name": "link out 35", |
|
"mode": "link", |
|
"links": [ |
|
"de66560d29e43d1a" |
|
], |
|
"x": 455, |
|
"y": 260, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "c8bf1233797afdc8", |
|
"type": "group", |
|
"z": "b0c52a6577bbe256", |
|
"g": "1481aa313159afc7", |
|
"name": "Control lights from Node-RED", |
|
"style": { |
|
"fill": "#ffbfbf", |
|
"fill-opacity": "0.23", |
|
"label": true, |
|
"color": "#000000" |
|
}, |
|
"nodes": [ |
|
"6fac50f7e0c1db4f", |
|
"41d35214d01be24c", |
|
"aa3b1273b3df766f", |
|
"2ba26376f0407a8c", |
|
"44e0ba53674dc4ac", |
|
"c79c9e1f35d5b85c" |
|
], |
|
"x": 44, |
|
"y": 239, |
|
"w": 302, |
|
"h": 242 |
|
}, |
|
{ |
|
"id": "6fac50f7e0c1db4f", |
|
"type": "inject", |
|
"z": "b0c52a6577bbe256", |
|
"g": "c8bf1233797afdc8", |
|
"name": "LIGHTS (random)", |
|
"props": [ |
|
{ |
|
"p": "topic", |
|
"vt": "str" |
|
} |
|
], |
|
"repeat": "", |
|
"crontab": "", |
|
"once": false, |
|
"onceDelay": 0.1, |
|
"topic": "LIGHTS", |
|
"x": 180, |
|
"y": 280, |
|
"wires": [ |
|
[ |
|
"41d35214d01be24c" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "41d35214d01be24c", |
|
"type": "change", |
|
"z": "b0c52a6577bbe256", |
|
"g": "c8bf1233797afdc8", |
|
"name": "Randomly turn on/off all bulbs", |
|
"rules": [ |
|
{ |
|
"t": "set", |
|
"p": "_ui", |
|
"pt": "msg", |
|
"to": "(\t /* Generate a true/false for each bulb */\t $b1 := $random() >= 0.5;\t $b2 := $random() >= 0.5;\t $b3 := $random() >= 0.5;\t $b4 := $random() >= 0.5;\t /* Apply to msg._ui to randomly update all bulbs */\t [\t {\t \"method\":\"update\",\t \"components\": [\t {\t \"id\":\"bulb1\",\t \"attributes\": {\t /* NB: Give this one a different colour to the others */\t \"class\":\"bulb posn1 \" & ($b1 ? \"bulb-fail\" : \"\"),\t /* We use a data attribute to make it easier to track on/off state */\t \"data-state\": ($b1 ? \"on\" : \"off\")\t }\t }\t ]\t },\t {\t \"method\":\"update\",\t \"components\": [\t {\t \"id\":\"bulb2\",\t \"attributes\": {\t \"class\":\"bulb posn2 \" & ($b2 ? \"bulb-warn\" : \"\"),\t \"data-state\": ($b2 ? \"on\" : \"off\")\t }\t }\t ]\t },\t {\t \"method\":\"update\",\t \"components\": [\t {\t \"id\":\"bulb3\",\t \"attributes\": {\t \"class\":\"bulb posn3 \" & ($b3 ? \"bulb-warn\" : \"\"),\t \"data-state\": ($b3 ? \"on\" : \"off\")\t }\t }\t ]\t },\t {\t \"method\":\"update\",\t \"components\": [\t {\t \"id\":\"bulb4\",\t \"attributes\": {\t \"class\":\"bulb posn4 \" & ($b4 ? \"bulb-warn\" : \"\"),\t \"data-state\": ($b4 ? \"on\" : \"off\")\t }\t }\t ]\t }\t ]\t)", |
|
"tot": "jsonata" |
|
} |
|
], |
|
"action": "", |
|
"property": "", |
|
"from": "", |
|
"to": "", |
|
"reg": false, |
|
"x": 305, |
|
"y": 280, |
|
"wires": [ |
|
[ |
|
"31476c96241456e6" |
|
] |
|
], |
|
"l": false |
|
}, |
|
{ |
|
"id": "aa3b1273b3df766f", |
|
"type": "inject", |
|
"z": "b0c52a6577bbe256", |
|
"g": "c8bf1233797afdc8", |
|
"name": "LIGHT 1 on", |
|
"props": [ |
|
{ |
|
"p": "_ui", |
|
"v": "[{\"method\":\"update\",\"components\":[{\"id\":\"bulb1\",\"attributes\":{\"class\":\"bulb posn1 bulb-fail\",\"data-state\":\"on\"}}]}]", |
|
"vt": "json" |
|
}, |
|
{ |
|
"p": "topic", |
|
"vt": "str" |
|
} |
|
], |
|
"repeat": "", |
|
"crontab": "", |
|
"once": false, |
|
"onceDelay": 0.1, |
|
"topic": "LIGHT-1", |
|
"x": 200, |
|
"y": 320, |
|
"wires": [ |
|
[ |
|
"31476c96241456e6" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "2ba26376f0407a8c", |
|
"type": "inject", |
|
"z": "b0c52a6577bbe256", |
|
"g": "c8bf1233797afdc8", |
|
"name": "LIGHT 1 off", |
|
"props": [ |
|
{ |
|
"p": "_ui", |
|
"v": "[{\"method\":\"update\",\"components\":[{\"id\":\"bulb1\",\"attributes\":{\"class\":\"bulb posn1\",\"data-state\":\"off\"}}]}]", |
|
"vt": "json" |
|
}, |
|
{ |
|
"p": "topic", |
|
"vt": "str" |
|
} |
|
], |
|
"repeat": "", |
|
"crontab": "", |
|
"once": false, |
|
"onceDelay": 0.1, |
|
"topic": "LIGHT-1", |
|
"x": 200, |
|
"y": 360, |
|
"wires": [ |
|
[ |
|
"31476c96241456e6" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "44e0ba53674dc4ac", |
|
"type": "inject", |
|
"z": "b0c52a6577bbe256", |
|
"g": "c8bf1233797afdc8", |
|
"name": "LIGHT 2 on", |
|
"props": [ |
|
{ |
|
"p": "_ui", |
|
"v": "[{\"method\":\"update\",\"components\":[{\"id\":\"bulb2\",\"attributes\":{\"class\":\"bulb posn2 bulb-warn\",\"data-state\":\"on\"}}]}]", |
|
"vt": "json" |
|
}, |
|
{ |
|
"p": "topic", |
|
"vt": "str" |
|
} |
|
], |
|
"repeat": "", |
|
"crontab": "", |
|
"once": false, |
|
"onceDelay": 0.1, |
|
"topic": "LIGHT-2", |
|
"x": 200, |
|
"y": 400, |
|
"wires": [ |
|
[ |
|
"31476c96241456e6" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "c79c9e1f35d5b85c", |
|
"type": "inject", |
|
"z": "b0c52a6577bbe256", |
|
"g": "c8bf1233797afdc8", |
|
"name": "LIGHT 2 off", |
|
"props": [ |
|
{ |
|
"p": "_ui", |
|
"v": "[{\"method\":\"update\",\"components\":[{\"id\":\"bulb2\",\"attributes\":{\"class\":\"bulb posn2\",\"data-state\":\"off\"}}]}]", |
|
"vt": "json" |
|
}, |
|
{ |
|
"p": "topic", |
|
"vt": "str" |
|
} |
|
], |
|
"repeat": "", |
|
"crontab": "", |
|
"once": false, |
|
"onceDelay": 0.1, |
|
"topic": "LIGHT-2", |
|
"x": 200, |
|
"y": 440, |
|
"wires": [ |
|
[ |
|
"31476c96241456e6" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "31d7b25eacbc4886", |
|
"type": "group", |
|
"z": "b0c52a6577bbe256", |
|
"g": "1481aa313159afc7", |
|
"name": "User clicks turn on/off", |
|
"style": { |
|
"fill": "#e3f3d3", |
|
"fill-opacity": "0.26", |
|
"label": true, |
|
"color": "#000000" |
|
}, |
|
"nodes": [ |
|
"0e05beed20183be4", |
|
"d0283601f7749ca5", |
|
"ef04eafde42be240" |
|
], |
|
"x": 684, |
|
"y": 111.5, |
|
"w": 252, |
|
"h": 97 |
|
}, |
|
{ |
|
"id": "0e05beed20183be4", |
|
"type": "switch", |
|
"z": "b0c52a6577bbe256", |
|
"g": "31d7b25eacbc4886", |
|
"name": "Switch out bulb clicks", |
|
"property": "payload.state", |
|
"propertyType": "msg", |
|
"rules": [ |
|
{ |
|
"t": "eq", |
|
"v": "on", |
|
"vt": "str" |
|
}, |
|
{ |
|
"t": "eq", |
|
"v": "off", |
|
"vt": "str" |
|
}, |
|
{ |
|
"t": "else" |
|
} |
|
], |
|
"checkall": "false", |
|
"repair": false, |
|
"outputs": 3, |
|
"x": 725, |
|
"y": 160, |
|
"wires": [ |
|
[ |
|
"d0283601f7749ca5" |
|
], |
|
[ |
|
"d0283601f7749ca5" |
|
], |
|
[ |
|
"3946fa1639bbc085" |
|
] |
|
], |
|
"outputLabels": [ |
|
"A bulb is currently ON", |
|
"A bulb is currently OFF", |
|
"Anything else" |
|
], |
|
"l": false |
|
}, |
|
{ |
|
"id": "d0283601f7749ca5", |
|
"type": "change", |
|
"z": "b0c52a6577bbe256", |
|
"g": "31d7b25eacbc4886", |
|
"name": "Toggle clicked bulb state", |
|
"rules": [ |
|
{ |
|
"t": "set", |
|
"p": "_ui", |
|
"pt": "msg", |
|
"to": "(\t [\t {\t \"method\":\"update\",\t \"components\":[\t {\t \"id\": _ui.id,\t \"attributes\":{\t /* Toggle fill colour */\t \"class\":\"bulb \" & payload.posn & \" \" & (payload.state = \"off\" ? \"bulb-warn\" : \"\"),\t /* Toggle state */\t \"data-state\": (payload.state = \"on\" ? \"off\" : \"on\")\t }\t }\t ]\t }\t]\t)", |
|
"tot": "jsonata" |
|
} |
|
], |
|
"action": "", |
|
"property": "", |
|
"from": "", |
|
"to": "", |
|
"reg": false, |
|
"x": 815, |
|
"y": 160, |
|
"wires": [ |
|
[ |
|
"69b5f82f6ed9b287", |
|
"ef04eafde42be240" |
|
] |
|
], |
|
"l": false |
|
}, |
|
{ |
|
"id": "ef04eafde42be240", |
|
"type": "link out", |
|
"z": "b0c52a6577bbe256", |
|
"g": "31d7b25eacbc4886", |
|
"name": "link out 34", |
|
"mode": "link", |
|
"links": [ |
|
"de66560d29e43d1a" |
|
], |
|
"x": 895, |
|
"y": 160, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "163c586becda91a3", |
|
"type": "comment", |
|
"z": "b0c52a6577bbe256", |
|
"g": "1481aa313159afc7", |
|
"name": "Updated: 2025-07-18 (UIBUILDER v7.4.0)", |
|
"info": "# CHANGELOG\n\n## 2025-07-17\n\n* Use `uib-save` node for source code.\n* Tweak `index.js`, `mySelect` no longer needed", |
|
"x": 690, |
|
"y": 60, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "74bd72889704a9e9", |
|
"type": "group", |
|
"z": "b0c52a6577bbe256", |
|
"g": "1481aa313159afc7", |
|
"name": "FE File Setup - Run this first (only needed once)", |
|
"style": { |
|
"label": true, |
|
"fill": "#dbcbe7", |
|
"fill-opacity": "0.28", |
|
"color": "#000000" |
|
}, |
|
"nodes": [ |
|
"2bad1b37fdea840b", |
|
"6061c037039c5d91", |
|
"96e1644c531c87e5", |
|
"ce11be44ade7811d", |
|
"a99f82e84ec51ee7", |
|
"0e30e80f845f28c2", |
|
"b7181750cccc7787", |
|
"dcbd90321ba32bc6", |
|
"54a4acc460189c68", |
|
"a915c85aea05485d", |
|
"6d9c42b7c4525129" |
|
], |
|
"x": 394, |
|
"y": 319, |
|
"w": 732, |
|
"h": 242 |
|
}, |
|
{ |
|
"id": "2bad1b37fdea840b", |
|
"type": "inject", |
|
"z": "b0c52a6577bbe256", |
|
"g": "74bd72889704a9e9", |
|
"name": "", |
|
"props": [ |
|
{ |
|
"p": "topic", |
|
"vt": "str" |
|
} |
|
], |
|
"repeat": "", |
|
"crontab": "", |
|
"once": false, |
|
"onceDelay": 0.1, |
|
"topic": "setup core files", |
|
"x": 455, |
|
"y": 400, |
|
"wires": [ |
|
[ |
|
"6061c037039c5d91", |
|
"ce11be44ade7811d", |
|
"dcbd90321ba32bc6" |
|
] |
|
], |
|
"l": false |
|
}, |
|
{ |
|
"id": "6061c037039c5d91", |
|
"type": "change", |
|
"z": "b0c52a6577bbe256", |
|
"g": "74bd72889704a9e9", |
|
"name": "index.html", |
|
"rules": [ |
|
{ |
|
"t": "set", |
|
"p": "fname", |
|
"pt": "msg", |
|
"to": "index.html", |
|
"tot": "str" |
|
} |
|
], |
|
"action": "", |
|
"property": "", |
|
"from": "", |
|
"to": "", |
|
"reg": false, |
|
"x": 630, |
|
"y": 400, |
|
"wires": [ |
|
[ |
|
"96e1644c531c87e5" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "96e1644c531c87e5", |
|
"type": "template", |
|
"z": "b0c52a6577bbe256", |
|
"g": "74bd72889704a9e9", |
|
"name": "", |
|
"field": "payload", |
|
"fieldType": "msg", |
|
"format": "html", |
|
"syntax": "plain", |
|
"template": "<!doctype html>\n<html lang=\"en\"><head>\n\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <link rel=\"icon\" href=\"../uibuilder/images/node-blue.ico\">\n\n <title>Dynamic SVG Example - Node-RED uibuilder</title>\n <meta name=\"description\" content=\"Node-RED uibuilder - Dynamic SVG Example\">\n\n <!-- Your own CSS (defaults to loading uibuilders css)-->\n <link type=\"text/css\" rel=\"stylesheet\" href=\"./index.css\" media=\"all\">\n\n <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / -->\n <script defer src=\"../uibuilder/uibuilder.iife.min.js\"></script>\n <script defer src=\"./index.js\">/* custom code */</script>\n <!-- #endregion -->\n\n <template id=\"bulb-template\">\n <svg id=\"mybulb\" class=\"bulb\" height=\"3rem\" viewBox=\"0 0 1024 1024\" version=\"1.1\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <filter id=\"shadow\">\n <feDropShadow dx=\"1\" dy=\"1\" stdDeviation=\"5\" flood-opacity=\"50%\" />\n </filter>\n <filter id=\"glow\" filterUnits=\"userSpaceOnUse\" x=\"-50%\" y=\"-50%\" width=\"200%\" height=\"200%\">\n <!-- blur the text at different levels-->\n <feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"5\" result=\"blur5\" />\n <feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"10\" result=\"blur10\" />\n <feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"20\" result=\"blur20\" />\n <feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"30\" result=\"blur30\" />\n <feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"50\" result=\"blur50\" />\n <!-- merge all the blurs except for the first one -->\n <feMerge result=\"blur-merged\">\n <feMergeNode in=\"blur10\" />\n <feMergeNode in=\"blur20\" />\n <feMergeNode in=\"blur30\" />\n <feMergeNode in=\"blur50\" />\n </feMerge>\n <!-- recolour the merged blurs red-->\n <feColorMatrix result=\"red-blur\" in=\"blur-merged\" type=\"matrix\" values=\"1 0 0 0 0\n 0 0.06 0 0 0\n 0 0 0.44 0 0\n 0 0 0 1 0\" />\n <feMerge>\n <!--<feMergeNode in=\"red-blur\"/> largest blurs coloured red -->\n <feMergeNode in=\"blur-merged\" />\n <feMergeNode in=\"blur5\" /> <!-- smallest blur left white -->\n <feMergeNode in=\"SourceGraphic\" /> <!-- original -->\n </feMerge>\n </filter>\n </defs>\n <title>TITLE</title>\n <path name=\"icon\"\n d=\"M511.549861 803.293331H408.419043a73.232959 73.232959 0 0 1-67.1862-41.991375 59.795719 59.795719 0 0 1-6.71862-30.569722 207.60536 207.60536 0 0 0-33.593101-113.88061 196.519637 196.519637 0 0 0-27.882273-33.5931A463.248853 463.248853 0 0 1 217.274302 504.314738a399.086031 399.086031 0 0 1-36.95241-75.248544 242.542184 242.542184 0 0 1-15.116895-77.264131 349.032312 349.032312 0 0 1 8.062344-84.990544 314.76735 314.76735 0 0 1 51.733375-114.888403A367.172586 367.172586 0 0 1 361.724634 34.011334 327.532728 327.532728 0 0 1 433.949799 8.144647 369.524103 369.524103 0 0 1 528.682342 0.418234a333.579486 333.579486 0 0 1 126.310057 29.225997 326.860866 326.860866 0 0 1 70.881442 44.678824A382.625412 382.625412 0 0 1 808.848799 168.383736a314.095488 314.095488 0 0 1 41.991375 105.146403 312.751764 312.751764 0 0 1 6.382689 92.045095 275.799353 275.799353 0 0 1-20.15586 76.256338 449.139751 449.139751 0 0 1-61.139443 107.16199 497.513815 497.513815 0 0 1-33.5931 39.639858 160.575019 160.575019 0 0 0-31.241583 48.038134 215.331773 215.331773 0 0 0-18.812136 55.428615c-1.679655 11.757585 0 23.179239-2.687448 33.5931a171.660742 171.660742 0 0 1-3.695241 25.194826 69.873649 69.873649 0 0 1-33.593101 40.647651 74.576683 74.576683 0 0 1-39.639858 10.07793zM490.050277 88.768088c-11.085723 0-22.171446 2.351517-33.5931 4.031172a210.96467 210.96467 0 0 0-74.240752 26.538549 244.221839 244.221839 0 0 0-55.428616 44.342893 222.386324 222.386324 0 0 0-43.335099 63.82689 230.784599 230.784599 0 0 0-19.483998 94.732543 28.218204 28.218204 0 0 0 33.5931 28.890066 28.890066 28.890066 0 0 0 22.171446-26.202618v-13.773171a167.965501 167.965501 0 0 1 9.406068-49.045927 184.762052 184.762052 0 0 1 64.834684-83.98275 167.965501 167.965501 0 0 1 93.72475-33.593101 142.770676 142.770676 0 0 0 18.140274 0 23.851101 23.851101 0 0 0 19.148067-15.452826 33.5931 33.5931 0 0 0 0-19.483998 23.51517 23.51517 0 0 0-20.491791-18.140274 122.950747 122.950747 0 0 0-15.116895 0zM647.601917 943.040628a15.788757 15.788757 0 0 1-13.773171 15.116895H400.356699a17.468412 17.468412 0 0 1-16.460619-8.734206 18.812136 18.812136 0 0 1 0-20.15586 16.124688 16.124688 0 0 1 16.460619-4.703034h227.089358a19.148067 19.148067 0 0 1 19.148067 20.827722zM405.731595 899.369598a18.140274 18.140274 0 0 1-16.460619-12.765378 17.804343 17.804343 0 0 1 15.452826-23.851101H635.508401a18.812136 18.812136 0 0 1 17.804343 13.773171 19.819929 19.819929 0 0 1-10.749792 21.499584 24.187032 24.187032 0 0 1-8.734206 0H423.535938zM437.64504 1022.992207a17.132481 17.132481 0 0 1-15.452826-9.406068 18.140274 18.140274 0 0 1 15.116895-26.202618h139.411367a19.819929 19.819929 0 0 1 19.483998 17.804343 16.124688 16.124688 0 0 1-8.734206 15.788757 19.148067 19.148067 0 0 1-9.741999 3.023379H442.348074z\" />\n <!-- <circle name=\"default\" cx=\"50\" cy=\"50\" r=\"50\"></circle> -->\n </svg>\n </template>\n\n</head><body>\n\n <h1 class=\"with-subtitle\">Dynamic SVG Example</h1>\n <div role=\"doc-subtitle\">Using the uibuilder IIFE library.</div>\n <p>\n This is a uibuilder example looking at how easy it is to create a dynamic view of IoT devices in a building\n using SVG images both for the background (floor-plan) and device indicators.\n </p>\n\n <div id=\"more\"><!-- '#more' is used as a parent for dynamic HTML content in examples --></div>\n\n <h2>My House, Ground Floor</h2>\n <div id=\"floorplan\" class=\"plan\"><!-- Bulb icons dynamically inserted here --></div>\n\n</body></html>", |
|
"output": "str", |
|
"x": 790, |
|
"y": 400, |
|
"wires": [ |
|
[ |
|
"6d9c42b7c4525129" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "ce11be44ade7811d", |
|
"type": "change", |
|
"z": "b0c52a6577bbe256", |
|
"g": "74bd72889704a9e9", |
|
"name": "index.js", |
|
"rules": [ |
|
{ |
|
"t": "set", |
|
"p": "fname", |
|
"pt": "msg", |
|
"to": "index.js", |
|
"tot": "str" |
|
} |
|
], |
|
"action": "", |
|
"property": "", |
|
"from": "", |
|
"to": "", |
|
"reg": false, |
|
"x": 620, |
|
"y": 440, |
|
"wires": [ |
|
[ |
|
"a99f82e84ec51ee7" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "a99f82e84ec51ee7", |
|
"type": "template", |
|
"z": "b0c52a6577bbe256", |
|
"g": "74bd72889704a9e9", |
|
"name": "", |
|
"field": "payload", |
|
"fieldType": "msg", |
|
"format": "javascript", |
|
"syntax": "plain", |
|
"template": "// @ts-nocheck\n/** Dynamic SVG example */\n\n// uibuilder.logLevel = 1\n\nfunction doMe(event) {\n uibuilder.eventSend(event)\n}\n\n/** Insert a clone of a template tag\n * NB: Template should have only a single direct child tag, nothing other than that tag and its contents will be cloned.\n * @param {HTMLTemplateElement} template The template to clone and insert\n * @param {HTMLElement} parent The parent within which to insert the clone\n * @param {object} [ui] Optional uib UI object that will apply changes to the cloned element (e.g. attribs, slot)\n */\nfunction htmlClone(template, parent, ui) {\n console.log(ui)\n if (!template || !(template instanceof Element)) {\n console.error('Template HTMLElement not provided or is not an HTML Element')\n return\n }\n if (!parent || !(parent instanceof Element)) {\n console.error('Parent HTMLElement not provided or is not an HTML Element')\n return\n }\n if (!ui) ui = {}\n if (!ui.position) ui.position = 'last'\n\n const clone = template.cloneNode(true)\n\n // Oops! Fns starting with `_` should not have been used - sorry. This fn no longer available directly.\n // Will add an equivalent in a future release (post v6.4.1) probably called `uiEnhanceElement`\n // uibuilder._uiComposeComponent(clone, ui)\n clone.id = ui.id\n clone.classList.add(ui.attributes['data-posn'])\n clone.dataset.state = ui.attributes['data-state']\n clone.dataset.posn = ui.attributes['data-posn']\n clone.onclick = doMe //uibuilder.eventSend\n\n if (ui.position === 'first') {\n // Insert new el before the first child of the parent. Ref: https://developer.mozilla.org/en-US/docs/Web/API/Node/insertBefore#example_3\n parent.insertBefore(clone, parent.firstChild)\n } else if (Number.isInteger(Number(ui.position))) {\n elParent.insertBefore(clone, parent.children[ui.position])\n } else {\n // Append to the required parent\n parent.appendChild(clone)\n }\n\n}\nconst x = {\n \"id\": \"bulb1\",\n \"attributes\": {\n \"class\": \"bulb posn1\",\n \"data-state\": \"off\",\n \"data-posn\": \"posn1\"\n },\n \"events\": {\n \"click\": \"uibuilder.eventSend\"\n },\n \"position\": \"last\"\n}\n\n// The Template tag in index.html contains a template \"bulb\" SVG image\n// Here, we clone that multiple times and set some properties.\n// Note that `htmlClone` is a function that will land in the uibuilder client in v6.2\n// Also, the $ function is improved in v6.2 so a copy of that is included here for convenience.\n//\n// We track state and position class on data-* attributes so that it is much easier to process\n// click events in Node-RED without having to create a custom click handler, we can just use the standard eventSend.\n// CSS classes do all the clever stuff 😁\n\nhtmlClone($('#bulb-template'), $('#floorplan'), {\n // As always, we set a unique ID for every created element so it can be updated easily later\n id: 'bulb1',\n // You only need this if you want to choose 'first' or a position number,\n // the clone will be added at the specified child position of the parent.\n // position: 'last', \n attributes: {\n // Apply base bulb class and a positioning class\n class: 'bulb posn1',\n // Track the on/off state separately - makes processing in Node-RED easier\n 'data-state': 'off',\n // Track the position class separately - makes processing in Node-RED easier\n 'data-posn': 'posn1'\n },\n // We have to add event handlers after a clone, they cannot be included in the template\n events: {\n click: 'uibuilder.eventSend'\n }\n})\n\nhtmlClone($('#bulb-template'), $('#floorplan'), {\n id: 'bulb2',\n attributes: {\n class: 'bulb posn2',\n 'data-state': 'off',\n 'data-posn': 'posn2'\n },\n events: {\n click: 'uibuilder.eventSend'\n }\n})\n\nhtmlClone($('#bulb-template'), $('#floorplan'), {\n id: 'bulb3',\n attributes: {\n class: 'bulb posn3',\n 'data-state': 'off',\n 'data-posn': 'posn3'\n },\n events: {\n click: 'uibuilder.eventSend'\n }\n})\n\nhtmlClone($('#bulb-template'), $('#floorplan'), {\n id: 'bulb4',\n attributes: {\n class: 'bulb posn4',\n 'data-state': 'off',\n 'data-posn': 'posn4'\n },\n events: {\n click: 'uibuilder.eventSend'\n },\n})\n", |
|
"output": "str", |
|
"x": 790, |
|
"y": 440, |
|
"wires": [ |
|
[ |
|
"6d9c42b7c4525129" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "0e30e80f845f28c2", |
|
"type": "comment", |
|
"z": "b0c52a6577bbe256", |
|
"g": "74bd72889704a9e9", |
|
"name": "Background Image - See comment text", |
|
"info": "Can't include this directly here otherwise\nthe flow cannot be posted to the forum.\n\nThe example background file can be obtained\nfrom the source GIST:\nhttps://gist.github.com/TotallyInformation/89487d20c635d54d9b98d963656ce429\n\nUse the zip download link:\nhttps://gist.github.com/TotallyInformation/89487d20c635d54d9b98d963656ce429/archive/30b8aae0e04cc36dd67a1c26aa31ffb3f10cff1a.zip\n\nTo get the whole archive and extract the\n`background.svg` file and put it into the same\nlocation as your `index.html` file.\n", |
|
"x": 710, |
|
"y": 520, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "b7181750cccc7787", |
|
"type": "comment", |
|
"z": "b0c52a6577bbe256", |
|
"g": "74bd72889704a9e9", |
|
"name": "Core", |
|
"info": "", |
|
"x": 610, |
|
"y": 360, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "dcbd90321ba32bc6", |
|
"type": "change", |
|
"z": "b0c52a6577bbe256", |
|
"g": "74bd72889704a9e9", |
|
"name": "index.css", |
|
"rules": [ |
|
{ |
|
"t": "set", |
|
"p": "fname", |
|
"pt": "msg", |
|
"to": "index.css", |
|
"tot": "str" |
|
} |
|
], |
|
"action": "", |
|
"property": "", |
|
"from": "", |
|
"to": "", |
|
"reg": false, |
|
"x": 620, |
|
"y": 480, |
|
"wires": [ |
|
[ |
|
"54a4acc460189c68" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "54a4acc460189c68", |
|
"type": "template", |
|
"z": "b0c52a6577bbe256", |
|
"g": "74bd72889704a9e9", |
|
"name": "", |
|
"field": "payload", |
|
"fieldType": "msg", |
|
"format": "css", |
|
"syntax": "plain", |
|
"template": "/* Load defaults from `<userDir>/node_modules/node-red-contrib-uibuilder/front-end/uib-brand.css`\n * This version auto-adjusts for light/dark browser settings but might not be as complete.\n */\n@import url(\"../uibuilder/uib-brand.css\");\n\n/* These variables build on existing variables in uib-brand.css\n * They will be incorporated into that file in uibuilder v6.2\n */\n:root {\n --warning-intense: hsl(var(--warning-hue) 100% 50%);\n --failure-intense: hsl(var(--failure-hue) 100% 50%);\n --surface5: hsl( /* additional background shade */\n var(--brand-hue)\n calc(100% * var(--surfaces-saturation))\n calc(\n 100% * (var(--surfaces-lightness)\n - (var(--surfaces-factor) * .20)\n + (var(--surfaces-factor) * var(--surfaces-bias))))\n );\n}\n\n\n/* Bulb classes control look, colour and position */\n\n.bulb {\n /* Default \"off\" class plus standard style */\n z-index: 9999 !important;\n /* Bulbs HAVE to be in the top z-layer */\n cursor: pointer;\n position: absolute;\n /* allows exact positioning within the parent div */\n transition: filter 2s ease -in -out 0s;\n background-color: rgba(0, 0, 0, 0.001);\n /* transparent background */\n filter: url(\"#shadow\");\n /* selects the shadow filter */\n}\n\n.bulb path {\n fill: grey;\n}\n\n.bulb-warn {\n /* Standard \"on\" class */\n filter: url('#glow');\n /* selects the glow filter instead of shadow */\n}\n\n.bulb-warn path {\n fill: var(--warning-intense);\n}\n\n.bulb-fail {\n /* Alternative \"on\" class with different colour */\n filter: url('#glow');\n}\n\n.bulb-fail path {\n fill: var(--failure-intense);\n}\n\n/* Bulb position classes, change as needed\n * Positions are relative to the parent floorplan div\n */\n.posn1 {\n top: 100px;\n left: 100px;\n}\n\n.posn2 {\n top: 120px;\n left: 270px;\n}\n\n.posn3 {\n top: 120px;\n left: 650px;\n}\n\n.posn4 {\n top: 270px;\n left: 250px;\n}\n\n/* floorplan div class change anything\n * except the position:relative.\n * The background image location is relative\n * to your uibuilder front-end files.\n */\n.plan {\n position: relative;\n width: 99%;\n height: 30rem;\n background: url(\"./background.svg\");\n background-color: var(--surface5);\n}\n", |
|
"output": "str", |
|
"x": 790, |
|
"y": 480, |
|
"wires": [ |
|
[ |
|
"6d9c42b7c4525129" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "a915c85aea05485d", |
|
"type": "uib-save", |
|
"z": "b0c52a6577bbe256", |
|
"g": "74bd72889704a9e9", |
|
"url": "eg-lights", |
|
"uibId": "1e515e41121d4036", |
|
"folder": "src", |
|
"fname": "", |
|
"createFolder": false, |
|
"reload": true, |
|
"usePageName": true, |
|
"encoding": "utf8", |
|
"mode": 438, |
|
"name": "", |
|
"topic": "", |
|
"x": 1040, |
|
"y": 400, |
|
"wires": [] |
|
}, |
|
{ |
|
"id": "6d9c42b7c4525129", |
|
"type": "junction", |
|
"z": "b0c52a6577bbe256", |
|
"g": "74bd72889704a9e9", |
|
"x": 950, |
|
"y": 400, |
|
"wires": [ |
|
[ |
|
"a915c85aea05485d" |
|
] |
|
] |
|
}, |
|
{ |
|
"id": "6064509c7f4a6337", |
|
"type": "global-config", |
|
"env": [], |
|
"modules": { |
|
"node-red-contrib-uibuilder": "7.5.0" |
|
} |
|
} |
|
] |
Hi, I don't know if I am too dumb to use this, but downloading the zip file just downloads the README and the flow. I cannot find the svg file anywhere (finally pulled it from the older version - https://gist.githubusercontent.com/TotallyInformation/02eb3716157db586f3f5b8a85c241009/raw/d8eec4763cde734751910684aa53858233e244be/background.svg). And it seems the template creator does not work for me. I copied the sources by hand, then it worked. Thanks for your awesome work!