Hi all, little mini FAQ about using MermaidJS to generate browser-based charts with data from Node-RED and with the support of UIBUILDER.
You will see some chat here: StateTrail-Node for Dashboard 2.0 - about using Mermaid for a timeline chart. It supports many other chart types too.
Mermaid takes TEXT chart descriptions and turns them into graphics. Usually it is used as an extension along side some tool to convert Markdown to HTML. If I get time, I'll add more info to this FAQ to show how to do that as well since UIBUILDER supports Markdown quite readily. But initially, I'll just show you 1 way to use the Mermaid library directly.
UIBUILDER progressively enhances native web tooling and so supports any web library.
[{"id":"65bbdbc1a8301e7b","type":"uibuilder","z":"1b2ed12a321034dc","name":"","topic":"","url":"mermaid-timeline","okToGo":true,"fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"templateFolder":"esm-blank-client","extTemplate":"","showfolder":false,"reload":true,"sourceFolder":"src","deployedVersion":"7.1.0","showMsgUib":true,"title":"","descr":"","editurl":"vscode://file/src/uibRoot/mermaid-timeline/?windowId=_blank","x":400,"y":340,"wires":[["a4004d1d51fe87d4"],[]]},{"id":"0980fae560b35fd6","type":"debug","z":"1b2ed12a321034dc","name":"uibuilder standard output","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"","statusType":"counter","x":703.0000305175781,"y":386.9999895095825,"wires":[],"l":false},{"id":"f4a8f0029f21b813","type":"link in","z":"1b2ed12a321034dc","name":"in to uib","links":["1e40e8ac1c2ee858"],"x":245,"y":340,"wires":[["65bbdbc1a8301e7b"]]},{"id":"a4004d1d51fe87d4","type":"switch","z":"1b2ed12a321034dc","name":"","property":"_ui.id","propertyType":"msg","rules":[{"t":"eq","v":"btn1","vt":"str"},{"t":"eq","v":"btn2","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":3,"x":590,"y":340,"wires":[["562c9d4ecec8efde"],["53ea9de0b8b3528a"],["0980fae560b35fd6"]]},{"id":"1e40e8ac1c2ee858","type":"link out","z":"1b2ed12a321034dc","name":"out to uib","mode":"link","links":["f4a8f0029f21b813"],"x":1045,"y":320,"wires":[]},{"id":"4571a4fe83cfd5b5","type":"template","z":"1b2ed12a321034dc","name":"index.html","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","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>Markdown-Mermaid Example - Node-RED uibuilder</title>\n <meta name=\"description\" content=\"Node-RED uibuilder - Markdown-Mermaid Example\">\n\n <!-- Your own CSS -->\n <link type=\"text/css\" rel=\"stylesheet\" href=\"./index.css\" media=\"all\">\n\n <script defer src=\"https://cdn.jsdelivr.net/npm/mermaid@11.4.1/dist/mermaid.min.js\"></script>\n <script defer src=\"../uibuilder/uibuilder.iife.min.js\"></script>\n <script defer src=\"./index.js\"></script>\n\n</head><body class=\"uib\">\n \n <h1 class=\"with-subtitle\">Markdown-Mermaid Example</h1>\n <div role=\"doc-subtitle\">Using the uibuilder IIFE library.</div>\n\n <div>\n <button id=\"btn1\" onclick=\"uibuilder.eventSend(event)\">Machine-1</button>\n <button id=\"btn2\" onclick=\"uibuilder.eventSend(event)\">Machine-2</button>\n </div>\n\n <!-- <pre id=\"chart\"></pre> -->\n <pre id=\"chart\" class=\"mermaid\"></pre>\n\n <div id=\"more\"></div>\n\n</body></html>\n","output":"str","x":910,"y":460,"wires":[["57d61672e9bf3aaf"]]},{"id":"d35ddf5578ec138c","type":"inject","z":"1b2ed12a321034dc","name":"Upd FE Code (Run this once to get the web page)","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":590,"y":460,"wires":[["4571a4fe83cfd5b5","dfb618a2f72fbebd"]]},{"id":"57d61672e9bf3aaf","type":"uib-save","z":"1b2ed12a321034dc","url":"mermaid-timeline","uibId":"65bbdbc1a8301e7b","folder":"src","fname":"index.html","createFolder":false,"reload":true,"usePageName":false,"encoding":"utf8","mode":438,"name":"index.html","topic":"","x":1070,"y":460,"wires":[]},{"id":"dfb618a2f72fbebd","type":"template","z":"1b2ed12a321034dc","name":"index.js","field":"payload","fieldType":"msg","format":"javascript","syntax":"mustache","template":"// @ts-nocheck\n\n// console.log(mermaid) // This is the mermaid library object\n\n// Don't let the library start automatically - we need control\nmermaid.initialize({ startOnLoad: false });\n\nconst printArguments = function (arg1, arg2, arg3) {\n alert('printArguments called with arguments: ' + arg1 + ', ' + arg2 + ', ' + arg3);\n};\n\n// Listen for incoming messages from Node-RED and action\nuibuilder.onChange('msg', (msg) => {\n // console.log(msg.payload)\n\n // Get a reference to the chart element\n const chart = document.getElementById('chart')\n\n // If the chart has already been processed, remove it\n delete chart.dataset.processed\n chart.innerHTML = ''\n\n // Set the innerHTML of the chart element to the payload\n $('#chart').textContent = msg.payload\n\n // (Re-)run the mermaid code\n mermaid.run({ querySelector: '#chart' });\n})\n","output":"str","x":900,"y":520,"wires":[["c6f4ee3f3127719b"]]},{"id":"c6f4ee3f3127719b","type":"uib-save","z":"1b2ed12a321034dc","url":"mermaid-timeline","uibId":"65bbdbc1a8301e7b","folder":"src","fname":"index.js","createFolder":false,"reload":true,"usePageName":false,"encoding":"utf8","mode":438,"name":"index.js","topic":"","x":1060,"y":520,"wires":[]},{"id":"e03baf0d7e66ba6a","type":"template","z":"1b2ed12a321034dc","name":"","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":" \n---\ndisplayMode: compact\n---\ngantt\n dateFormat YYYY-MM-DD HH:mm\n axisFormat %H:%M\n tickInterval 1hour\n title Machine Operation Status (HTML) \n \n section {{machine}}\n {{data}}\n \n click des1 call printArguments(\"test1\", \"test2\", test3)\n","output":"str","x":940,"y":320,"wires":[["1e40e8ac1c2ee858"]]},{"id":"562c9d4ecec8efde","type":"function","z":"1b2ed12a321034dc","name":"Machine 1 data","func":"msg.data = `\n\nOFF :crit, des1, 2014-12-15 00:00, 2014-12-15 01:10\nON :active, des2, 2014-12-15 01:10, 2014-12-15 02:20\nOFF :crit, des1, 2014-12-15 02:20, 2014-12-15 09:30\nON :active, des2, 2014-12-15 09:30, 2014-12-15 14:00\n\n`\n\nmsg.machine = 'machine-1'\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":300,"wires":[["e03baf0d7e66ba6a"]]},{"id":"53ea9de0b8b3528a","type":"function","z":"1b2ed12a321034dc","name":"Machine 2 data","func":"msg.data = `\n OFF :active, des1, 2014-12-15 00:00, 2014-12-15 01:10\n ON :crit, des2, 2014-12-15 01:10, 2014-12-15 02:20\n OFF :active, des1, 2014-12-15 02:20, 2014-12-15 09:30\n ON :crit, des2, 2014-12-15 09:30, 2014-12-15 14:00\n`\n\nmsg.machine = 'machine-2'\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":340,"wires":[["e03baf0d7e66ba6a"]]}]
After setting up the uibuilder and uib-save nodes and deploying. Trigger the inject to get the desired web page and script. The page looks like this:
Press one of the buttons to get the chart:
Then press the other button to get the other chart. The data comes from link to the other thread.
The 2 function nodes set up the text data for each machine output and the template merges the data into the Mermaid chart description text. The full text looks like this:
---
displayMode: compact
---
gantt
dateFormat YYYY-MM-DD HH:mm
axisFormat %H:%M
tickInterval 1hour
title Machine Operation Status (HTML)
section machine-1
OFF :crit, des1, 2014-12-15 00:00, 2014-12-15 01:10
ON :active, des2, 2014-12-15 01:10, 2014-12-15 02:20
OFF :crit, des1, 2014-12-15 02:20, 2014-12-15 09:30
ON :active, des2, 2014-12-15 09:30, 2014-12-15 14:00
Note the initial blank line - you need that otherwise Mermaid objects to the YAML displayMode instruction.
Also note that Mermaid is essentially a STATIC chart. You cannot dynamically extend the chart data, you can only completely rebuild the chart. But with the magic of Node-RED and UIBUILDER, rebuilding the chart is simple.
This is a very simplistic example but it certainly works. Not sure whether I'd ever actually use this outside of a Markdown page but the approach could occasionally be helpful.