Browse Source

Merge pull request #4235 from ChantsovaEkaterina/feature/extend-table-widget-settings

UI: extend table widget settings
pull/4240/head
Igor Kulikov 5 years ago
committed by GitHub
parent
commit
7ff92cf5f4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      application/src/main/data/json/system/widget_bundles/alarm_widgets.json
  2. 8
      application/src/main/data/json/system/widget_bundles/cards.json
  3. 3
      ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html
  4. 56
      ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts
  5. 2
      ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.html
  6. 5
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html
  7. 63
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts
  8. 58
      ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts
  9. 9
      ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html
  10. 58
      ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts
  11. 1
      ui-ngx/src/app/shared/pipe/public-api.ts
  12. 25
      ui-ngx/src/app/shared/pipe/selectable-columns.pipe.ts
  13. 3
      ui-ngx/src/app/shared/shared.module.ts

4
application/src/main/data/json/system/widget_bundles/alarm_widgets.json

@ -19,8 +19,8 @@
"templateHtml": "<tb-alarms-table-widget \n [ctx]=\"ctx\">\n</tb-alarms-table-widget>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.alarmsTableWidget.onDataUpdated();\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"AlarmTableSettings\",\n \"properties\": {\n \"alarmsTitle\": {\n \"title\": \"Alarms table title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"enableSelection\": {\n \"title\": \"Enable alarms selection\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSearch\": {\n \"title\": \"Enable alarms search\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSelectColumnDisplay\": {\n \"title\": \"Enable select columns to display\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableFilter\": {\n \"title\": \"Enable alarm filter\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyAction\": {\n \"title\": \"Always display actions column\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"displayDetails\": {\n \"title\": \"Display alarm details\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowAcknowledgment\": {\n \"title\": \"Allow alarms acknowledgment\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowClear\": {\n \"title\": \"Allow alarms clear\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultSortOrder\": {\n \"title\": \"Default sort order\",\n \"type\": \"string\",\n \"default\": \"-createdTime\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"alarmsTitle\",\n \"enableSelection\",\n \"enableSearch\",\n \"enableSelectColumnDisplay\",\n \"enableFilter\",\n \"enableStickyAction\",\n \"displayDetails\",\n \"allowAcknowledgment\",\n \"allowClear\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"defaultSortOrder\"\n ]\n}",
"dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"columnWidth\": {\n \"title\": \"Column width (px or %)\",\n \"type\": \"string\",\n \"default\": \"0px\"\n },\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, alarm, filter)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"columnWidth\",\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"AlarmTableSettings\",\n \"properties\": {\n \"alarmsTitle\": {\n \"title\": \"Alarms table title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"enableSelection\": {\n \"title\": \"Enable alarms selection\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSearch\": {\n \"title\": \"Enable alarms search\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSelectColumnDisplay\": {\n \"title\": \"Enable select columns to display\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableFilter\": {\n \"title\": \"Enable alarm filter\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyHeader\": {\n \"title\": \"Always display header\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyAction\": {\n \"title\": \"Always display actions column\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"displayDetails\": {\n \"title\": \"Display alarm details\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowAcknowledgment\": {\n \"title\": \"Allow alarms acknowledgment\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"allowClear\": {\n \"title\": \"Allow alarms clear\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultSortOrder\": {\n \"title\": \"Default sort order\",\n \"type\": \"string\",\n \"default\": \"-createdTime\"\n },\n \"useRowStyleFunction\": {\n \"title\": \"Use row style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"rowStyleFunction\": {\n \"title\": \"Row style function: f(alarm, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"alarmsTitle\",\n \"enableSelection\",\n \"enableSearch\",\n \"enableSelectColumnDisplay\",\n \"enableFilter\",\n \"enableStickyHeader\",\n \"enableStickyAction\",\n \"displayDetails\",\n \"allowAcknowledgment\",\n \"allowClear\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"defaultSortOrder\",\n \"useRowStyleFunction\",\n {\n \"key\": \"rowStyleFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useRowStyleFunction === true\"\n }\n ]\n}",
"dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"columnWidth\": {\n \"title\": \"Column width (px or %)\",\n \"type\": \"string\",\n \"default\": \"0px\"\n },\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value, alarm, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, alarm, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"defaultColumnVisibility\": {\n \"title\": \"Default column visibility\",\n \"type\": \"string\",\n \"default\": \"visible\"\n },\n \"columnSelectionToDisplay\": {\n \"title\": \"Column selection in 'Columns to Display'\",\n \"type\": \"string\",\n \"default\": \"enabled\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"columnWidth\",\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useCellStyleFunction === true\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useCellContentFunction === true\"\n },\n {\n \"key\": \"defaultColumnVisibility\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"visible\",\n \"label\": \"Visible\"\n },\n {\n \"value\": \"hidden\",\n \"label\": \"Hidden\"\n } \n ]\n },\n {\n \"key\": \"columnSelectionToDisplay\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"enabled\",\n \"label\": \"Enabled\"\n },\n {\n \"value\": \"disabled\",\n \"label\": \"Disabled\"\n } \n ]\n }\n ]\n}",
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"allowAcknowledgment\":true,\"allowClear\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"-createdTime\",\"enableSelectColumnDisplay\":true,\"enableStickyAction\":false,\"enableFilter\":true},\"title\":\"Alarms table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"alarmSource\":{\"type\":\"function\",\"dataKeys\":[{\"name\":\"createdTime\",\"type\":\"alarm\",\"label\":\"Created time\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.021092237451093787},{\"name\":\"originator\",\"type\":\"alarm\",\"label\":\"Originator\",\"color\":\"#4caf50\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.2780007688856758},{\"name\":\"type\",\"type\":\"alarm\",\"label\":\"Type\",\"color\":\"#f44336\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.7323586880398418},{\"name\":\"severity\",\"type\":\"alarm\",\"label\":\"Severity\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":false,\"useCellContentFunction\":false},\"_hash\":0.09927019860088193},{\"name\":\"status\",\"type\":\"alarm\",\"label\":\"Status\",\"color\":\"#607d8b\",\"settings\":{\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6588418951443418}],\"entityAliasId\":null,\"name\":\"alarms\"},\"alarmSearchStatus\":\"ANY\",\"alarmsPollingInterval\":5,\"showTitleIcon\":false,\"titleIcon\":\"more_horiz\",\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"displayTimewindow\":true,\"actions\":{},\"alarmStatusList\":[],\"alarmSeverityList\":[],\"alarmTypeList\":[],\"searchPropagatedAlarms\":false}"
}
}

8
application/src/main/data/json/system/widget_bundles/cards.json

@ -55,8 +55,8 @@
"templateHtml": "<tb-timeseries-table-widget \n [ctx]=\"ctx\">\n</tb-timeseries-table-widget>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeseriesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n ignoreDataUpdateOnIntervalTick: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"TimeseriesTableSettings\",\n \"properties\": {\n \"showTimestamp\": {\n \"title\": \"Display timestamp column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showMilliseconds\": {\n \"title\": \"Display timestamp milliseconds\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n }, \n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"hideEmptyLines\": {\n \"title\": \"Hide empty lines\",\n \"type\": \"boolean\",\n \"default\": false\n }\n },\n \"required\": []\n },\n \"form\": [\n \"showTimestamp\",\n \"showMilliseconds\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"hideEmptyLines\"\n ]\n}",
"dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"TimeseriesTableSettings\",\n \"properties\": {\n \"enableSearch\": {\n \"title\": \"Enable search\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyHeader\": {\n \"title\": \"Always display header\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyAction\": {\n \"title\": \"Always display actions column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showTimestamp\": {\n \"title\": \"Display timestamp column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"showMilliseconds\": {\n \"title\": \"Display timestamp milliseconds\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n }, \n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"hideEmptyLines\": {\n \"title\": \"Hide empty lines\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"disableStickyHeader\": {\n \"title\": \"Disable sticky header\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"useRowStyleFunction\": {\n \"title\": \"Use row style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"rowStyleFunction\": {\n \"title\": \"Row style function: f(rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"enableSearch\",\n \"enableStickyHeader\",\n \"enableStickyAction\",\n \"showTimestamp\",\n \"showMilliseconds\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"hideEmptyLines\",\n \"useRowStyleFunction\",\n {\n \"key\": \"rowStyleFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useRowStyleFunction === true\"\n }\n ]\n}",
"dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value, rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, rowData, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useCellStyleFunction === true\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useCellContentFunction === true\"\n }\n ]\n}",
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Temperature °C\",\"color\":\"#2196f3\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = (value + 60)/120 * 100;\\n var color = tinycolor.mix('blue', 'red', amount = percent);\\n color.setAlpha(.5);\\n return {\\n paddingLeft: '20px',\\n color: '#ffffff',\\n background: color.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 40 - 20;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -60) {\\n\\tvalue = -60;\\n} else if (value > 60) {\\n\\tvalue = 60;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Humidity, %\",\"color\":\"#ffc107\",\"settings\":{\"useCellStyleFunction\":true,\"cellStyleFunction\":\"if (value) {\\n var percent = value;\\n var backgroundColor = tinycolor('blue');\\n backgroundColor.setAlpha(value/100);\\n var color = 'blue';\\n if (value > 50) {\\n color = 'white';\\n }\\n \\n return {\\n paddingLeft: '20px',\\n color: color,\\n background: backgroundColor.toRgbString(),\\n fontSize: '18px'\\n };\\n} else {\\n return {};\\n}\",\"useCellContentFunction\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 20 - 10;\\nvar multiplier = Math.pow(10, 1 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 5) {\\n\\tvalue = 5;\\n} else if (value > 100) {\\n\\tvalue = 100;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":60000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"showTimestamp\":true,\"displayPagination\":true,\"defaultPageSize\":10},\"title\":\"Timeseries table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"widgetStyle\":{},\"actions\":{},\"showTitleIcon\":false,\"iconColor\":\"rgba(0, 0, 0, 0.87)\",\"iconSize\":\"24px\"}"
}
},
@ -127,8 +127,8 @@
"templateHtml": "<tb-entities-table-widget \n [ctx]=\"ctx\">\n</tb-entities-table-widget>",
"templateCss": "",
"controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.entitiesTableWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n hasDataPageLink: true,\n warnOnPageDataOverflow: false,\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'actionCellButton': {\n name: 'widget-action.action-cell-button',\n multiple: true\n },\n 'rowClick': {\n name: 'widget-action.row-click',\n multiple: false\n },\n 'rowDoubleClick': {\n name: 'widget-action.row-double-click',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"entitiesTitle\": {\n \"title\": \"Entities table title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"enableSearch\": {\n \"title\": \"Enable entities search\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSelectColumnDisplay\": {\n \"title\": \"Enable select columns to display\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayEntityName\": {\n \"title\": \"Display entity name column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"entityNameColumnTitle\": {\n \"title\": \"Entity name column title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"displayEntityLabel\": {\n \"title\": \"Display entity label column\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"entityLabelColumnTitle\": {\n \"title\": \"Entity label column title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"displayEntityType\": {\n \"title\": \"Display entity type column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultSortOrder\": {\n \"title\": \"Default sort order\",\n \"type\": \"string\",\n \"default\": \"entityName\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"entitiesTitle\",\n \"enableSearch\",\n \"enableSelectColumnDisplay\",\n \"displayEntityName\",\n \"entityNameColumnTitle\",\n \"displayEntityLabel\",\n \"entityLabelColumnTitle\",\n \"displayEntityType\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"defaultSortOrder\"\n ]\n}",
"dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"columnWidth\": {\n \"title\": \"Column width (px or %)\",\n \"type\": \"string\",\n \"default\": \"0px\"\n },\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, entity, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"columnWidth\",\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\"\n }\n ]\n}",
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EntitiesTableSettings\",\n \"properties\": {\n \"entitiesTitle\": {\n \"title\": \"Entities table title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"enableSearch\": {\n \"title\": \"Enable entities search\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableSelectColumnDisplay\": {\n \"title\": \"Enable select columns to display\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyHeader\": {\n \"title\": \"Always display header\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"enableStickyAction\": {\n \"title\": \"Always display actions column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayEntityName\": {\n \"title\": \"Display entity name column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"entityNameColumnTitle\": {\n \"title\": \"Entity name column title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"displayEntityLabel\": {\n \"title\": \"Display entity label column\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"entityLabelColumnTitle\": {\n \"title\": \"Entity label column title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"displayEntityType\": {\n \"title\": \"Display entity type column\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"displayPagination\": {\n \"title\": \"Display pagination\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"defaultPageSize\": {\n \"title\": \"Default page size\",\n \"type\": \"number\",\n \"default\": 10\n },\n \"defaultSortOrder\": {\n \"title\": \"Default sort order\",\n \"type\": \"string\",\n \"default\": \"entityName\"\n },\n \"useRowStyleFunction\": {\n \"title\": \"Use row style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"rowStyleFunction\": {\n \"title\": \"Row style function: f(entity, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"entitiesTitle\",\n \"enableSearch\",\n \"enableSelectColumnDisplay\",\n \"enableStickyHeader\",\n \"enableStickyAction\",\n \"displayEntityName\",\n \"entityNameColumnTitle\",\n \"displayEntityLabel\",\n \"entityLabelColumnTitle\",\n \"displayEntityType\",\n \"displayPagination\",\n \"defaultPageSize\",\n \"defaultSortOrder\",\n \"useRowStyleFunction\",\n {\n \"key\": \"rowStyleFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useRowStyleFunction === true\"\n }\n ]\n}",
"dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {\n \"columnWidth\": {\n \"title\": \"Column width (px or %)\",\n \"type\": \"string\",\n \"default\": \"0px\"\n },\n \"useCellStyleFunction\": {\n \"title\": \"Use cell style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellStyleFunction\": {\n \"title\": \"Cell style function: f(value, entity, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"useCellContentFunction\": {\n \"title\": \"Use cell content function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"cellContentFunction\": {\n \"title\": \"Cell content function: f(value, entity, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"defaultColumnVisibility\": {\n \"title\": \"Default column visibility\",\n \"type\": \"string\",\n \"default\": \"visible\"\n },\n \"columnSelectionToDisplay\": {\n \"title\": \"Column selection in 'Columns to Display'\",\n \"type\": \"string\",\n \"default\": \"enabled\"\n }\n },\n \"required\": []\n },\n \"form\": [\n \"columnWidth\",\n \"useCellStyleFunction\",\n {\n \"key\": \"cellStyleFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useCellStyleFunction === true\"\n },\n \"useCellContentFunction\",\n {\n \"key\": \"cellContentFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useCellContentFunction === true\"\n },\n {\n \"key\": \"defaultColumnVisibility\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"visible\",\n \"label\": \"Visible\"\n },\n {\n \"value\": \"hidden\",\n \"label\": \"Hidden\"\n } \n ]\n },\n {\n \"key\": \"columnSelectionToDisplay\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"enabled\",\n \"label\": \"Enabled\"\n },\n {\n \"value\": \"disabled\",\n \"label\": \"Disabled\"\n } \n ]\n }\n ]\n}",
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{\"enableSelection\":true,\"enableSearch\":true,\"displayDetails\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"defaultSortOrder\":\"entityName\",\"displayEntityName\":true,\"displayEntityType\":true},\"title\":\"Entities table\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Cos\",\"color\":\"#4caf50\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.8926244886945558,\"funcBody\":\"return Math.round(1000*Math.cos(time/5000));\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#f44336\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.6401141393938932,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}]}"
}
},

3
ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html

@ -120,12 +120,13 @@
</div>
</mat-cell>
</ng-container>
<mat-header-row [ngClass]="{'mat-row-select': enableSelection}" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-header-row [ngClass]="{'mat-row-select': enableSelection}" *matHeaderRowDef="displayedColumns; sticky: enableStickyHeader"></mat-header-row>
<mat-row [ngClass]="{'mat-row-select': enableSelection,
'mat-selected': alarmsDatasource.isSelected(alarm),
'tb-current-entity': alarmsDatasource.isCurrentAlarm(alarm),
'invisible': alarmsDatasource.dataLoading}"
*matRowDef="let alarm; columns: displayedColumns;"
[ngStyle]="rowStyle(alarm)"
(click)="onRowClick($event, alarm)"></mat-row>
</table>
<span [fxShow]="(alarmsDatasource.isEmpty() | async) && !alarmsDatasource.dataLoading"

56
ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts

@ -60,7 +60,11 @@ import {
getAlarmValue,
getCellContentInfo,
getCellStyleInfo,
getColumnDefaultVisibility,
getColumnSelectionAvailability,
getColumnWidth,
getRowStyleInfo,
RowStyleInfo,
TableWidgetDataKeySettings,
TableWidgetSettings,
widthStyle
@ -108,10 +112,11 @@ import { entityFields } from '@shared/models/entity.models';
interface AlarmsTableWidgetSettings extends TableWidgetSettings {
alarmsTitle: string;
enableSelectColumnDisplay: boolean;
defaultSortOrder: string;
enableSelection: boolean;
enableStatusFilter?: boolean;
enableFilter: boolean;
enableStickyAction: boolean;
displayDetails: boolean;
allowAcknowledgment: boolean;
allowClear: boolean;
@ -139,6 +144,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
public enableSelection = true;
public displayPagination = true;
public enableStickyHeader = true;
public enableStickyAction = false;
public pageSizeOptions;
public pageLink: AlarmDataPageLink;
@ -165,6 +171,10 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
private contentsInfo: {[key: string]: CellContentInfo} = {};
private stylesInfo: {[key: string]: CellStyleInfo} = {};
private columnWidth: {[key: string]: string} = {};
private columnDefaultVisibility: {[key: string]: boolean} = {};
private columnSelectionAvailability: {[key: string]: boolean} = {};
private rowStylesInfo: RowStyleInfo;
private searchAction: WidgetAction = {
name: 'action.search',
@ -311,6 +321,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true;
this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true;
this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true;
this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : false;
this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true;
let enableFilter;
@ -323,6 +334,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
}
this.alarmFilterAction.show = enableFilter;
this.rowStylesInfo = getRowStyleInfo(this.settings, 'alarm, ctx');
const pageSize = this.settings.defaultPageSize;
if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) {
this.defaultPageSize = pageSize;
@ -383,18 +396,21 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
keySettings.columnWidth = '120px';
}
}
this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings);
this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings, 'value, alarm, ctx');
this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx');
this.contentsInfo[dataKey.def].units = dataKey.units;
this.contentsInfo[dataKey.def].decimals = dataKey.decimals;
this.columnWidth[dataKey.def] = getColumnWidth(keySettings);
this.columnDefaultVisibility[dataKey.def] = getColumnDefaultVisibility(keySettings);
this.columnSelectionAvailability[dataKey.def] = getColumnSelectionAvailability(keySettings);
this.columns.push(dataKey);
if (dataKey.type !== DataKeyType.alarm) {
latestDataKeys.push(dataKey);
}
});
this.displayedColumns.push(...this.columns.map(column => column.def));
this.displayedColumns.push(...this.columns.filter(column => this.columnDefaultVisibility[column.def])
.map(column => column.def));
}
if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) {
this.defaultSortOrder = this.utils.customTranslation(this.settings.defaultSortOrder, this.settings.defaultSortOrder);
@ -450,7 +466,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
return {
title: column.title,
def: column.def,
display: this.displayedColumns.indexOf(column.def) > -1
display: this.displayedColumns.indexOf(column.def) > -1,
selectable: this.columnSelectionAvailability[column.def]
};
});
@ -464,7 +481,9 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
if (this.enableSelection) {
this.displayedColumns.unshift('select');
}
this.displayedColumns.push('actions');
if (this.actionCellDescriptors.length) {
this.displayedColumns.push('actions');
}
}
} as DisplayColumnsPanelData
},
@ -590,6 +609,30 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
return widthStyle(columnWidth);
}
public rowStyle(alarm: AlarmDataInfo): any {
let style: any = {};
if (alarm) {
if (this.rowStylesInfo.useRowStyleFunction && this.rowStylesInfo.rowStyleFunction) {
try {
style = this.rowStylesInfo.rowStyleFunction(alarm, this.ctx);
if (!isObject(style)) {
throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`);
}
if (Array.isArray(style)) {
throw new TypeError(`Array instead of style object`);
}
} catch (e) {
style = {};
console.warn(`Row style function in widget '${this.ctx.widgetTitle}' ` +
`returns '${e}'. Please check your row style function.`);
}
} else {
style = {};
}
}
return style;
}
public cellStyle(alarm: AlarmDataInfo, key: EntityColumn): any {
let style: any = {};
if (alarm && key) {
@ -597,7 +640,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
const value = getAlarmValue(alarm, key);
if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) {
try {
style = styleInfo.cellStyleFunction(value);
style = styleInfo.cellStyleFunction(value, alarm, this.ctx);
if (!isObject(style)) {
throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`);
}
@ -639,6 +682,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
return '';
} else {
content = this.utils.customTranslation(content, content);
switch (typeof content) {
case 'string':
return this.domSanitizer.bypassSecurityTrustHtml(content);

2
ui-ngx/src/app/modules/home/components/widget/lib/display-columns-panel.component.html

@ -17,7 +17,7 @@
-->
<div fxLayout="column" class="mat-content mat-padding">
<label class="tb-title" translate>entity.columns-to-display</label>
<mat-checkbox style="padding-bottom: 8px;" [(ngModel)]="column.display" *ngFor="let column of columns"
<mat-checkbox style="padding-bottom: 8px;" [(ngModel)]="column.display" *ngFor="let column of (columns | selectableColumns)"
(ngModelChange)="update()">
{{ column.title }}
</mat-checkbox>

5
ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html

@ -47,7 +47,7 @@
[ngStyle]="cellStyle(entity, column)">
</mat-cell>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
<ng-container matColumnDef="actions" [stickyEnd]="enableStickyAction">
<mat-header-cell *matHeaderCellDef [ngStyle.gt-md]="{ minWidth: (actionCellDescriptors.length * 40) + 'px',
maxWidth: (actionCellDescriptors.length * 40) + 'px',
width: (actionCellDescriptors.length * 40) + 'px' }">
@ -81,10 +81,11 @@
</div>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-header-row *matHeaderRowDef="displayedColumns; sticky: enableStickyHeader"></mat-header-row>
<mat-row [ngClass]="{'tb-current-entity': entityDatasource.isCurrentEntity(entity),
'invisible': entityDatasource.dataLoading}"
*matRowDef="let entity; columns: displayedColumns;"
[ngStyle]="rowStyle(entity)"
(click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row>
</table>
<span [fxShow]="(entityDatasource.isEmpty() | async) && !entityDatasource.dataLoading"

63
ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts

@ -65,8 +65,12 @@ import {
fromEntityColumnDef,
getCellContentInfo,
getCellStyleInfo,
getColumnDefaultVisibility,
getColumnSelectionAvailability,
getColumnWidth,
getEntityValue,
getRowStyleInfo,
RowStyleInfo,
TableWidgetDataKeySettings,
TableWidgetSettings,
widthStyle
@ -92,6 +96,8 @@ import { DatePipe } from '@angular/common';
interface EntitiesTableWidgetSettings extends TableWidgetSettings {
entitiesTitle: string;
enableSelectColumnDisplay: boolean;
defaultSortOrder: string;
displayEntityName: boolean;
entityNameColumnTitle: string;
displayEntityLabel: boolean;
@ -114,6 +120,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
@ViewChild(MatSort) sort: MatSort;
public displayPagination = true;
public enableStickyHeader = true;
public enableStickyAction = true;
public pageSizeOptions;
public pageLink: EntityDataPageLink;
public sortOrderProperty: string;
@ -135,6 +143,10 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
private contentsInfo: {[key: string]: CellContentInfo} = {};
private stylesInfo: {[key: string]: CellStyleInfo} = {};
private columnWidth: {[key: string]: string} = {};
private columnDefaultVisibility: {[key: string]: boolean} = {};
private columnSelectionAvailability: {[key: string]: boolean} = {};
private rowStylesInfo: RowStyleInfo;
private searchAction: WidgetAction = {
name: 'action.search',
@ -231,8 +243,12 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true;
this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true;
this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true;
this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : true;
this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true;
this.rowStylesInfo = getRowStyleInfo(this.settings, 'entity, ctx');
const pageSize = this.settings.defaultPageSize;
if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) {
this.defaultPageSize = pageSize;
@ -297,6 +313,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
useCellStyleFunction: false
};
this.columnWidth.entityName = '0px';
this.columnDefaultVisibility.entityName = true;
this.columnSelectionAvailability.entityName = true;
}
if (displayEntityLabel) {
this.columns.push(
@ -318,6 +336,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
useCellStyleFunction: false
};
this.columnWidth.entityLabel = '0px';
this.columnDefaultVisibility.entityLabel = true;
this.columnSelectionAvailability.entityLabel = true;
}
if (displayEntityType) {
this.columns.push(
@ -339,6 +359,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
useCellStyleFunction: false
};
this.columnWidth.entityType = '0px';
this.columnDefaultVisibility.entityType = true;
this.columnSelectionAvailability.entityType = true;
}
const dataKeys: Array<DataKey> = [];
@ -366,14 +388,17 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
}
}
this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings);
this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings, 'value, entity, ctx');
this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, entity, ctx');
this.contentsInfo[dataKey.def].units = dataKey.units;
this.contentsInfo[dataKey.def].decimals = dataKey.decimals;
this.columnWidth[dataKey.def] = getColumnWidth(keySettings);
this.columnDefaultVisibility[dataKey.def] = getColumnDefaultVisibility(keySettings);
this.columnSelectionAvailability[dataKey.def] = getColumnSelectionAvailability(keySettings);
this.columns.push(dataKey);
});
this.displayedColumns.push(...this.columns.map(column => column.def));
this.displayedColumns.push(...this.columns.filter(column => this.columnDefaultVisibility[column.def])
.map(column => column.def));
}
if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) {
@ -420,7 +445,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
return {
title: column.title,
def: column.def,
display: this.displayedColumns.indexOf(column.def) > -1
display: this.displayedColumns.indexOf(column.def) > -1,
selectable: this.columnSelectionAvailability[column.def]
};
});
@ -431,7 +457,9 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
columns,
columnsUpdated: (newColumns) => {
this.displayedColumns = newColumns.filter(column => column.display).map(column => column.def);
this.displayedColumns.push('actions');
if (this.actionCellDescriptors.length) {
this.displayedColumns.push('actions');
}
}
} as DisplayColumnsPanelData
},
@ -507,6 +535,30 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
return widthStyle(columnWidth);
}
public rowStyle(entity: EntityData): any {
let style: any = {};
if (entity) {
if (this.rowStylesInfo.useRowStyleFunction && this.rowStylesInfo.rowStyleFunction) {
try {
style = this.rowStylesInfo.rowStyleFunction(entity, this.ctx);
if (!isObject(style)) {
throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`);
}
if (Array.isArray(style)) {
throw new TypeError(`Array instead of style object`);
}
} catch (e) {
style = {};
console.warn(`Row style function in widget '${this.ctx.widgetTitle}' ` +
`returns '${e}'. Please check your row style function.`);
}
} else {
style = {};
}
}
return style;
}
public cellStyle(entity: EntityData, key: EntityColumn): any {
let style: any = {};
if (entity && key) {
@ -514,7 +566,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
const value = getEntityValue(entity, key);
if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) {
try {
style = styleInfo.cellStyleFunction(value);
style = styleInfo.cellStyleFunction(value, entity, this.ctx);
if (!isObject(style)) {
throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`);
}
@ -556,6 +608,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
return '';
} else {
content = this.utils.customTranslation(content, content);
switch (typeof content) {
case 'string':
return this.domSanitizer.bypassSecurityTrustHtml(content);

58
ui-ngx/src/app/modules/home/components/widget/lib/table-widget.models.ts

@ -24,20 +24,28 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
const tinycolor = tinycolor_;
type ColumnVisibilityOptions = 'visible' | 'hidden';
type ColumnSelectionOptions = 'enabled' | 'disabled';
export interface TableWidgetSettings {
enableSearch: boolean;
enableSelectColumnDisplay: boolean;
enableStickyAction: boolean;
enableStickyHeader: boolean;
displayPagination: boolean;
defaultPageSize: number;
defaultSortOrder: string;
useRowStyleFunction: boolean;
rowStyleFunction?: string;
}
export interface TableWidgetDataKeySettings {
columnWidth?: string;
useCellStyleFunction: boolean;
cellStyleFunction: string;
cellStyleFunction?: string;
useCellContentFunction: boolean;
cellContentFunction: string;
cellContentFunction?: string;
defaultColumnVisibility: ColumnVisibilityOptions;
columnSelectionToDisplay: ColumnSelectionOptions;
}
export interface EntityData {
@ -58,6 +66,7 @@ export interface DisplayColumn {
title: string;
def: string;
display: boolean;
selectable: boolean;
}
export type CellContentFunction = (...args: any[]) => string;
@ -69,13 +78,20 @@ export interface CellContentInfo {
decimals?: number;
}
export type CellStyleFunction = (value: any) => any;
export type CellStyleFunction = (...args: any[]) => any;
export interface CellStyleInfo {
useCellStyleFunction: boolean;
cellStyleFunction?: CellStyleFunction;
}
export type RowStyleFunction = (...args: any[]) => any;
export interface RowStyleInfo {
useRowStyleFunction: boolean;
rowStyleFunction?: RowStyleFunction;
}
export function entityDataSortOrderFromString(strSortOrder: string, columns: EntityColumn[]): EntityDataSortOrder {
if (!strSortOrder && !strSortOrder.length) {
@ -196,14 +212,35 @@ export function getAlarmValue(alarm: AlarmDataInfo, key: EntityColumn) {
}
}
export function getCellStyleInfo(keySettings: TableWidgetDataKeySettings): CellStyleInfo {
export function getRowStyleInfo(settings: TableWidgetSettings, ...args: string[]): RowStyleInfo {
let rowStyleFunction: RowStyleFunction = null;
let useRowStyleFunction = false;
if (settings.useRowStyleFunction === true) {
if (isDefined(settings.rowStyleFunction) && settings.rowStyleFunction.length > 0) {
try {
rowStyleFunction = new Function(...args, settings.rowStyleFunction) as RowStyleFunction;
useRowStyleFunction = true;
} catch (e) {
rowStyleFunction = null;
useRowStyleFunction = false;
}
}
}
return {
useRowStyleFunction,
rowStyleFunction
};
}
export function getCellStyleInfo(keySettings: TableWidgetDataKeySettings, ...args: string[]): CellStyleInfo {
let cellStyleFunction: CellStyleFunction = null;
let useCellStyleFunction = false;
if (keySettings.useCellStyleFunction === true) {
if (isDefined(keySettings.cellStyleFunction) && keySettings.cellStyleFunction.length > 0) {
try {
cellStyleFunction = new Function('value', keySettings.cellStyleFunction) as CellStyleFunction;
cellStyleFunction = new Function(...args, keySettings.cellStyleFunction) as CellStyleFunction;
useCellStyleFunction = true;
} catch (e) {
cellStyleFunction = null;
@ -251,6 +288,13 @@ export function widthStyle(width: string): any {
return widthStyleObj;
}
export function getColumnDefaultVisibility(keySettings: TableWidgetDataKeySettings): boolean {
return !(isDefined(keySettings.defaultColumnVisibility) && keySettings.defaultColumnVisibility === 'hidden');
}
export function getColumnSelectionAvailability(keySettings: TableWidgetDataKeySettings): boolean {
return !(isDefined(keySettings.columnSelectionToDisplay) && keySettings.columnSelectionToDisplay === 'disabled');
}
export function constructTableCssString(widgetConfig: WidgetConfig): string {

9
ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.html

@ -48,17 +48,17 @@
<mat-header-cell *matHeaderCellDef mat-sort-header>Timestamp</mat-header-cell>
<mat-cell *matCellDef="let row;"
[innerHTML]="cellContent(source, 0, row, row[0])"
[ngStyle]="cellStyle(source, 0, row[0])">
[ngStyle]="cellStyle(source, 0, row, row[0])">
</mat-cell>
</ng-container>
<ng-container [matColumnDef]="h.index + ''" *ngFor="let h of source.header; trackBy: trackByColumnIndex;">
<mat-header-cell *matHeaderCellDef mat-sort-header> {{ h.dataKey.label }} </mat-header-cell>
<mat-cell *matCellDef="let row;"
[innerHTML]="cellContent(source, h.index, row, row[h.index])"
[ngStyle]="cellStyle(source, h.index, row[h.index])">
[ngStyle]="cellStyle(source, h.index, row, row[h.index])">
</mat-cell>
</ng-container>
<ng-container matColumnDef="actions" stickyEnd>
<ng-container matColumnDef="actions" [stickyEnd]="enableStickyAction">
<mat-header-cell *matHeaderCellDef [ngStyle.gt-md]="{ minWidth: (actionCellDescriptors.length * 40) + 'px',
maxWidth: (actionCellDescriptors.length * 40) + 'px',
width: (actionCellDescriptors.length * 40) + 'px' }">
@ -92,8 +92,9 @@
</div>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="source.displayedColumns; sticky: true"></mat-header-row>
<mat-header-row *matHeaderRowDef="source.displayedColumns; sticky: enableStickyHeader"></mat-header-row>
<mat-row *matRowDef="let row; columns: source.displayedColumns;"
[ngStyle]="rowStyle(source, row)"
(click)="onRowClick($event, row)"></mat-row>
</table>
<span [fxShow]="source.timeseriesDatasource.isEmpty() | async"

58
ui-ngx/src/app/modules/home/components/widget/lib/timeseries-table-widget.component.ts

@ -57,17 +57,17 @@ import {
constructTableCssString,
getCellContentInfo,
getCellStyleInfo,
TableWidgetDataKeySettings
getRowStyleInfo,
RowStyleInfo,
TableWidgetDataKeySettings, TableWidgetSettings
} from '@home/components/widget/lib/table-widget.models';
import { Overlay } from '@angular/cdk/overlay';
import { SubscriptionEntityInfo } from '@core/api/widget-api.models';
import { DatePipe } from '@angular/common';
interface TimeseriesTableWidgetSettings {
export interface TimeseriesTableWidgetSettings extends TableWidgetSettings {
showTimestamp: boolean;
showMilliseconds: boolean;
displayPagination: boolean;
defaultPageSize: number;
hideEmptyLines: boolean;
}
@ -111,6 +111,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
@ViewChildren(MatSort) sorts: QueryList<MatSort>;
public displayPagination = true;
public enableStickyHeader = true;
public enableStickyAction = true;
public pageSizeOptions;
public textSearchMode = false;
public textSearch: string = null;
@ -129,6 +131,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
public showTimestamp = true;
private dateFormatFilter: string;
private rowStylesInfo: RowStyleInfo;
private subscriptions: Subscription[] = [];
private searchAction: WidgetAction = {
@ -196,11 +200,16 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
this.actionCellDescriptors = this.ctx.actionsApi.getActionDescriptors('actionCellButton');
this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true;
this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true;
this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true;
this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : true;
this.hideEmptyLines = isDefined(this.settings.hideEmptyLines) ? this.settings.hideEmptyLines : false;
this.showTimestamp = this.settings.showTimestamp !== false;
this.dateFormatFilter = (this.settings.showMilliseconds !== true) ? 'yyyy-MM-dd HH:mm:ss' : 'yyyy-MM-dd HH:mm:ss.SSS';
this.rowStylesInfo = getRowStyleInfo(this.settings, 'rowData, ctx');
const pageSize = this.settings.defaultPageSize;
if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) {
this.defaultPageSize = pageSize;
@ -258,13 +267,15 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
});
source.displayedColumns.push(index + '');
source.rowDataTemplate[dataKey.label] = null;
source.stylesInfo.push(getCellStyleInfo(keySettings));
source.stylesInfo.push(getCellStyleInfo(keySettings, 'value, rowData, ctx'));
const cellContentInfo = getCellContentInfo(keySettings, 'value, rowData, ctx');
cellContentInfo.units = dataKey.units;
cellContentInfo.decimals = dataKey.decimals;
source.contentsInfo.push(cellContentInfo);
}
source.displayedColumns.push('actions');
if (this.actionCellDescriptors.length) {
source.displayedColumns.push('actions');
}
const tsDatasource = new TimeseriesDatasource(source, this.hideEmptyLines, this.dateFormatFilter, this.datePipe);
tsDatasource.dataUpdated(this.data);
this.sources.push(source);
@ -376,13 +387,43 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
return source.datasource.entityId;
}
public cellStyle(source: TimeseriesTableSource, index: number, value: any): any {
public rowStyle(source: TimeseriesTableSource, row: TimeseriesRow): any {
let style: any = {};
if (this.rowStylesInfo.useRowStyleFunction && this.rowStylesInfo.rowStyleFunction) {
try {
const rowData = source.rowDataTemplate;
rowData.Timestamp = row[0];
source.header.forEach((headerInfo) => {
rowData[headerInfo.dataKey.name] = row[headerInfo.index];
});
style = this.rowStylesInfo.rowStyleFunction(rowData, this.ctx);
if (!isObject(style)) {
throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`);
}
if (Array.isArray(style)) {
throw new TypeError(`Array instead of style object`);
}
} catch (e) {
style = {};
console.warn(`Row style function in widget ` +
`'${this.ctx.widgetConfig.title}' returns '${e}'. Please check your row style function.`);
}
}
return style;
}
public cellStyle(source: TimeseriesTableSource, index: number, row: TimeseriesRow, value: any): any {
let style: any = {};
if (index > 0) {
const styleInfo = source.stylesInfo[index - 1];
if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) {
try {
style = styleInfo.cellStyleFunction(value);
const rowData = source.rowDataTemplate;
rowData.Timestamp = row[0];
source.header.forEach((headerInfo) => {
rowData[headerInfo.dataKey.name] = row[headerInfo.index];
});
style = styleInfo.cellStyleFunction(value, rowData, this.ctx);
if (!isObject(style)) {
throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`);
}
@ -425,6 +466,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
if (!isDefined(content)) {
return '';
} else {
content = this.utils.customTranslation(content, content);
switch (typeof content) {
case 'string':
return this.domSanitizer.bypassSecurityTrustHtml(content);

1
ui-ngx/src/app/shared/pipe/public-api.ts

@ -21,3 +21,4 @@ export * from './milliseconds-to-time-string.pipe';
export * from './nospace.pipe';
export * from './truncate.pipe';
export * from './file-size.pipe';
export * from './selectable-columns.pipe';

25
ui-ngx/src/app/shared/pipe/selectable-columns.pipe.ts

@ -0,0 +1,25 @@
///
/// Copyright © 2016-2021 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import { Pipe, PipeTransform } from '@angular/core';
import { DisplayColumn } from '@home/components/widget/lib/table-widget.models';
@Pipe({ name: 'selectableColumns' })
export class SelectableColumnsPipe implements PipeTransform {
transform(allColumns: DisplayColumn[]): DisplayColumn[] {
return allColumns.filter(column => column.selectable);
}
}

3
ui-ngx/src/app/shared/shared.module.ts

@ -139,6 +139,7 @@ import { ContactComponent } from '@shared/components/contact.component';
import { TimezoneSelectComponent } from '@shared/components/time/timezone-select.component';
import { FileSizePipe } from '@shared/pipe/file-size.pipe';
import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-search.component';
import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe';
@NgModule({
providers: [
@ -223,6 +224,7 @@ import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-
TruncatePipe,
TbJsonPipe,
FileSizePipe,
SelectableColumnsPipe,
KeyboardShortcutPipe,
TbJsonToStringDirective,
JsonObjectEditDialogComponent,
@ -393,6 +395,7 @@ import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-
TbJsonPipe,
KeyboardShortcutPipe,
FileSizePipe,
SelectableColumnsPipe,
TranslateModule,
JsonObjectEditDialogComponent,
HistorySelectorComponent,

Loading…
Cancel
Save