@flowfuse/node-red-dashboard 1.4.1 → 1.4.2-603b043-202403211300.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.
Files changed (51) hide show
  1. package/nodes/config/locales/en-US/ui_base.json +7 -2
  2. package/nodes/config/ui_base.html +244 -21
  3. package/nodes/config/ui_theme.html +13 -2
  4. package/nodes/widgets/locales/en-US/ui_table.json +7 -1
  5. package/nodes/widgets/ui_table.html +19 -3
  6. package/nodes/widgets/ui_table.js +10 -1
  7. package/package.json +1 -1
  8. package/dist/assets/array-Nw74a44z.js +0 -1
  9. package/dist/assets/c4Diagram-c0b17d02-a2pebagc.js +0 -10
  10. package/dist/assets/classDiagram-a8cc8886-GZGIdAJ_.js +0 -1
  11. package/dist/assets/classDiagram-v2-802a48d3-HsmQ8rG1.js +0 -2
  12. package/dist/assets/createText-3b1f58a4-mxGZySa5.js +0 -7
  13. package/dist/assets/edges-0005682e-fKeIHISH.js +0 -4
  14. package/dist/assets/erDiagram-dedf2781-eQZ1uSA4.js +0 -51
  15. package/dist/assets/flowDb-ff651a22-LfeipZDx.js +0 -6
  16. package/dist/assets/flowDiagram-d6f8fe3a-JN4Ed9yZ.js +0 -4
  17. package/dist/assets/flowDiagram-v2-58f49b84-33yARutL.js +0 -1
  18. package/dist/assets/flowchart-elk-definition-56584a6c-bfP0iEF8.js +0 -139
  19. package/dist/assets/ganttDiagram-088dbd90-e7z2jUgU.js +0 -266
  20. package/dist/assets/gitGraphDiagram-e0ffc2d1-I_KYQh4w.js +0 -70
  21. package/dist/assets/index-Bg4_FLds.css +0 -13
  22. package/dist/assets/index-asnKLxJa.js +0 -244
  23. package/dist/assets/index-f58d48f9-d4cGR4h9.js +0 -1
  24. package/dist/assets/infoDiagram-64895a6e-vN27pifj.js +0 -7
  25. package/dist/assets/init-Hi12RPRh.js +0 -1
  26. package/dist/assets/journeyDiagram-adaa34f8-xi8F80bo.js +0 -139
  27. package/dist/assets/layout-3LCZs9R0.js +0 -1
  28. package/dist/assets/line-fvKpAihG.js +0 -1
  29. package/dist/assets/linear-uqAl50Gy.js +0 -1
  30. package/dist/assets/logo-yAM2wbsA.png +0 -0
  31. package/dist/assets/materialdesignicons-webfont-AeJCkVfO.woff2 +0 -0
  32. package/dist/assets/materialdesignicons-webfont-GtBnFie6.woff +0 -0
  33. package/dist/assets/materialdesignicons-webfont-IwmCr2II.ttf +0 -0
  34. package/dist/assets/materialdesignicons-webfont-JbV82i5g.eot +0 -0
  35. package/dist/assets/mindmap-definition-57868176-LV1c2j1Q.js +0 -109
  36. package/dist/assets/ordinal-OOfG4Z7H.js +0 -1
  37. package/dist/assets/pieDiagram-3fca7ce7-vfEvOfTZ.js +0 -35
  38. package/dist/assets/quadrantDiagram-0ca4be02-OEgV-acy.js +0 -7
  39. package/dist/assets/requirementDiagram-e13af0f0-uQ3nZvjs.js +0 -52
  40. package/dist/assets/sankeyDiagram-a7f8e230-ndSWm-w9.js +0 -8
  41. package/dist/assets/sequenceDiagram-84aa38e3-1fPqwfPD.js +0 -122
  42. package/dist/assets/stateDiagram-9a586ac6--PiHEPfC.js +0 -1
  43. package/dist/assets/stateDiagram-v2-96f2b9df-NwLYhf_v.js +0 -1
  44. package/dist/assets/styles-1b0c237a-MROaEgpy.js +0 -110
  45. package/dist/assets/styles-622362e4-vxjPbudl.js +0 -160
  46. package/dist/assets/styles-a1a6e33f-zXFCWLTw.js +0 -207
  47. package/dist/assets/svgDraw-70101091-9ZNdrwGr.js +0 -2
  48. package/dist/assets/svgDrawCommon-42e92da3-N6cP2Lo3.js +0 -1
  49. package/dist/assets/timeline-definition-1a90b03d-RxP2u2Dq.js +0 -61
  50. package/dist/favicon.ico +0 -0
  51. package/dist/index.html +0 -20
@@ -10,6 +10,7 @@
10
10
  "openDashboard": "Open Dashboard",
11
11
  "layout": "Layout",
12
12
  "layoutMessage": "Here you can re-order and move your widgets, groups and pages.",
13
+ "unattachedMessage": "The following Groups have do not have a parent page defined.",
13
14
  "theming": "Theming",
14
15
  "themingMessage": "Here you can can get quick access to your UI Themes, defined on your Dashboard.",
15
16
  "page": "Page",
@@ -26,13 +27,17 @@
26
27
  },
27
28
  "layout": {
28
29
  "pages": "Pages",
30
+ "page": "Page",
31
+ "group": "Group",
29
32
  "edit": "Edit",
30
33
  "focus": "Focus",
31
34
  "collapse": "Collapse",
32
- "expand": "Expand"
35
+ "expand": "Expand",
36
+ "unattachedGroups": "Unattached Groups"
33
37
  },
34
38
  "themes": {
35
- "header": "Themes"
39
+ "header": "Themes",
40
+ "theme": "Theme"
36
41
  },
37
42
  "colors": {
38
43
  "light": "var(--nrdb-node-light)",
@@ -57,6 +57,10 @@
57
57
  background-color: #e3f2fd;
58
58
  }
59
59
  /* Dashboard 2.0 Sidebar */
60
+ .nrdb2-sb-list-button-group {
61
+ display: flex;
62
+ gap: 4px;
63
+ }
60
64
  .nrdb2-sidebar-tab-content {
61
65
  padding: 8px 10px;
62
66
  height: 100%;
@@ -81,6 +85,11 @@
81
85
  padding: 0;
82
86
  border-bottom: 0;
83
87
  }
88
+ .nrdb2-sb-unattached-groups-list li {
89
+ padding: 0;
90
+ border-bottom: 0;
91
+ background-color: #ffefef;
92
+ }
84
93
  .nrdb2-sb-list-header {
85
94
  display: flex;
86
95
  gap: 6px;
@@ -312,7 +321,7 @@
312
321
  * Add Custom Dashboard Side Menu
313
322
  * */
314
323
 
315
- function uiLink (id, name, path) {
324
+ function dashboardLink (id, name, path) {
316
325
  const base = RED.settings.httpNodeRoot || '/'
317
326
  const basePart = base.endsWith('/') ? base : `${base}/`
318
327
  const dashPart = path.startsWith('/') ? path.slice(1) : path
@@ -465,6 +474,128 @@
465
474
  }
466
475
  }
467
476
 
477
+ function addConfigNode (node) {
478
+ if (!node.user) {
479
+ node.users = []
480
+ }
481
+ node.dirty = true
482
+ RED.nodes.add(node)
483
+ RED.editor.validateNode(node)
484
+ RED.history.push({
485
+ t: 'add',
486
+ nodes: [node.id],
487
+ dirty: RED.nodes.dirty()
488
+ })
489
+ RED.nodes.dirty(true)
490
+ }
491
+
492
+ function mapDefaults (defaults) {
493
+ const values = {}
494
+ for (const key in defaults) {
495
+ if (Object.prototype.hasOwnProperty.call(defaults, key)) {
496
+ values[key] = defaults[key].value
497
+ }
498
+ }
499
+ return values
500
+ }
501
+
502
+ function addDefaultPage (baseId, themeId) {
503
+ const page = RED.nodes.getType('ui-page')
504
+ const pageNode = {
505
+ _def: page,
506
+ id: RED.nodes.id(),
507
+ type: 'ui-page',
508
+ ...mapDefaults(page.defaults),
509
+ path: '/pageN', // TODO: generate a unique path
510
+ name: 'Page N',
511
+ ui: baseId,
512
+ theme: themeId,
513
+ layout: 'grid'
514
+ }
515
+
516
+ addConfigNode(pageNode)
517
+ return pageNode
518
+ }
519
+
520
+ function addDefaultGroup (pageId) {
521
+ const group = RED.nodes.getType('ui-group')
522
+ const groupNode = {
523
+ _def: group,
524
+ id: RED.nodes.id(),
525
+ type: 'ui-group',
526
+ ...mapDefaults(group.defaults),
527
+ name: 'My Group',
528
+ page: pageId
529
+ }
530
+
531
+ addConfigNode(groupNode)
532
+ return groupNode
533
+ }
534
+
535
+ function addDefaultTheme () {
536
+ const theme = RED.nodes.getType('ui-theme')
537
+ const themeNode = {
538
+ _def: theme,
539
+ id: RED.nodes.id(),
540
+ type: 'ui-theme',
541
+ ...mapDefaults(theme.defaults),
542
+ name: 'Default Theme'
543
+ }
544
+
545
+ addConfigNode(themeNode)
546
+ return themeNode
547
+ }
548
+
549
+ function addLayoutsDefaults () {
550
+ const cNodes = ['ui-base', 'ui-page', 'ui-group', 'ui-theme']
551
+ let exists = false
552
+ RED.nodes.eachConfig((n) => {
553
+ if (cNodes.includes(n.type)) {
554
+ exists = true
555
+ }
556
+ })
557
+
558
+ // check if we haven't got any of these yet
559
+ if (!exists) {
560
+ // Add Single Base Node
561
+ const base = RED.nodes.getType('ui-base')
562
+ const baseNode = {
563
+ _def: base,
564
+ id: RED.nodes.id(),
565
+ type: 'ui-base',
566
+ ...mapDefaults(base.defaults),
567
+ name: 'My Dashboard'
568
+ }
569
+
570
+ addConfigNode(baseNode)
571
+
572
+ const theme = addDefaultTheme()
573
+ const page = addDefaultPage(base.id, theme.id)
574
+ const group = addDefaultGroup(page.id)
575
+
576
+ // update existing `ui-` nodes to use the new base/page/theme/group
577
+ RED.nodes.eachNode((node) => {
578
+ if (node.type.startsWith('ui-')) {
579
+ // if node has a group property
580
+ if (hasProperty(node._def.defaults, 'group') && !node.group) {
581
+ // group-scoped widgets - which is most of them
582
+ node.group = group.id
583
+ } else if (hasProperty(node._def.defaults, 'page') && !node.page) {
584
+ // page-scoped widgets
585
+ node.page = page.id
586
+ } else if (hasProperty(node._def.defaults, 'ui') && !node.ui) {
587
+ // base-scoped widgets, e.g. ui-notification/control
588
+ node.ui = baseNode.id
589
+ }
590
+ RED.editor.validateNode(node)
591
+ }
592
+ })
593
+
594
+ RED.view.redraw()
595
+ RED.sidebar.config.refresh()
596
+ }
597
+ }
598
+
468
599
  // watch for nodes changed, added, removed - use this to refresh the sidebar
469
600
  let refreshBusy = false // this is set/reset inside refreshSidebarEditors
470
601
  const refreshSidebarEditorDebounced = debounce(refreshSidebarEditors, 300)
@@ -478,7 +609,6 @@
478
609
  const conditionalSidebarRefresh = function (node, eventName) {
479
610
  // if the layout editor is not in view, don't refresh
480
611
  if ($('#ff-node-red-dashboard').parent().css('display') === 'none') { return }
481
- if ($('#dashboard-2-pages').css('display') === 'none') { return }
482
612
  // if a refresh is in progress, don't refresh
483
613
  if (refreshBusy) { return }
484
614
  if (RED._db2debug) { console.log('dashboard 2: conditionalSidebarRefresh (node, eventName)', node, eventName) }
@@ -514,6 +644,7 @@
514
644
  conditionalSidebarRefresh(event, 'nodes:change')
515
645
  })
516
646
 
647
+ const addLayoutsDefaultsDebounced = debounce(addLayoutsDefaults, 25)
517
648
  const checkDuplicateUiBasesDebounced = debounce(checkDuplicateUiBases, 25)
518
649
  RED.events.on('nodes:add', function (node) {
519
650
  if (RED._db2debug) { console.log('nodes:add', node) }
@@ -527,6 +658,7 @@
527
658
  if (node.type.startsWith('ui-')) {
528
659
  // action on all ui- elements to ensure we've remapped (now) missing ui-base nodes
529
660
  checkDuplicateUiBasesDebounced()
661
+ addLayoutsDefaultsDebounced()
530
662
  }
531
663
  })
532
664
  RED.events.on('nodes:remove', function (event) {
@@ -538,7 +670,7 @@
538
670
  * @param {Object} parent - jQuery object to add this button group as a child element to
539
671
  * @param {DashboardItem} item - The page/group/widget that these actions are bound to
540
672
  */
541
- function addRowActions (parent, item) {
673
+ function addRowActions (parent, item, list) {
542
674
  const configNodes = ['ui-base', 'ui-page', 'ui-group', 'ui-theme']
543
675
  const btnGroup = $('<div>', { class: 'nrdb2-sb-list-header-button-group', id: item.id }).appendTo(parent)
544
676
  if (!configNodes.includes(item.type)) {
@@ -559,6 +691,15 @@
559
691
  evt.stopPropagation()
560
692
  evt.preventDefault()
561
693
  })
694
+ if (item.type === 'ui-page') {
695
+ // add the "+ group" button
696
+ $('<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button"><i class="fa fa-plus"></i> ' + c_('layout.group') + '</a>')
697
+ .click(function (evt) {
698
+ list.editableList('addItem')
699
+ evt.preventDefault()
700
+ })
701
+ .appendTo(btnGroup)
702
+ }
562
703
  }
563
704
 
564
705
  /**
@@ -660,26 +801,34 @@
660
801
  * @param {DashboardItemLookup} widgetsByGroup - The lookup of widgets by group
661
802
  */
662
803
  function addGroupOrderingList (pageId, container, groups, widgetsByGroup) {
663
- // ordered list of groupss to live within a container (e.g. page list item)
804
+ // ordered list of groups to live within a container (e.g. page list item)
664
805
  const groupsOL = $('<ol>', { class: 'nrdb2-sb-group-list' }).appendTo(container).editableList({
665
806
  sortable: '.nrdb2-sb-groups-list-header',
666
807
  addButton: false,
667
808
  height: 'auto',
668
809
  connectWith: '.nrdb2-sb-group-list',
669
810
  addItem: function (container, i, group) {
811
+ if (!group || !group.id) {
812
+ console.log('add group', group, 'to', pageId)
813
+ // this is a new page that's been added and we need to setup the basics
814
+ group = addDefaultGroup(pageId)
815
+ RED.editor.editConfig('', group.type, group.id)
816
+ }
817
+
818
+ const widgets = widgetsByGroup[group.id] || []
670
819
  const titleRow = $('<div>', { class: 'nrdb2-sb-list-header nrdb2-sb-groups-list-header' }).appendTo(container)
671
820
  $('<i class="nrdb2-sb-list-handle nrdb2-sb-group-list-handle fa fa-bars"></i>').appendTo(titleRow)
672
821
  const chevron = $('<i class="fa fa-angle-down nrdb2-sb-list-chevron">', { style: 'width:10px;' }).appendTo(titleRow)
673
822
  const groupicon = 'fa-table'
674
823
  $('<i>', { class: 'nrdb2-sb-icon nrdb2-sb-group-icon fa ' + groupicon }).appendTo(titleRow)
675
824
  $('<span>', { class: 'nrdb2-sb-title' }).text(group.name || group.id).appendTo(titleRow)
825
+ $('<span>', { class: 'nrdb2-sb-info' }).text(`${widgets.length} Widgets`).appendTo(titleRow)
676
826
 
677
827
  const actions = $('<div>', { class: 'nrdb2-sb-list-header-actions' }).appendTo(titleRow)
678
828
  addRowActions(actions, group)
679
829
  addRowStateOptions(actions, group)
680
830
 
681
831
  // adds widgets within this group
682
- const widgets = widgetsByGroup[group.id] || []
683
832
  const widgetsList = $('<div>', { class: 'nrdb2-sb-widget-list-container' }).appendTo(container)
684
833
 
685
834
  // add chevron/list toggle
@@ -724,6 +873,8 @@
724
873
  if (RED._db2debug) { if (RED._db2debug) { console.log('dashboard 2: ui_base.html: addGroupOrderingList: adding group', group) } }
725
874
  groupsOL.editableList('addItem', group)
726
875
  })
876
+
877
+ return groupsOL
727
878
  }
728
879
 
729
880
  /**
@@ -869,12 +1020,21 @@
869
1020
  .appendTo(buttonGroup)
870
1021
  RED.popover.tooltip(buttonExpand, c_('layout.expand'))
871
1022
 
1023
+ // add page button
1024
+ $('<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button"><i class="fa fa-plus"></i> ' + c_('layout.page') + '</a>')
1025
+ .click(function (evt) {
1026
+ pagesOL.editableList('addItem')
1027
+ evt.preventDefault()
1028
+ })
1029
+ .appendTo(buttonGroup)
1030
+
872
1031
  divTabs.append('<div class="nrdb2-layout-helptext">' + c_('label.layoutMessage') + '</div>')
873
1032
 
874
1033
  /** @type {DashboardItemLookup} */
875
1034
  const pages = {}
876
1035
  /** @type {DashboardItemLookup} */
877
1036
  const groupsByPage = {}
1037
+ const unattachedGroups = []
878
1038
  /** @type {DashboardItemLookup} */
879
1039
  const widgetsByGroup = {}
880
1040
  const subflowDefinitions = new Map()
@@ -883,11 +1043,16 @@
883
1043
  if (n.type === 'ui-page' && !!n.ui) {
884
1044
  pages[n.id] = toDashboardItem(n)
885
1045
  }
886
- if (n.type === 'ui-group' && !!n.page) {
887
- if (!groupsByPage[n.page]) {
888
- groupsByPage[n.page] = []
1046
+ if (n.type === 'ui-group') {
1047
+ const p = n.page
1048
+ if (!p) {
1049
+ unattachedGroups.push(toDashboardItem(n))
1050
+ } else {
1051
+ if (!groupsByPage[p]) {
1052
+ groupsByPage[p] = []
1053
+ }
1054
+ groupsByPage[p].push(toDashboardItem(n))
889
1055
  }
890
- groupsByPage[n.page].push(toDashboardItem(n))
891
1056
  }
892
1057
  })
893
1058
 
@@ -951,27 +1116,34 @@
951
1116
  sortable: '.nrdb2-sb-pages-list-header',
952
1117
  addButton: false,
953
1118
  addItem: function (container, i, page) {
1119
+ if (!page || !page.id) {
1120
+ // this is a new page that's been added and we need to setup the basics
1121
+ page = addDefaultPage()
1122
+ RED.editor.editConfig('', page.type, page.id)
1123
+ }
1124
+ const groups = groupsByPage[page.id] || []
1125
+
954
1126
  container.addClass('nrdb2-sb-pages-list-item')
955
1127
 
956
1128
  const titleRow = $('<div>', { class: 'nrdb2-sb-list-header nrdb2-sb-pages-list-header' }).appendTo(container)
1129
+ const groupsList = $('<div>', { class: 'nrdb2-sb-group-list-container' }).appendTo(container)
1130
+
1131
+ // build title row
957
1132
  $('<i class="nrdb2-sb-list-handle nrdb2-sb-page-list-handle fa fa-bars"></i>').appendTo(titleRow)
958
1133
  const chevron = $('<i class="fa fa-angle-down nrdb2-sb-list-chevron">', { style: 'width:10px;' }).appendTo(titleRow)
959
1134
  const tabicon = 'fa-object-group'
960
1135
  $('<i>', { class: 'nrdb2-sb-icon nrdb2-sb-tab-icon fa ' + tabicon }).appendTo(titleRow)
961
1136
  $('<span>', { class: 'nrdb2-sb-title' }).text(page.name || page.id).appendTo(titleRow)
962
-
963
- // page - actions
964
- const actions = $('<div>', { class: 'nrdb2-sb-list-header-actions' }).appendTo(titleRow)
965
- addRowActions(actions, page)
966
- addRowStateOptions(actions, page)
1137
+ $('<span>', { class: 'nrdb2-sb-info' }).text(`${groups.length} Groups`).appendTo(titleRow)
967
1138
 
968
1139
  // adds groups within this page
969
- const groups = groupsByPage[page.id] || []
970
- const groupsList = $('<div>', { class: 'nrdb2-sb-group-list-container' }).appendTo(container)
971
-
972
1140
  titleRow.click(titleToggle(page.id, groupsList, chevron))
1141
+ const groupsOL = addGroupOrderingList(page.id, groupsList, groups, widgetsByGroup)
973
1142
 
974
- addGroupOrderingList(page.id, groupsList, groups, widgetsByGroup)
1143
+ // page - actions
1144
+ const actions = $('<div>', { class: 'nrdb2-sb-list-header-actions' }).appendTo(titleRow)
1145
+ addRowActions(actions, page, groupsOL)
1146
+ addRowStateOptions(actions, page)
975
1147
  },
976
1148
  sortItems: function (items) {
977
1149
  // track any changes
@@ -986,9 +1158,9 @@
986
1158
  }
987
1159
  })
988
1160
 
989
- Object.values(groupsByPage).sort((a, b) => a.order - b.order).forEach(function (groups) {
1161
+ Object.values(pages).sort((a, b) => a.order - b.order).forEach(function (page) {
1162
+ const groups = groupsByPage[page.id] || []
990
1163
  if (RED._db2debug) { console.log('dashboard 2: ui_base.html: buildLayoutOrderEditor: adding groups', groups) }
991
- const page = pages[groups[0].page]
992
1164
  if (page) {
993
1165
  pagesOL.editableList('addItem', page)
994
1166
  }
@@ -997,6 +1169,41 @@
997
1169
  // })
998
1170
  })
999
1171
 
1172
+ // add Unattached Groups to the bottom
1173
+ if (unattachedGroups.length > 0) {
1174
+ const unattachedGroupsSection = $('<div>', { class: 'nrdb2-sidebar-header', style: 'padding-top: 12px;' }).appendTo(divTabs)
1175
+ $('<b>').html(c_('layout.unattachedGroups')).appendTo(unattachedGroupsSection)
1176
+ divTabs.append('<div class="nrdb2-layout-helptext">' + c_('label.unattachedMessage') + '</div>')
1177
+
1178
+ // we have some groups bound to a page that no longer exists
1179
+ const unattachedGroupsOL = $('<ol>', { class: 'nrdb2-sb-unattached-groups-list' }).appendTo(divTabs).editableList({
1180
+ sortable: '.nrdb2-sb-unattached-groups-list-header',
1181
+ addButton: false,
1182
+ addItem: function (container, i, group) {
1183
+ if (!group || !group.id) {
1184
+ // this is a new group that's been added and we need to setup the basics
1185
+ group = addDefaultGroup()
1186
+ RED.editor.editConfig('', group.type, group.id)
1187
+ }
1188
+ container.addClass('nrdb2-sb-unattached-groups-list-item')
1189
+
1190
+ const titleRow = $('<div>', { class: 'nrdb2-sb-list-header nrdb2-sb-unattached-groups-list-header' }).appendTo(container)
1191
+ const tabicon = 'fa-table'
1192
+ $('<i>', { class: 'nrdb2-sb-icon nrdb2-sb-tab-icon fa ' + tabicon }).appendTo(titleRow)
1193
+ $('<span>', { class: 'nrdb2-sb-title' }).text(group.name || group.id).appendTo(titleRow)
1194
+ $('<span>', { class: 'nrdb2-sb-info' }).text('No Page Assigned').appendTo(titleRow)
1195
+
1196
+ // group - actions
1197
+ const actions = $('<div>', { class: 'nrdb2-sb-list-header-actions' }).appendTo(titleRow)
1198
+ addRowActions(actions, group)
1199
+ }
1200
+ })
1201
+
1202
+ unattachedGroups.forEach(function (group) {
1203
+ unattachedGroupsOL.editableList('addItem', group)
1204
+ })
1205
+ }
1206
+
1000
1207
  // call updateLayoutVisibility to sync display level
1001
1208
  updateLayoutVisibility(layoutDisplayLevel)
1002
1209
  }
@@ -1009,6 +1216,17 @@
1009
1216
  const themeHeader = $('<div>', { class: 'nrdb2-sidebar-header' }).appendTo(divTabs)
1010
1217
  $('<b>').html(c_('themes.header')).appendTo(themeHeader)
1011
1218
 
1219
+ // button group
1220
+ const buttonGroup = $('<div>', { class: 'nrdb2-sb-list-button-group' }).appendTo(themeHeader)
1221
+
1222
+ // add theme button
1223
+ $('<a href="#" class="editor-button editor-button-small nr-db-sb-list-header-button"><i class="fa fa-plus"></i> ' + c_('themes.theme') + '</a>')
1224
+ .click(function (evt) {
1225
+ themesOL.editableList('addItem')
1226
+ evt.preventDefault()
1227
+ })
1228
+ .appendTo(buttonGroup)
1229
+
1012
1230
  divTabs.append('<div class="nrdb2-layout-helptext">' + c_('label.themingMessage') + '</div>')
1013
1231
 
1014
1232
  const themes = {}
@@ -1024,6 +1242,11 @@
1024
1242
  sortable: '.nrdb2-sb-pages-list-header',
1025
1243
  addButton: false,
1026
1244
  addItem: function (container, i, theme) {
1245
+ if (!theme || !theme.id) {
1246
+ // this is a new theme that's been added and we need to setup the basics
1247
+ theme = addDefaultTheme()
1248
+ RED.editor.editConfig('', theme.type, theme.id)
1249
+ }
1027
1250
  container.addClass('nrdb2-sb-pages-list-item')
1028
1251
 
1029
1252
  const titleRow = $('<div>', { class: 'nrdb2-sb-list-header nrdb2-sb-themes-list-header' }).appendTo(container)
@@ -1108,7 +1331,7 @@
1108
1331
  RED.nodes.eachConfig(function (n) {
1109
1332
  if (n.type === 'ui-base') {
1110
1333
  const base = n
1111
- sidebar.append(uiLink(base.id, base.name, base.path))
1334
+ sidebar.append(dashboardLink(base.id, base.name, base.path))
1112
1335
  const divTab = $('<div class="red-ui-tabs">').appendTo(sidebar)
1113
1336
 
1114
1337
  const ulDashboardTabs = $('<ul id="dashboard-tabs-list"></ul>').appendTo(divTab)
@@ -8,11 +8,22 @@
8
8
  },
9
9
  // colors
10
10
  colors: {
11
- value: null
11
+ value: {
12
+ surface: '#ffffff',
13
+ primary: '#0094CE',
14
+ bgPage: '#eeeeee',
15
+ groupBg: '#ffffff',
16
+ groupOutline: '#cccccc'
17
+ }
12
18
  },
13
19
  // sizes
14
20
  sizes: {
15
- value: null
21
+ value: {
22
+ pagePadding: '12px',
23
+ groupGap: '12px',
24
+ groupBorderRadius: '4px',
25
+ widgetGap: '12px'
26
+ }
16
27
  }
17
28
  },
18
29
  oneditprepare: function () {
@@ -8,7 +8,13 @@
8
8
  "autoCalculateColumns": "Auto Calculate Columns",
9
9
  "key": "Key",
10
10
  "label": "Label",
11
- "column": "column"
11
+ "column": "column",
12
+ "selection": "Interaction"
13
+ },
14
+ "selection": {
15
+ "none": "None",
16
+ "checkbox": "Checkboxes",
17
+ "click": "Click"
12
18
  }
13
19
  }
14
20
  }
@@ -21,13 +21,15 @@
21
21
  },
22
22
  height: { value: 0 },
23
23
  maxrows: { value: 0, validate: RED.validators.number() },
24
+ passthru: { value: false },
24
25
  autocols: { value: true },
26
+ selectionType: { value: 'none' },
25
27
  columns: {
26
28
  value: null
27
29
  }
28
30
  },
29
31
  inputs: 1,
30
- outputs: 0,
32
+ outputs: 1,
31
33
  outputLabels: function () { return this.mode },
32
34
  icon: 'font-awesome/fa-table',
33
35
  paletteLabel: 'table',
@@ -35,6 +37,12 @@
35
37
  return this.name || 'table'
36
38
  },
37
39
  oneditprepare: function () {
40
+ // handle backward compatibility
41
+ const validSelectionTypes = ['none', 'checkbox', 'click']
42
+ if (!validSelectionTypes.includes(this.selectionType)) {
43
+ $('#node-input-selectionType').val('none')
44
+ }
45
+
38
46
  $('#node-input-size').elementSizer({
39
47
  width: '#node-input-width',
40
48
  height: '#node-input-height',
@@ -62,7 +70,7 @@
62
70
  $('<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)
63
71
 
64
72
  $('<input/>', { class: 'node-input-column-key', type: 'text', style: 'margin-left:7px; width:calc(50% - 32px);', placeholder: RED._('@flowfuse/node-red-dashboard/ui-table:ui-table.label.key'), value: col.key }).appendTo(row)
65
- $('<input/>', { class: 'node-input-column-label', type: 'text', style: 'margin-left:7px; width:calc(50% - 32px);', placeholder: RED._('@flowfuse/node-red-dashboard/ui-table:ui-table.label.label'), value: col.label }).appendTo(row)
73
+ $('<input/>', { class: 'node-input-column-label', type: 'text', style: 'margin-left:7px; width:calc(50% - 32px);', placeholder: RED._('@flowfuse/node-red-dashboard/ui-table:ui-table.label.label'), value: col.title }).appendTo(row)
66
74
 
67
75
  const finalSpan = $('<span/>', { style: 'float:right; margin-right:8px;' }).appendTo(row)
68
76
  const deleteButton = $('<a/>', { href: '#', class: 'editor-button editor-button-small', style: 'margin-top:7px; margin-left:5px;' }).appendTo(finalSpan)
@@ -80,7 +88,7 @@
80
88
 
81
89
  $('#node-input-add-column').click(function () {
82
90
  generateColumn($('#node-input-column-container').children().length + 1, {})
83
- $('#node-input-column-container-div').scrollTop($('#node-input-column-column-div').get(0).scrollHeight)
91
+ $('#node-input-column-container-div').scrollTop($('#node-input-column-container-div').get(0).scrollHeight)
84
92
  })
85
93
 
86
94
  for (let i = 0; i < this.columns?.length; i++) {
@@ -130,6 +138,14 @@
130
138
  <label for="node-input-maxrows"><i class="fa fa-tag"></i> <span data-i18n="ui-table.label.maxRows"></label>
131
139
  <input type="number" id="node-input-maxrows">
132
140
  </div>
141
+ <div class="form-row">
142
+ <label for="node-input-selectionType"><i class="fa fa-crosshairs"></i> <span data-i18n="ui-table.label.selection"></span></label>
143
+ <select id="node-input-selectionType">
144
+ <option value="none" data-i18n="ui-table.selection.none"></option>
145
+ <option value="click" data-i18n="ui-table.selection.click"></option>
146
+ <option value="checkbox" data-i18n="ui-table.selection.checkbox"></option>
147
+ </select>
148
+ </div>
133
149
  <div class="form-row" style="display:flex;">
134
150
  <label for="node-input-width" style="vertical-align:top"><i class="fa fa-list-alt"></i> <span data-i18n="ui-table.label.columns"></label>
135
151
  <div class="form-row node-input-column-container-row" style="margin-bottom: 0px; width:calc(70% + 15px);">
@@ -1,3 +1,5 @@
1
+ const datastore = require('../store/data.js')
2
+
1
3
  module.exports = function (RED) {
2
4
  function TableNode (config) {
3
5
  const node = this
@@ -21,7 +23,14 @@ module.exports = function (RED) {
21
23
  }
22
24
 
23
25
  // inform the dashboard UI that we are adding this node
24
- group.register(node, config)
26
+ group.register(node, config, {
27
+ onAction: true,
28
+ onInput: function (msg) {
29
+ // store the latest msg passed to node
30
+ datastore.save(group.getBase(), node, msg)
31
+ // do nothing else - do not pass the message on
32
+ }
33
+ })
25
34
  }
26
35
 
27
36
  RED.nodes.registerType('ui-table', TableNode)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowfuse/node-red-dashboard",
3
- "version": "1.4.1",
3
+ "version": "1.4.2-603b043-202403211300.0",
4
4
  "description": "Dashboard 2.0 - 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"
@@ -1 +0,0 @@
1
- function t(r){return typeof r=="object"&&"length"in r?r:Array.from(r)}export{t as a};