Commit 7ff92cf5f44f633052c7cb8dfd53c701d823bc53

Authored by Igor Kulikov
Committed by GitHub
2 parents 5a4e1b0f 63a0421b

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

UI: extend table widget settings
@@ -19,8 +19,8 @@ @@ -19,8 +19,8 @@
19 "templateHtml": "<tb-alarms-table-widget \n [ctx]=\"ctx\">\n</tb-alarms-table-widget>", 19 "templateHtml": "<tb-alarms-table-widget \n [ctx]=\"ctx\">\n</tb-alarms-table-widget>",
20 "templateCss": "", 20 "templateCss": "",
21 "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", 21 "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",
22 - "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}",  
23 - "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}", 22 + "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}",
  23 + "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}",
24 "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}" 24 "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}"
25 } 25 }
26 } 26 }
@@ -55,8 +55,8 @@ @@ -55,8 +55,8 @@
55 "templateHtml": "<tb-timeseries-table-widget \n [ctx]=\"ctx\">\n</tb-timeseries-table-widget>", 55 "templateHtml": "<tb-timeseries-table-widget \n [ctx]=\"ctx\">\n</tb-timeseries-table-widget>",
56 "templateCss": "", 56 "templateCss": "",
57 "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}", 57 "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}",
58 - "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}",  
59 - "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}", 58 + "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}",
  59 + "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}",
60 "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\"}" 60 "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\"}"
61 } 61 }
62 }, 62 },
@@ -127,8 +127,8 @@ @@ -127,8 +127,8 @@
127 "templateHtml": "<tb-entities-table-widget \n [ctx]=\"ctx\">\n</tb-entities-table-widget>", 127 "templateHtml": "<tb-entities-table-widget \n [ctx]=\"ctx\">\n</tb-entities-table-widget>",
128 "templateCss": "", 128 "templateCss": "",
129 "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", 129 "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",
130 - "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}",  
131 - "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}", 130 + "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}",
  131 + "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}",
132 "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;\"}]}]}" 132 "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;\"}]}]}"
133 } 133 }
134 }, 134 },
@@ -120,12 +120,13 @@ @@ -120,12 +120,13 @@
120 </div> 120 </div>
121 </mat-cell> 121 </mat-cell>
122 </ng-container> 122 </ng-container>
123 - <mat-header-row [ngClass]="{'mat-row-select': enableSelection}" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> 123 + <mat-header-row [ngClass]="{'mat-row-select': enableSelection}" *matHeaderRowDef="displayedColumns; sticky: enableStickyHeader"></mat-header-row>
124 <mat-row [ngClass]="{'mat-row-select': enableSelection, 124 <mat-row [ngClass]="{'mat-row-select': enableSelection,
125 'mat-selected': alarmsDatasource.isSelected(alarm), 125 'mat-selected': alarmsDatasource.isSelected(alarm),
126 'tb-current-entity': alarmsDatasource.isCurrentAlarm(alarm), 126 'tb-current-entity': alarmsDatasource.isCurrentAlarm(alarm),
127 'invisible': alarmsDatasource.dataLoading}" 127 'invisible': alarmsDatasource.dataLoading}"
128 *matRowDef="let alarm; columns: displayedColumns;" 128 *matRowDef="let alarm; columns: displayedColumns;"
  129 + [ngStyle]="rowStyle(alarm)"
129 (click)="onRowClick($event, alarm)"></mat-row> 130 (click)="onRowClick($event, alarm)"></mat-row>
130 </table> 131 </table>
131 <span [fxShow]="(alarmsDatasource.isEmpty() | async) && !alarmsDatasource.dataLoading" 132 <span [fxShow]="(alarmsDatasource.isEmpty() | async) && !alarmsDatasource.dataLoading"
@@ -60,7 +60,11 @@ import { @@ -60,7 +60,11 @@ import {
60 getAlarmValue, 60 getAlarmValue,
61 getCellContentInfo, 61 getCellContentInfo,
62 getCellStyleInfo, 62 getCellStyleInfo,
  63 + getColumnDefaultVisibility,
  64 + getColumnSelectionAvailability,
63 getColumnWidth, 65 getColumnWidth,
  66 + getRowStyleInfo,
  67 + RowStyleInfo,
64 TableWidgetDataKeySettings, 68 TableWidgetDataKeySettings,
65 TableWidgetSettings, 69 TableWidgetSettings,
66 widthStyle 70 widthStyle
@@ -108,10 +112,11 @@ import { entityFields } from '@shared/models/entity.models'; @@ -108,10 +112,11 @@ import { entityFields } from '@shared/models/entity.models';
108 112
109 interface AlarmsTableWidgetSettings extends TableWidgetSettings { 113 interface AlarmsTableWidgetSettings extends TableWidgetSettings {
110 alarmsTitle: string; 114 alarmsTitle: string;
  115 + enableSelectColumnDisplay: boolean;
  116 + defaultSortOrder: string;
111 enableSelection: boolean; 117 enableSelection: boolean;
112 enableStatusFilter?: boolean; 118 enableStatusFilter?: boolean;
113 enableFilter: boolean; 119 enableFilter: boolean;
114 - enableStickyAction: boolean;  
115 displayDetails: boolean; 120 displayDetails: boolean;
116 allowAcknowledgment: boolean; 121 allowAcknowledgment: boolean;
117 allowClear: boolean; 122 allowClear: boolean;
@@ -139,6 +144,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @@ -139,6 +144,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
139 144
140 public enableSelection = true; 145 public enableSelection = true;
141 public displayPagination = true; 146 public displayPagination = true;
  147 + public enableStickyHeader = true;
142 public enableStickyAction = false; 148 public enableStickyAction = false;
143 public pageSizeOptions; 149 public pageSizeOptions;
144 public pageLink: AlarmDataPageLink; 150 public pageLink: AlarmDataPageLink;
@@ -165,6 +171,10 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @@ -165,6 +171,10 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
165 private contentsInfo: {[key: string]: CellContentInfo} = {}; 171 private contentsInfo: {[key: string]: CellContentInfo} = {};
166 private stylesInfo: {[key: string]: CellStyleInfo} = {}; 172 private stylesInfo: {[key: string]: CellStyleInfo} = {};
167 private columnWidth: {[key: string]: string} = {}; 173 private columnWidth: {[key: string]: string} = {};
  174 + private columnDefaultVisibility: {[key: string]: boolean} = {};
  175 + private columnSelectionAvailability: {[key: string]: boolean} = {};
  176 +
  177 + private rowStylesInfo: RowStyleInfo;
168 178
169 private searchAction: WidgetAction = { 179 private searchAction: WidgetAction = {
170 name: 'action.search', 180 name: 'action.search',
@@ -311,6 +321,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @@ -311,6 +321,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
311 321
312 this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true; 322 this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true;
313 this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true; 323 this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true;
  324 + this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true;
314 this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : false; 325 this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : false;
315 this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true; 326 this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true;
316 let enableFilter; 327 let enableFilter;
@@ -323,6 +334,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @@ -323,6 +334,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
323 } 334 }
324 this.alarmFilterAction.show = enableFilter; 335 this.alarmFilterAction.show = enableFilter;
325 336
  337 + this.rowStylesInfo = getRowStyleInfo(this.settings, 'alarm, ctx');
  338 +
326 const pageSize = this.settings.defaultPageSize; 339 const pageSize = this.settings.defaultPageSize;
327 if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) { 340 if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) {
328 this.defaultPageSize = pageSize; 341 this.defaultPageSize = pageSize;
@@ -383,18 +396,21 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @@ -383,18 +396,21 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
383 keySettings.columnWidth = '120px'; 396 keySettings.columnWidth = '120px';
384 } 397 }
385 } 398 }
386 - this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings); 399 + this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings, 'value, alarm, ctx');
387 this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx'); 400 this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx');
388 this.contentsInfo[dataKey.def].units = dataKey.units; 401 this.contentsInfo[dataKey.def].units = dataKey.units;
389 this.contentsInfo[dataKey.def].decimals = dataKey.decimals; 402 this.contentsInfo[dataKey.def].decimals = dataKey.decimals;
390 this.columnWidth[dataKey.def] = getColumnWidth(keySettings); 403 this.columnWidth[dataKey.def] = getColumnWidth(keySettings);
  404 + this.columnDefaultVisibility[dataKey.def] = getColumnDefaultVisibility(keySettings);
  405 + this.columnSelectionAvailability[dataKey.def] = getColumnSelectionAvailability(keySettings);
391 this.columns.push(dataKey); 406 this.columns.push(dataKey);
392 407
393 if (dataKey.type !== DataKeyType.alarm) { 408 if (dataKey.type !== DataKeyType.alarm) {
394 latestDataKeys.push(dataKey); 409 latestDataKeys.push(dataKey);
395 } 410 }
396 }); 411 });
397 - this.displayedColumns.push(...this.columns.map(column => column.def)); 412 + this.displayedColumns.push(...this.columns.filter(column => this.columnDefaultVisibility[column.def])
  413 + .map(column => column.def));
398 } 414 }
399 if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) { 415 if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) {
400 this.defaultSortOrder = this.utils.customTranslation(this.settings.defaultSortOrder, this.settings.defaultSortOrder); 416 this.defaultSortOrder = this.utils.customTranslation(this.settings.defaultSortOrder, this.settings.defaultSortOrder);
@@ -450,7 +466,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @@ -450,7 +466,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
450 return { 466 return {
451 title: column.title, 467 title: column.title,
452 def: column.def, 468 def: column.def,
453 - display: this.displayedColumns.indexOf(column.def) > -1 469 + display: this.displayedColumns.indexOf(column.def) > -1,
  470 + selectable: this.columnSelectionAvailability[column.def]
454 }; 471 };
455 }); 472 });
456 473
@@ -464,7 +481,9 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @@ -464,7 +481,9 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
464 if (this.enableSelection) { 481 if (this.enableSelection) {
465 this.displayedColumns.unshift('select'); 482 this.displayedColumns.unshift('select');
466 } 483 }
467 - this.displayedColumns.push('actions'); 484 + if (this.actionCellDescriptors.length) {
  485 + this.displayedColumns.push('actions');
  486 + }
468 } 487 }
469 } as DisplayColumnsPanelData 488 } as DisplayColumnsPanelData
470 }, 489 },
@@ -590,6 +609,30 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @@ -590,6 +609,30 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
590 return widthStyle(columnWidth); 609 return widthStyle(columnWidth);
591 } 610 }
592 611
  612 + public rowStyle(alarm: AlarmDataInfo): any {
  613 + let style: any = {};
  614 + if (alarm) {
  615 + if (this.rowStylesInfo.useRowStyleFunction && this.rowStylesInfo.rowStyleFunction) {
  616 + try {
  617 + style = this.rowStylesInfo.rowStyleFunction(alarm, this.ctx);
  618 + if (!isObject(style)) {
  619 + throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`);
  620 + }
  621 + if (Array.isArray(style)) {
  622 + throw new TypeError(`Array instead of style object`);
  623 + }
  624 + } catch (e) {
  625 + style = {};
  626 + console.warn(`Row style function in widget '${this.ctx.widgetTitle}' ` +
  627 + `returns '${e}'. Please check your row style function.`);
  628 + }
  629 + } else {
  630 + style = {};
  631 + }
  632 + }
  633 + return style;
  634 + }
  635 +
593 public cellStyle(alarm: AlarmDataInfo, key: EntityColumn): any { 636 public cellStyle(alarm: AlarmDataInfo, key: EntityColumn): any {
594 let style: any = {}; 637 let style: any = {};
595 if (alarm && key) { 638 if (alarm && key) {
@@ -597,7 +640,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @@ -597,7 +640,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
597 const value = getAlarmValue(alarm, key); 640 const value = getAlarmValue(alarm, key);
598 if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) { 641 if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) {
599 try { 642 try {
600 - style = styleInfo.cellStyleFunction(value); 643 + style = styleInfo.cellStyleFunction(value, alarm, this.ctx);
601 if (!isObject(style)) { 644 if (!isObject(style)) {
602 throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`); 645 throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`);
603 } 646 }
@@ -639,6 +682,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit, @@ -639,6 +682,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
639 return ''; 682 return '';
640 683
641 } else { 684 } else {
  685 + content = this.utils.customTranslation(content, content);
642 switch (typeof content) { 686 switch (typeof content) {
643 case 'string': 687 case 'string':
644 return this.domSanitizer.bypassSecurityTrustHtml(content); 688 return this.domSanitizer.bypassSecurityTrustHtml(content);
@@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
17 --> 17 -->
18 <div fxLayout="column" class="mat-content mat-padding"> 18 <div fxLayout="column" class="mat-content mat-padding">
19 <label class="tb-title" translate>entity.columns-to-display</label> 19 <label class="tb-title" translate>entity.columns-to-display</label>
20 - <mat-checkbox style="padding-bottom: 8px;" [(ngModel)]="column.display" *ngFor="let column of columns" 20 + <mat-checkbox style="padding-bottom: 8px;" [(ngModel)]="column.display" *ngFor="let column of (columns | selectableColumns)"
21 (ngModelChange)="update()"> 21 (ngModelChange)="update()">
22 {{ column.title }} 22 {{ column.title }}
23 </mat-checkbox> 23 </mat-checkbox>
@@ -47,7 +47,7 @@ @@ -47,7 +47,7 @@
47 [ngStyle]="cellStyle(entity, column)"> 47 [ngStyle]="cellStyle(entity, column)">
48 </mat-cell> 48 </mat-cell>
49 </ng-container> 49 </ng-container>
50 - <ng-container matColumnDef="actions" stickyEnd> 50 + <ng-container matColumnDef="actions" [stickyEnd]="enableStickyAction">
51 <mat-header-cell *matHeaderCellDef [ngStyle.gt-md]="{ minWidth: (actionCellDescriptors.length * 40) + 'px', 51 <mat-header-cell *matHeaderCellDef [ngStyle.gt-md]="{ minWidth: (actionCellDescriptors.length * 40) + 'px',
52 maxWidth: (actionCellDescriptors.length * 40) + 'px', 52 maxWidth: (actionCellDescriptors.length * 40) + 'px',
53 width: (actionCellDescriptors.length * 40) + 'px' }"> 53 width: (actionCellDescriptors.length * 40) + 'px' }">
@@ -81,10 +81,11 @@ @@ -81,10 +81,11 @@
81 </div> 81 </div>
82 </mat-cell> 82 </mat-cell>
83 </ng-container> 83 </ng-container>
84 - <mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> 84 + <mat-header-row *matHeaderRowDef="displayedColumns; sticky: enableStickyHeader"></mat-header-row>
85 <mat-row [ngClass]="{'tb-current-entity': entityDatasource.isCurrentEntity(entity), 85 <mat-row [ngClass]="{'tb-current-entity': entityDatasource.isCurrentEntity(entity),
86 'invisible': entityDatasource.dataLoading}" 86 'invisible': entityDatasource.dataLoading}"
87 *matRowDef="let entity; columns: displayedColumns;" 87 *matRowDef="let entity; columns: displayedColumns;"
  88 + [ngStyle]="rowStyle(entity)"
88 (click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row> 89 (click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row>
89 </table> 90 </table>
90 <span [fxShow]="(entityDatasource.isEmpty() | async) && !entityDatasource.dataLoading" 91 <span [fxShow]="(entityDatasource.isEmpty() | async) && !entityDatasource.dataLoading"
@@ -65,8 +65,12 @@ import { @@ -65,8 +65,12 @@ import {
65 fromEntityColumnDef, 65 fromEntityColumnDef,
66 getCellContentInfo, 66 getCellContentInfo,
67 getCellStyleInfo, 67 getCellStyleInfo,
  68 + getColumnDefaultVisibility,
  69 + getColumnSelectionAvailability,
68 getColumnWidth, 70 getColumnWidth,
69 getEntityValue, 71 getEntityValue,
  72 + getRowStyleInfo,
  73 + RowStyleInfo,
70 TableWidgetDataKeySettings, 74 TableWidgetDataKeySettings,
71 TableWidgetSettings, 75 TableWidgetSettings,
72 widthStyle 76 widthStyle
@@ -92,6 +96,8 @@ import { DatePipe } from '@angular/common'; @@ -92,6 +96,8 @@ import { DatePipe } from '@angular/common';
92 96
93 interface EntitiesTableWidgetSettings extends TableWidgetSettings { 97 interface EntitiesTableWidgetSettings extends TableWidgetSettings {
94 entitiesTitle: string; 98 entitiesTitle: string;
  99 + enableSelectColumnDisplay: boolean;
  100 + defaultSortOrder: string;
95 displayEntityName: boolean; 101 displayEntityName: boolean;
96 entityNameColumnTitle: string; 102 entityNameColumnTitle: string;
97 displayEntityLabel: boolean; 103 displayEntityLabel: boolean;
@@ -114,6 +120,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -114,6 +120,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
114 @ViewChild(MatSort) sort: MatSort; 120 @ViewChild(MatSort) sort: MatSort;
115 121
116 public displayPagination = true; 122 public displayPagination = true;
  123 + public enableStickyHeader = true;
  124 + public enableStickyAction = true;
117 public pageSizeOptions; 125 public pageSizeOptions;
118 public pageLink: EntityDataPageLink; 126 public pageLink: EntityDataPageLink;
119 public sortOrderProperty: string; 127 public sortOrderProperty: string;
@@ -135,6 +143,10 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -135,6 +143,10 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
135 private contentsInfo: {[key: string]: CellContentInfo} = {}; 143 private contentsInfo: {[key: string]: CellContentInfo} = {};
136 private stylesInfo: {[key: string]: CellStyleInfo} = {}; 144 private stylesInfo: {[key: string]: CellStyleInfo} = {};
137 private columnWidth: {[key: string]: string} = {}; 145 private columnWidth: {[key: string]: string} = {};
  146 + private columnDefaultVisibility: {[key: string]: boolean} = {};
  147 + private columnSelectionAvailability: {[key: string]: boolean} = {};
  148 +
  149 + private rowStylesInfo: RowStyleInfo;
138 150
139 private searchAction: WidgetAction = { 151 private searchAction: WidgetAction = {
140 name: 'action.search', 152 name: 'action.search',
@@ -231,8 +243,12 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -231,8 +243,12 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
231 243
232 this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true; 244 this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true;
233 this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true; 245 this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true;
  246 + this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true;
  247 + this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : true;
234 this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true; 248 this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true;
235 249
  250 + this.rowStylesInfo = getRowStyleInfo(this.settings, 'entity, ctx');
  251 +
236 const pageSize = this.settings.defaultPageSize; 252 const pageSize = this.settings.defaultPageSize;
237 if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) { 253 if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) {
238 this.defaultPageSize = pageSize; 254 this.defaultPageSize = pageSize;
@@ -297,6 +313,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -297,6 +313,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
297 useCellStyleFunction: false 313 useCellStyleFunction: false
298 }; 314 };
299 this.columnWidth.entityName = '0px'; 315 this.columnWidth.entityName = '0px';
  316 + this.columnDefaultVisibility.entityName = true;
  317 + this.columnSelectionAvailability.entityName = true;
300 } 318 }
301 if (displayEntityLabel) { 319 if (displayEntityLabel) {
302 this.columns.push( 320 this.columns.push(
@@ -318,6 +336,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -318,6 +336,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
318 useCellStyleFunction: false 336 useCellStyleFunction: false
319 }; 337 };
320 this.columnWidth.entityLabel = '0px'; 338 this.columnWidth.entityLabel = '0px';
  339 + this.columnDefaultVisibility.entityLabel = true;
  340 + this.columnSelectionAvailability.entityLabel = true;
321 } 341 }
322 if (displayEntityType) { 342 if (displayEntityType) {
323 this.columns.push( 343 this.columns.push(
@@ -339,6 +359,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -339,6 +359,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
339 useCellStyleFunction: false 359 useCellStyleFunction: false
340 }; 360 };
341 this.columnWidth.entityType = '0px'; 361 this.columnWidth.entityType = '0px';
  362 + this.columnDefaultVisibility.entityType = true;
  363 + this.columnSelectionAvailability.entityType = true;
342 } 364 }
343 365
344 const dataKeys: Array<DataKey> = []; 366 const dataKeys: Array<DataKey> = [];
@@ -366,14 +388,17 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -366,14 +388,17 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
366 } 388 }
367 } 389 }
368 390
369 - this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings); 391 + this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings, 'value, entity, ctx');
370 this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, entity, ctx'); 392 this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, entity, ctx');
371 this.contentsInfo[dataKey.def].units = dataKey.units; 393 this.contentsInfo[dataKey.def].units = dataKey.units;
372 this.contentsInfo[dataKey.def].decimals = dataKey.decimals; 394 this.contentsInfo[dataKey.def].decimals = dataKey.decimals;
373 this.columnWidth[dataKey.def] = getColumnWidth(keySettings); 395 this.columnWidth[dataKey.def] = getColumnWidth(keySettings);
  396 + this.columnDefaultVisibility[dataKey.def] = getColumnDefaultVisibility(keySettings);
  397 + this.columnSelectionAvailability[dataKey.def] = getColumnSelectionAvailability(keySettings);
374 this.columns.push(dataKey); 398 this.columns.push(dataKey);
375 }); 399 });
376 - this.displayedColumns.push(...this.columns.map(column => column.def)); 400 + this.displayedColumns.push(...this.columns.filter(column => this.columnDefaultVisibility[column.def])
  401 + .map(column => column.def));
377 } 402 }
378 403
379 if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) { 404 if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) {
@@ -420,7 +445,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -420,7 +445,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
420 return { 445 return {
421 title: column.title, 446 title: column.title,
422 def: column.def, 447 def: column.def,
423 - display: this.displayedColumns.indexOf(column.def) > -1 448 + display: this.displayedColumns.indexOf(column.def) > -1,
  449 + selectable: this.columnSelectionAvailability[column.def]
424 }; 450 };
425 }); 451 });
426 452
@@ -431,7 +457,9 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -431,7 +457,9 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
431 columns, 457 columns,
432 columnsUpdated: (newColumns) => { 458 columnsUpdated: (newColumns) => {
433 this.displayedColumns = newColumns.filter(column => column.display).map(column => column.def); 459 this.displayedColumns = newColumns.filter(column => column.display).map(column => column.def);
434 - this.displayedColumns.push('actions'); 460 + if (this.actionCellDescriptors.length) {
  461 + this.displayedColumns.push('actions');
  462 + }
435 } 463 }
436 } as DisplayColumnsPanelData 464 } as DisplayColumnsPanelData
437 }, 465 },
@@ -507,6 +535,30 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -507,6 +535,30 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
507 return widthStyle(columnWidth); 535 return widthStyle(columnWidth);
508 } 536 }
509 537
  538 + public rowStyle(entity: EntityData): any {
  539 + let style: any = {};
  540 + if (entity) {
  541 + if (this.rowStylesInfo.useRowStyleFunction && this.rowStylesInfo.rowStyleFunction) {
  542 + try {
  543 + style = this.rowStylesInfo.rowStyleFunction(entity, this.ctx);
  544 + if (!isObject(style)) {
  545 + throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`);
  546 + }
  547 + if (Array.isArray(style)) {
  548 + throw new TypeError(`Array instead of style object`);
  549 + }
  550 + } catch (e) {
  551 + style = {};
  552 + console.warn(`Row style function in widget '${this.ctx.widgetTitle}' ` +
  553 + `returns '${e}'. Please check your row style function.`);
  554 + }
  555 + } else {
  556 + style = {};
  557 + }
  558 + }
  559 + return style;
  560 + }
  561 +
510 public cellStyle(entity: EntityData, key: EntityColumn): any { 562 public cellStyle(entity: EntityData, key: EntityColumn): any {
511 let style: any = {}; 563 let style: any = {};
512 if (entity && key) { 564 if (entity && key) {
@@ -514,7 +566,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -514,7 +566,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
514 const value = getEntityValue(entity, key); 566 const value = getEntityValue(entity, key);
515 if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) { 567 if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) {
516 try { 568 try {
517 - style = styleInfo.cellStyleFunction(value); 569 + style = styleInfo.cellStyleFunction(value, entity, this.ctx);
518 if (!isObject(style)) { 570 if (!isObject(style)) {
519 throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`); 571 throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`);
520 } 572 }
@@ -556,6 +608,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni @@ -556,6 +608,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
556 return ''; 608 return '';
557 609
558 } else { 610 } else {
  611 + content = this.utils.customTranslation(content, content);
559 switch (typeof content) { 612 switch (typeof content) {
560 case 'string': 613 case 'string':
561 return this.domSanitizer.bypassSecurityTrustHtml(content); 614 return this.domSanitizer.bypassSecurityTrustHtml(content);
@@ -24,20 +24,28 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; @@ -24,20 +24,28 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
24 24
25 const tinycolor = tinycolor_; 25 const tinycolor = tinycolor_;
26 26
  27 +type ColumnVisibilityOptions = 'visible' | 'hidden';
  28 +
  29 +type ColumnSelectionOptions = 'enabled' | 'disabled';
  30 +
27 export interface TableWidgetSettings { 31 export interface TableWidgetSettings {
28 enableSearch: boolean; 32 enableSearch: boolean;
29 - enableSelectColumnDisplay: boolean; 33 + enableStickyAction: boolean;
  34 + enableStickyHeader: boolean;
30 displayPagination: boolean; 35 displayPagination: boolean;
31 defaultPageSize: number; 36 defaultPageSize: number;
32 - defaultSortOrder: string; 37 + useRowStyleFunction: boolean;
  38 + rowStyleFunction?: string;
33 } 39 }
34 40
35 export interface TableWidgetDataKeySettings { 41 export interface TableWidgetDataKeySettings {
36 columnWidth?: string; 42 columnWidth?: string;
37 useCellStyleFunction: boolean; 43 useCellStyleFunction: boolean;
38 - cellStyleFunction: string; 44 + cellStyleFunction?: string;
39 useCellContentFunction: boolean; 45 useCellContentFunction: boolean;
40 - cellContentFunction: string; 46 + cellContentFunction?: string;
  47 + defaultColumnVisibility: ColumnVisibilityOptions;
  48 + columnSelectionToDisplay: ColumnSelectionOptions;
41 } 49 }
42 50
43 export interface EntityData { 51 export interface EntityData {
@@ -58,6 +66,7 @@ export interface DisplayColumn { @@ -58,6 +66,7 @@ export interface DisplayColumn {
58 title: string; 66 title: string;
59 def: string; 67 def: string;
60 display: boolean; 68 display: boolean;
  69 + selectable: boolean;
61 } 70 }
62 71
63 export type CellContentFunction = (...args: any[]) => string; 72 export type CellContentFunction = (...args: any[]) => string;
@@ -69,13 +78,20 @@ export interface CellContentInfo { @@ -69,13 +78,20 @@ export interface CellContentInfo {
69 decimals?: number; 78 decimals?: number;
70 } 79 }
71 80
72 -export type CellStyleFunction = (value: any) => any; 81 +export type CellStyleFunction = (...args: any[]) => any;
73 82
74 export interface CellStyleInfo { 83 export interface CellStyleInfo {
75 useCellStyleFunction: boolean; 84 useCellStyleFunction: boolean;
76 cellStyleFunction?: CellStyleFunction; 85 cellStyleFunction?: CellStyleFunction;
77 } 86 }
78 87
  88 +export type RowStyleFunction = (...args: any[]) => any;
  89 +
  90 +export interface RowStyleInfo {
  91 + useRowStyleFunction: boolean;
  92 + rowStyleFunction?: RowStyleFunction;
  93 +}
  94 +
79 95
80 export function entityDataSortOrderFromString(strSortOrder: string, columns: EntityColumn[]): EntityDataSortOrder { 96 export function entityDataSortOrderFromString(strSortOrder: string, columns: EntityColumn[]): EntityDataSortOrder {
81 if (!strSortOrder && !strSortOrder.length) { 97 if (!strSortOrder && !strSortOrder.length) {
@@ -196,14 +212,35 @@ export function getAlarmValue(alarm: AlarmDataInfo, key: EntityColumn) { @@ -196,14 +212,35 @@ export function getAlarmValue(alarm: AlarmDataInfo, key: EntityColumn) {
196 } 212 }
197 } 213 }
198 214
199 -export function getCellStyleInfo(keySettings: TableWidgetDataKeySettings): CellStyleInfo { 215 +export function getRowStyleInfo(settings: TableWidgetSettings, ...args: string[]): RowStyleInfo {
  216 + let rowStyleFunction: RowStyleFunction = null;
  217 + let useRowStyleFunction = false;
  218 +
  219 + if (settings.useRowStyleFunction === true) {
  220 + if (isDefined(settings.rowStyleFunction) && settings.rowStyleFunction.length > 0) {
  221 + try {
  222 + rowStyleFunction = new Function(...args, settings.rowStyleFunction) as RowStyleFunction;
  223 + useRowStyleFunction = true;
  224 + } catch (e) {
  225 + rowStyleFunction = null;
  226 + useRowStyleFunction = false;
  227 + }
  228 + }
  229 + }
  230 + return {
  231 + useRowStyleFunction,
  232 + rowStyleFunction
  233 + };
  234 +}
  235 +
  236 +export function getCellStyleInfo(keySettings: TableWidgetDataKeySettings, ...args: string[]): CellStyleInfo {
200 let cellStyleFunction: CellStyleFunction = null; 237 let cellStyleFunction: CellStyleFunction = null;
201 let useCellStyleFunction = false; 238 let useCellStyleFunction = false;
202 239
203 if (keySettings.useCellStyleFunction === true) { 240 if (keySettings.useCellStyleFunction === true) {
204 if (isDefined(keySettings.cellStyleFunction) && keySettings.cellStyleFunction.length > 0) { 241 if (isDefined(keySettings.cellStyleFunction) && keySettings.cellStyleFunction.length > 0) {
205 try { 242 try {
206 - cellStyleFunction = new Function('value', keySettings.cellStyleFunction) as CellStyleFunction; 243 + cellStyleFunction = new Function(...args, keySettings.cellStyleFunction) as CellStyleFunction;
207 useCellStyleFunction = true; 244 useCellStyleFunction = true;
208 } catch (e) { 245 } catch (e) {
209 cellStyleFunction = null; 246 cellStyleFunction = null;
@@ -251,6 +288,13 @@ export function widthStyle(width: string): any { @@ -251,6 +288,13 @@ export function widthStyle(width: string): any {
251 return widthStyleObj; 288 return widthStyleObj;
252 } 289 }
253 290
  291 +export function getColumnDefaultVisibility(keySettings: TableWidgetDataKeySettings): boolean {
  292 + return !(isDefined(keySettings.defaultColumnVisibility) && keySettings.defaultColumnVisibility === 'hidden');
  293 +}
  294 +
  295 +export function getColumnSelectionAvailability(keySettings: TableWidgetDataKeySettings): boolean {
  296 + return !(isDefined(keySettings.columnSelectionToDisplay) && keySettings.columnSelectionToDisplay === 'disabled');
  297 +}
254 298
255 299
256 export function constructTableCssString(widgetConfig: WidgetConfig): string { 300 export function constructTableCssString(widgetConfig: WidgetConfig): string {
@@ -48,17 +48,17 @@ @@ -48,17 +48,17 @@
48 <mat-header-cell *matHeaderCellDef mat-sort-header>Timestamp</mat-header-cell> 48 <mat-header-cell *matHeaderCellDef mat-sort-header>Timestamp</mat-header-cell>
49 <mat-cell *matCellDef="let row;" 49 <mat-cell *matCellDef="let row;"
50 [innerHTML]="cellContent(source, 0, row, row[0])" 50 [innerHTML]="cellContent(source, 0, row, row[0])"
51 - [ngStyle]="cellStyle(source, 0, row[0])"> 51 + [ngStyle]="cellStyle(source, 0, row, row[0])">
52 </mat-cell> 52 </mat-cell>
53 </ng-container> 53 </ng-container>
54 <ng-container [matColumnDef]="h.index + ''" *ngFor="let h of source.header; trackBy: trackByColumnIndex;"> 54 <ng-container [matColumnDef]="h.index + ''" *ngFor="let h of source.header; trackBy: trackByColumnIndex;">
55 <mat-header-cell *matHeaderCellDef mat-sort-header> {{ h.dataKey.label }} </mat-header-cell> 55 <mat-header-cell *matHeaderCellDef mat-sort-header> {{ h.dataKey.label }} </mat-header-cell>
56 <mat-cell *matCellDef="let row;" 56 <mat-cell *matCellDef="let row;"
57 [innerHTML]="cellContent(source, h.index, row, row[h.index])" 57 [innerHTML]="cellContent(source, h.index, row, row[h.index])"
58 - [ngStyle]="cellStyle(source, h.index, row[h.index])"> 58 + [ngStyle]="cellStyle(source, h.index, row, row[h.index])">
59 </mat-cell> 59 </mat-cell>
60 </ng-container> 60 </ng-container>
61 - <ng-container matColumnDef="actions" stickyEnd> 61 + <ng-container matColumnDef="actions" [stickyEnd]="enableStickyAction">
62 <mat-header-cell *matHeaderCellDef [ngStyle.gt-md]="{ minWidth: (actionCellDescriptors.length * 40) + 'px', 62 <mat-header-cell *matHeaderCellDef [ngStyle.gt-md]="{ minWidth: (actionCellDescriptors.length * 40) + 'px',
63 maxWidth: (actionCellDescriptors.length * 40) + 'px', 63 maxWidth: (actionCellDescriptors.length * 40) + 'px',
64 width: (actionCellDescriptors.length * 40) + 'px' }"> 64 width: (actionCellDescriptors.length * 40) + 'px' }">
@@ -92,8 +92,9 @@ @@ -92,8 +92,9 @@
92 </div> 92 </div>
93 </mat-cell> 93 </mat-cell>
94 </ng-container> 94 </ng-container>
95 - <mat-header-row *matHeaderRowDef="source.displayedColumns; sticky: true"></mat-header-row> 95 + <mat-header-row *matHeaderRowDef="source.displayedColumns; sticky: enableStickyHeader"></mat-header-row>
96 <mat-row *matRowDef="let row; columns: source.displayedColumns;" 96 <mat-row *matRowDef="let row; columns: source.displayedColumns;"
  97 + [ngStyle]="rowStyle(source, row)"
97 (click)="onRowClick($event, row)"></mat-row> 98 (click)="onRowClick($event, row)"></mat-row>
98 </table> 99 </table>
99 <span [fxShow]="source.timeseriesDatasource.isEmpty() | async" 100 <span [fxShow]="source.timeseriesDatasource.isEmpty() | async"
@@ -57,17 +57,17 @@ import { @@ -57,17 +57,17 @@ import {
57 constructTableCssString, 57 constructTableCssString,
58 getCellContentInfo, 58 getCellContentInfo,
59 getCellStyleInfo, 59 getCellStyleInfo,
60 - TableWidgetDataKeySettings 60 + getRowStyleInfo,
  61 + RowStyleInfo,
  62 + TableWidgetDataKeySettings, TableWidgetSettings
61 } from '@home/components/widget/lib/table-widget.models'; 63 } from '@home/components/widget/lib/table-widget.models';
62 import { Overlay } from '@angular/cdk/overlay'; 64 import { Overlay } from '@angular/cdk/overlay';
63 import { SubscriptionEntityInfo } from '@core/api/widget-api.models'; 65 import { SubscriptionEntityInfo } from '@core/api/widget-api.models';
64 import { DatePipe } from '@angular/common'; 66 import { DatePipe } from '@angular/common';
65 67
66 -interface TimeseriesTableWidgetSettings { 68 +export interface TimeseriesTableWidgetSettings extends TableWidgetSettings {
67 showTimestamp: boolean; 69 showTimestamp: boolean;
68 showMilliseconds: boolean; 70 showMilliseconds: boolean;
69 - displayPagination: boolean;  
70 - defaultPageSize: number;  
71 hideEmptyLines: boolean; 71 hideEmptyLines: boolean;
72 } 72 }
73 73
@@ -111,6 +111,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI @@ -111,6 +111,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
111 @ViewChildren(MatSort) sorts: QueryList<MatSort>; 111 @ViewChildren(MatSort) sorts: QueryList<MatSort>;
112 112
113 public displayPagination = true; 113 public displayPagination = true;
  114 + public enableStickyHeader = true;
  115 + public enableStickyAction = true;
114 public pageSizeOptions; 116 public pageSizeOptions;
115 public textSearchMode = false; 117 public textSearchMode = false;
116 public textSearch: string = null; 118 public textSearch: string = null;
@@ -129,6 +131,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI @@ -129,6 +131,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
129 public showTimestamp = true; 131 public showTimestamp = true;
130 private dateFormatFilter: string; 132 private dateFormatFilter: string;
131 133
  134 + private rowStylesInfo: RowStyleInfo;
  135 +
132 private subscriptions: Subscription[] = []; 136 private subscriptions: Subscription[] = [];
133 137
134 private searchAction: WidgetAction = { 138 private searchAction: WidgetAction = {
@@ -196,11 +200,16 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI @@ -196,11 +200,16 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
196 200
197 this.actionCellDescriptors = this.ctx.actionsApi.getActionDescriptors('actionCellButton'); 201 this.actionCellDescriptors = this.ctx.actionsApi.getActionDescriptors('actionCellButton');
198 202
  203 + this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true;
199 this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true; 204 this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true;
  205 + this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true;
  206 + this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : true;
200 this.hideEmptyLines = isDefined(this.settings.hideEmptyLines) ? this.settings.hideEmptyLines : false; 207 this.hideEmptyLines = isDefined(this.settings.hideEmptyLines) ? this.settings.hideEmptyLines : false;
201 this.showTimestamp = this.settings.showTimestamp !== false; 208 this.showTimestamp = this.settings.showTimestamp !== false;
202 this.dateFormatFilter = (this.settings.showMilliseconds !== true) ? 'yyyy-MM-dd HH:mm:ss' : 'yyyy-MM-dd HH:mm:ss.SSS'; 209 this.dateFormatFilter = (this.settings.showMilliseconds !== true) ? 'yyyy-MM-dd HH:mm:ss' : 'yyyy-MM-dd HH:mm:ss.SSS';
203 210
  211 + this.rowStylesInfo = getRowStyleInfo(this.settings, 'rowData, ctx');
  212 +
204 const pageSize = this.settings.defaultPageSize; 213 const pageSize = this.settings.defaultPageSize;
205 if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) { 214 if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) {
206 this.defaultPageSize = pageSize; 215 this.defaultPageSize = pageSize;
@@ -258,13 +267,15 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI @@ -258,13 +267,15 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
258 }); 267 });
259 source.displayedColumns.push(index + ''); 268 source.displayedColumns.push(index + '');
260 source.rowDataTemplate[dataKey.label] = null; 269 source.rowDataTemplate[dataKey.label] = null;
261 - source.stylesInfo.push(getCellStyleInfo(keySettings)); 270 + source.stylesInfo.push(getCellStyleInfo(keySettings, 'value, rowData, ctx'));
262 const cellContentInfo = getCellContentInfo(keySettings, 'value, rowData, ctx'); 271 const cellContentInfo = getCellContentInfo(keySettings, 'value, rowData, ctx');
263 cellContentInfo.units = dataKey.units; 272 cellContentInfo.units = dataKey.units;
264 cellContentInfo.decimals = dataKey.decimals; 273 cellContentInfo.decimals = dataKey.decimals;
265 source.contentsInfo.push(cellContentInfo); 274 source.contentsInfo.push(cellContentInfo);
266 } 275 }
267 - source.displayedColumns.push('actions'); 276 + if (this.actionCellDescriptors.length) {
  277 + source.displayedColumns.push('actions');
  278 + }
268 const tsDatasource = new TimeseriesDatasource(source, this.hideEmptyLines, this.dateFormatFilter, this.datePipe); 279 const tsDatasource = new TimeseriesDatasource(source, this.hideEmptyLines, this.dateFormatFilter, this.datePipe);
269 tsDatasource.dataUpdated(this.data); 280 tsDatasource.dataUpdated(this.data);
270 this.sources.push(source); 281 this.sources.push(source);
@@ -376,13 +387,43 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI @@ -376,13 +387,43 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
376 return source.datasource.entityId; 387 return source.datasource.entityId;
377 } 388 }
378 389
379 - public cellStyle(source: TimeseriesTableSource, index: number, value: any): any { 390 + public rowStyle(source: TimeseriesTableSource, row: TimeseriesRow): any {
  391 + let style: any = {};
  392 + if (this.rowStylesInfo.useRowStyleFunction && this.rowStylesInfo.rowStyleFunction) {
  393 + try {
  394 + const rowData = source.rowDataTemplate;
  395 + rowData.Timestamp = row[0];
  396 + source.header.forEach((headerInfo) => {
  397 + rowData[headerInfo.dataKey.name] = row[headerInfo.index];
  398 + });
  399 + style = this.rowStylesInfo.rowStyleFunction(rowData, this.ctx);
  400 + if (!isObject(style)) {
  401 + throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`);
  402 + }
  403 + if (Array.isArray(style)) {
  404 + throw new TypeError(`Array instead of style object`);
  405 + }
  406 + } catch (e) {
  407 + style = {};
  408 + console.warn(`Row style function in widget ` +
  409 + `'${this.ctx.widgetConfig.title}' returns '${e}'. Please check your row style function.`);
  410 + }
  411 + }
  412 + return style;
  413 + }
  414 +
  415 + public cellStyle(source: TimeseriesTableSource, index: number, row: TimeseriesRow, value: any): any {
380 let style: any = {}; 416 let style: any = {};
381 if (index > 0) { 417 if (index > 0) {
382 const styleInfo = source.stylesInfo[index - 1]; 418 const styleInfo = source.stylesInfo[index - 1];
383 if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) { 419 if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) {
384 try { 420 try {
385 - style = styleInfo.cellStyleFunction(value); 421 + const rowData = source.rowDataTemplate;
  422 + rowData.Timestamp = row[0];
  423 + source.header.forEach((headerInfo) => {
  424 + rowData[headerInfo.dataKey.name] = row[headerInfo.index];
  425 + });
  426 + style = styleInfo.cellStyleFunction(value, rowData, this.ctx);
386 if (!isObject(style)) { 427 if (!isObject(style)) {
387 throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`); 428 throw new TypeError(`${style === null ? 'null' : typeof style} instead of style object`);
388 } 429 }
@@ -425,6 +466,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI @@ -425,6 +466,7 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
425 if (!isDefined(content)) { 466 if (!isDefined(content)) {
426 return ''; 467 return '';
427 } else { 468 } else {
  469 + content = this.utils.customTranslation(content, content);
428 switch (typeof content) { 470 switch (typeof content) {
429 case 'string': 471 case 'string':
430 return this.domSanitizer.bypassSecurityTrustHtml(content); 472 return this.domSanitizer.bypassSecurityTrustHtml(content);
@@ -21,3 +21,4 @@ export * from './milliseconds-to-time-string.pipe'; @@ -21,3 +21,4 @@ export * from './milliseconds-to-time-string.pipe';
21 export * from './nospace.pipe'; 21 export * from './nospace.pipe';
22 export * from './truncate.pipe'; 22 export * from './truncate.pipe';
23 export * from './file-size.pipe'; 23 export * from './file-size.pipe';
  24 +export * from './selectable-columns.pipe';
  1 +///
  2 +/// Copyright © 2016-2021 The Thingsboard Authors
  3 +///
  4 +/// Licensed under the Apache License, Version 2.0 (the "License");
  5 +/// you may not use this file except in compliance with the License.
  6 +/// You may obtain a copy of the License at
  7 +///
  8 +/// http://www.apache.org/licenses/LICENSE-2.0
  9 +///
  10 +/// Unless required by applicable law or agreed to in writing, software
  11 +/// distributed under the License is distributed on an "AS IS" BASIS,
  12 +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 +/// See the License for the specific language governing permissions and
  14 +/// limitations under the License.
  15 +///
  16 +
  17 +import { Pipe, PipeTransform } from '@angular/core';
  18 +import { DisplayColumn } from '@home/components/widget/lib/table-widget.models';
  19 +
  20 +@Pipe({ name: 'selectableColumns' })
  21 +export class SelectableColumnsPipe implements PipeTransform {
  22 + transform(allColumns: DisplayColumn[]): DisplayColumn[] {
  23 + return allColumns.filter(column => column.selectable);
  24 + }
  25 +}
@@ -139,6 +139,7 @@ import { ContactComponent } from '@shared/components/contact.component'; @@ -139,6 +139,7 @@ import { ContactComponent } from '@shared/components/contact.component';
139 import { TimezoneSelectComponent } from '@shared/components/time/timezone-select.component'; 139 import { TimezoneSelectComponent } from '@shared/components/time/timezone-select.component';
140 import { FileSizePipe } from '@shared/pipe/file-size.pipe'; 140 import { FileSizePipe } from '@shared/pipe/file-size.pipe';
141 import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-search.component'; 141 import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-search.component';
  142 +import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe';
142 143
143 @NgModule({ 144 @NgModule({
144 providers: [ 145 providers: [
@@ -223,6 +224,7 @@ import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle- @@ -223,6 +224,7 @@ import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-
223 TruncatePipe, 224 TruncatePipe,
224 TbJsonPipe, 225 TbJsonPipe,
225 FileSizePipe, 226 FileSizePipe,
  227 + SelectableColumnsPipe,
226 KeyboardShortcutPipe, 228 KeyboardShortcutPipe,
227 TbJsonToStringDirective, 229 TbJsonToStringDirective,
228 JsonObjectEditDialogComponent, 230 JsonObjectEditDialogComponent,
@@ -393,6 +395,7 @@ import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle- @@ -393,6 +395,7 @@ import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-
393 TbJsonPipe, 395 TbJsonPipe,
394 KeyboardShortcutPipe, 396 KeyboardShortcutPipe,
395 FileSizePipe, 397 FileSizePipe,
  398 + SelectableColumnsPipe,
396 TranslateModule, 399 TranslateModule,
397 JsonObjectEditDialogComponent, 400 JsonObjectEditDialogComponent,
398 HistorySelectorComponent, 401 HistorySelectorComponent,