@flowfuse/node-red-dashboard 1.30.1-d74e2b2-202512211307.0 → 1.30.1

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.
Files changed (72) hide show
  1. package/dist/apple-touch-icon.png +0 -0
  2. package/dist/assets/_basePickBy-FqRZvdub.js +1 -0
  3. package/dist/assets/_baseUniq-DiFk0efX.js +1 -0
  4. package/dist/assets/architectureDiagram-VXUJARFQ-jAqyWwtR.js +36 -0
  5. package/dist/assets/blockDiagram-VD42YOAC-CKJM_wgX.js +122 -0
  6. package/dist/assets/c4Diagram-YG6GDRKO-CxvH_CnU.js +10 -0
  7. package/dist/assets/chunk-4BX2VUAB-DIDmWMKO.js +1 -0
  8. package/dist/assets/chunk-55IACEB6-Df5y1Lp_.js +1 -0
  9. package/dist/assets/chunk-B4BG7PRW-CO-AHyum.js +165 -0
  10. package/dist/assets/chunk-DI55MBZ5-BybdrWiR.js +220 -0
  11. package/dist/assets/chunk-FMBD7UC4-X9A-MON_.js +15 -0
  12. package/dist/assets/chunk-QN33PNHL-Cu4PVK5_.js +1 -0
  13. package/dist/assets/chunk-QZHKN3VN-Dc9O1mlv.js +1 -0
  14. package/dist/assets/chunk-TZMSLE5B-BONSH9X8.js +1 -0
  15. package/dist/assets/classDiagram-2ON5EDUG-Cxnli4ed.js +1 -0
  16. package/dist/assets/classDiagram-v2-WZHVMYZB-Cxnli4ed.js +1 -0
  17. package/dist/assets/clone-B9AAPnxW.js +1 -0
  18. package/dist/assets/cose-bilkent-S5V4N54A-DLVc0vYA.js +1 -0
  19. package/dist/assets/cytoscape.esm-EolcthU4.js +16 -0
  20. package/dist/assets/dagre-6UL2VRFP-BUmOTBjK.js +4 -0
  21. package/dist/assets/diagram-PSM6KHXK-DDqf8b18.js +24 -0
  22. package/dist/assets/diagram-QEK2KX5R-p3AlQ66r.js +43 -0
  23. package/dist/assets/diagram-S2PKOQOG-CGFyLHgt.js +24 -0
  24. package/dist/assets/disconnected-BuxohaUu.png +0 -0
  25. package/dist/assets/echarts-B5dUVA7m.js +73 -0
  26. package/dist/assets/erDiagram-Q2GNP2WA-Bt9WYEVl.js +60 -0
  27. package/dist/assets/flowDiagram-NV44I4VS-C5THihSS.js +162 -0
  28. package/dist/assets/ganttDiagram-JELNMOA3-DAkh2oA5.js +267 -0
  29. package/dist/assets/gitGraphDiagram-NY62KEGX-CHv2T5DP.js +65 -0
  30. package/dist/assets/graph-BUdFdnDY.js +1 -0
  31. package/dist/assets/index-BKldaNxV.css +13 -0
  32. package/dist/assets/index-CgN92DC4.js +121 -0
  33. package/dist/assets/infoDiagram-WHAUD3N6-D3Iff__M.js +2 -0
  34. package/dist/assets/journeyDiagram-XKPGCS4Q-Dma7uvIP.js +139 -0
  35. package/dist/assets/kanban-definition-3W4ZIXB7-D56zGc6I.js +89 -0
  36. package/dist/assets/katex-ChWnQ-fc.js +261 -0
  37. package/dist/assets/layout-CX2674-m.js +1 -0
  38. package/dist/assets/logo-DIAzbBuw.png +0 -0
  39. package/dist/assets/materialdesignicons-webfont-B7mPwVP_.ttf +0 -0
  40. package/dist/assets/materialdesignicons-webfont-CSr8KVlo.eot +0 -0
  41. package/dist/assets/materialdesignicons-webfont-Dp5v-WZN.woff2 +0 -0
  42. package/dist/assets/materialdesignicons-webfont-PXm3-2wK.woff +0 -0
  43. package/dist/assets/mermaid-4yaRk5L6.js +256 -0
  44. package/dist/assets/mindmap-definition-VGOIOE7T-M2wzuJex.js +68 -0
  45. package/dist/assets/pieDiagram-ADFJNKIX-CS__cEty.js +30 -0
  46. package/dist/assets/quadrantDiagram-AYHSOK5B-Cq8EFFXy.js +7 -0
  47. package/dist/assets/requirementDiagram-UZGBJVZJ-DJ7dPR9-.js +64 -0
  48. package/dist/assets/sankeyDiagram-TZEHDZUN-Bv2bFVOF.js +10 -0
  49. package/dist/assets/sequenceDiagram-WL72ISMW-CNC5hfHr.js +145 -0
  50. package/dist/assets/stateDiagram-FKZM4ZOC-DPogLVYr.js +1 -0
  51. package/dist/assets/stateDiagram-v2-4FDKWEC3-DYEZ0q8X.js +1 -0
  52. package/dist/assets/timeline-definition-IT6M3QCI-DtZbKxJ6.js +61 -0
  53. package/dist/assets/treemap-KMMF4GRG--xu3S5FE.js +128 -0
  54. package/dist/assets/vue-vendor-0dSdYzIF.js +49 -0
  55. package/dist/assets/workbox-window.prod.es5-BIl4cyR9.js +2 -0
  56. package/dist/assets/xychartDiagram-PRI3JC2R-HntCx39-.js +7 -0
  57. package/dist/favicon.ico +0 -0
  58. package/dist/favicon.svg +482 -0
  59. package/dist/index.html +24 -0
  60. package/dist/logo-512x512.png +0 -0
  61. package/dist/logo.svg +482 -0
  62. package/dist/maskable-icon-512x512.png +0 -0
  63. package/dist/pwa-192x192.png +0 -0
  64. package/dist/pwa-512x512.png +0 -0
  65. package/dist/pwa-64x64.png +0 -0
  66. package/dist/sw.js +2 -0
  67. package/nodes/store/data.js +17 -0
  68. package/nodes/widgets/locales/en-US/ui_chart.html +41 -1
  69. package/nodes/widgets/ui_chart.js +90 -53
  70. package/nodes/widgets/ui_control.js +2 -3
  71. package/nodes/widgets/ui_spacer.html +4 -4
  72. package/package.json +1 -1
@@ -14,6 +14,9 @@ module.exports = function (RED) {
14
14
  const group = RED.nodes.getNode(config.group)
15
15
  const base = group.getBase()
16
16
 
17
+ // add a chartOptions object into the config
18
+ config.chartOptions = config.chartOptions || {}
19
+
17
20
  // correct typing
18
21
  if (typeof config.xmin !== 'undefined') {
19
22
  config.xmin = parseFloat(config.xmin)
@@ -58,19 +61,36 @@ module.exports = function (RED) {
58
61
  if (removeOlder > 0) {
59
62
  const removeOlderUnit = parseFloat(config.removeOlderUnit)
60
63
  const ago = (removeOlder * removeOlderUnit) * 1000 // milliseconds ago
61
- const cutoff = (new Date()).getTime() - ago
62
- const _msg = datastore.get(node.id).filter((msg) => {
64
+ const cutOff = (new Date()).getTime() - ago
65
+ const filterFn = (msg) => {
63
66
  let timestamp = msg._datapoint.x
64
67
  // is x already a millisecond timestamp?
65
68
  if (typeof (msg._datapoint.x) === 'string') {
66
69
  timestamp = (new Date(msg._datapoint.x)).getTime()
67
70
  }
68
- return timestamp > cutoff
69
- })
70
- datastore.save(base, node, _msg)
71
+ return timestamp > cutOff
72
+ }
73
+ datastore.filter(base, node, filterFn)
71
74
  }
72
75
  }
73
76
 
77
+ /**
78
+ * For categorical xaxis and types other than histogram then only keep the latest data point for
79
+ * each category in each series
80
+ */
81
+ function clearOldCategoricalPoints () {
82
+ const points = datastore.get(node.id)
83
+ const latestSet = {}
84
+ for (const item of points) {
85
+ const { category, x } = item._datapoint
86
+ const key = JSON.stringify([category, x]) // a unique key for each category/series combination
87
+ latestSet[key] = item
88
+ }
89
+
90
+ const filtered = Object.values(latestSet)
91
+ datastore.save(base, node, filtered)
92
+ }
93
+
74
94
  // ensure sane defaults
75
95
  if (!['msg', 'str', 'property', 'timestamp'].includes(config.xAxisPropertyType)) {
76
96
  config.xAxisPropertyType = 'timestamp' // default to 'timestamp'
@@ -209,61 +229,78 @@ module.exports = function (RED) {
209
229
  if (!datastore.get(node.id)) {
210
230
  datastore.save(base, node, [])
211
231
  }
212
- if (Array.isArray(msg.payload) && !msg.payload.length) {
213
- // clear history
214
- datastore.save(base, node, [])
215
- } else {
216
- // delete old data if a replace is being performed.
217
- // This is the case if msg.action is replace
218
- // or the node is configured for replace and this is not being overriden by msg.action set to append
219
- if (msg.action === 'replace' || (config.action === 'replace' && msg.action !== 'append')) {
220
- // clear our data store as we are replacing data
232
+ // To prevent ui_update messages from deleting old data, skip this section if no msg.payload present
233
+ if (typeof msg.payload !== 'undefined') {
234
+ if (Array.isArray(msg.payload) && !msg.payload.length) {
235
+ // clear history
221
236
  datastore.save(base, node, [])
222
- }
223
- if (!Array.isArray(msg.payload)) {
224
- // quick clone of msg, and store in history
225
- datastore.append(base, node, {
226
- ...msg
227
- })
228
237
  } else {
229
- // we have an array in msg.payload, let's split them
230
- msg.payload.forEach((p, i) => {
231
- const payload = JSON.parse(JSON.stringify(p))
232
- const d = msg._datapoint ? msg._datapoint[i] : null
233
- const m = {
234
- ...msg,
235
- payload,
236
- _datapoint: d
238
+ // delete old data if a replace is being performed.
239
+ // This is the case if msg.action is replace
240
+ // or the node is configured for replace and this is not being overriden by msg.action set to append
241
+ if (msg.action === 'replace' || (config.action === 'replace' && msg.action !== 'append')) {
242
+ // clear our data store as we are replacing data
243
+ datastore.save(base, node, [])
244
+ }
245
+ if (!Array.isArray(msg.payload)) {
246
+ // quick clone of msg, and store in history
247
+ datastore.append(base, node, {
248
+ ...msg
249
+ })
250
+ } else {
251
+ // we have an array in msg.payload, let's split them
252
+ msg.payload.forEach((p, i) => {
253
+ const payload = JSON.parse(JSON.stringify(p))
254
+ const d = msg._datapoint ? msg._datapoint[i] : null
255
+ const m = {
256
+ ...msg,
257
+ payload,
258
+ _datapoint: d
259
+ }
260
+ datastore.append(base, node, m)
261
+ })
262
+ }
263
+
264
+ const maxPoints = parseInt(config.removeOlderPoints)
265
+
266
+ if (maxPoints && config.removeOlderPoints) {
267
+ // account for multiple lines?
268
+ // client-side does this for _each_ line
269
+ // remove older points using datastore.filter instead of saving the whole array
270
+ const lineCounts = {}
271
+ const _msg = datastore.get(node.id) || []
272
+
273
+ // determine which message objects to keep (latest maxPoints per label)
274
+ const keepIndexes = []
275
+ let doFiltering = false
276
+ for (let i = _msg.length - 1; i >= 0; i--) {
277
+ const m = _msg[i]
278
+ const label = m.topic
279
+ lineCounts[label] = lineCounts[label] || 0
280
+ if (lineCounts[label] < maxPoints) {
281
+ keepIndexes[i] = true
282
+ lineCounts[label]++
283
+ } else {
284
+ doFiltering = true
285
+ }
237
286
  }
238
- datastore.append(base, node, m)
239
- })
240
- }
241
287
 
242
- const maxPoints = parseInt(config.removeOlderPoints)
243
-
244
- if (maxPoints && config.removeOlderPoints) {
245
- // account for multiple lines?
246
- // client-side does this for _each_ line
247
- // remove older points
248
- const lineCounts = {}
249
- const _msg = datastore.get(node.id)
250
- // trawl through in reverse order, and only keep the latest points (up to maxPoints) for each label
251
- for (let i = _msg.length - 1; i >= 0; i--) {
252
- const msg = _msg[i]
253
- const label = msg.topic
254
- lineCounts[label] = lineCounts[label] || 0
255
- if (lineCounts[label] >= maxPoints) {
256
- _msg.splice(i, 1)
257
- } else {
258
- lineCounts[label]++
288
+ // filter the datastore to only keep the selected messages
289
+ if (doFiltering) {
290
+ datastore.filter(base, node, (m, i) => {
291
+ return keepIndexes[i]
292
+ })
259
293
  }
260
294
  }
261
- datastore.save(base, node, _msg)
262
- }
263
295
 
264
- if (config.xAxisType === 'time' && config.removeOlder && config.removeOlderUnit) {
265
- // remove any points older than the specified time
266
- clearOldPoints()
296
+ if (config.xAxisType === 'time' && config.removeOlder && config.removeOlderUnit) {
297
+ // remove any points older than the specified time
298
+ clearOldPoints()
299
+ } else if (config.xAxisType === 'category' && config.chartType !== 'histogram') {
300
+ // for categorical xaxis and types other than histogram then only keep the latest data point for
301
+ // each category in each series
302
+ clearOldCategoricalPoints()
303
+ }
267
304
  }
268
305
  }
269
306
 
@@ -201,7 +201,6 @@ module.exports = function (RED) {
201
201
  // When the target was to identify a widget, then the search is over here if a widget has been found
202
202
  if (widgetNode && type === 'widget') {
203
203
  identifiedNodes.push(widgetNode)
204
- return
205
204
  }
206
205
  }
207
206
 
@@ -338,10 +337,10 @@ module.exports = function (RED) {
338
337
  widgets.hide = updateStateStore('widget', widgets.hide, msg, 'visible', false)
339
338
  }
340
339
  if ('enable' in widgets) {
341
- widgets.enable = updateStateStore('widget', widgets.enable, msg, 'disabled', false)
340
+ widgets.enable = updateStateStore('widget', widgets.enable, msg, 'enabled', true)
342
341
  }
343
342
  if ('disable' in widgets) {
344
- widgets.disable = updateStateStore('widget', widgets.disable, msg, 'disabled', true)
343
+ widgets.disable = updateStateStore('widget', widgets.disable, msg, 'enabled', false)
345
344
  }
346
345
 
347
346
  // ensure consistency in payload format
@@ -77,19 +77,19 @@
77
77
  </div>
78
78
  <div class="form-row nr-db-ui-element-hide-in-subflow">
79
79
  <label><i class="fa fa-object-group"></i> <span data-i18n="ui-base.label.size">Size</label>
80
- <button class="editor-button" id="node-input-size"></button>
80
+ <button class="editor-button" id="node-config-input-size"></button>
81
81
  </div>
82
82
  <div class="form-row nr-db-ui-element-show-in-subflow">
83
83
  <label><i class="fa fa-arrows-h"></i> <span data-i18n="ui-base.label.width">Width</label>
84
- <input type="hidden" id="node-input-width">
84
+ <input type="hidden" id="node-config-input-width">
85
85
  </div>
86
86
  <div class="form-row nr-db-ui-element-show-in-subflow">
87
87
  <label><i class="fa fa-arrows-v"></i> <span data-i18n="ui-base.label.height">Height</label>
88
- <input type="hidden" id="node-input-height">
88
+ <input type="hidden" id="node-config-input-height">
89
89
  </div>
90
90
  <div class="form-row nr-db-ui-element-show-in-subflow">
91
91
  <label><i class="fa fa-arrows"></i> <span data-i18n="ui-base.label.order">Order</label>
92
- <input type="text" id="node-input-order">
92
+ <input type="text" id="node-input-config-order">
93
93
  </div>
94
94
  <div class="form-row">
95
95
  <label for="node-config-input-className"><i class="fa fa-code"></i> <span data-i18n="ui-spacer.label.class"></span></label>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowfuse/node-red-dashboard",
3
- "version": "1.30.1-d74e2b2-202512211307.0",
3
+ "version": "1.30.1",
4
4
  "description": "FlowFuse Dashboard - A collection of Node-RED nodes that provide functionality to build your own UI applications (inc. forms, buttons, charts).",
5
5
  "keywords": [
6
6
  "node-red"