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 19 "templateHtml": "<tb-alarms-table-widget \n [ctx]=\"ctx\">\n</tb-alarms-table-widget>",
20 20 "templateCss": "",
21 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 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 55 "templateHtml": "<tb-timeseries-table-widget \n [ctx]=\"ctx\">\n</tb-timeseries-table-widget>",
56 56 "templateCss": "",
57 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 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 127 "templateHtml": "<tb-entities-table-widget \n [ctx]=\"ctx\">\n</tb-entities-table-widget>",
128 128 "templateCss": "",
129 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 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 120 </div>
121 121 </mat-cell>
122 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 124 <mat-row [ngClass]="{'mat-row-select': enableSelection,
125 125 'mat-selected': alarmsDatasource.isSelected(alarm),
126 126 'tb-current-entity': alarmsDatasource.isCurrentAlarm(alarm),
127 127 'invisible': alarmsDatasource.dataLoading}"
128 128 *matRowDef="let alarm; columns: displayedColumns;"
  129 + [ngStyle]="rowStyle(alarm)"
129 130 (click)="onRowClick($event, alarm)"></mat-row>
130 131 </table>
131 132 <span [fxShow]="(alarmsDatasource.isEmpty() | async) && !alarmsDatasource.dataLoading"
... ...
... ... @@ -60,7 +60,11 @@ import {
60 60 getAlarmValue,
61 61 getCellContentInfo,
62 62 getCellStyleInfo,
  63 + getColumnDefaultVisibility,
  64 + getColumnSelectionAvailability,
63 65 getColumnWidth,
  66 + getRowStyleInfo,
  67 + RowStyleInfo,
64 68 TableWidgetDataKeySettings,
65 69 TableWidgetSettings,
66 70 widthStyle
... ... @@ -108,10 +112,11 @@ import { entityFields } from '@shared/models/entity.models';
108 112
109 113 interface AlarmsTableWidgetSettings extends TableWidgetSettings {
110 114 alarmsTitle: string;
  115 + enableSelectColumnDisplay: boolean;
  116 + defaultSortOrder: string;
111 117 enableSelection: boolean;
112 118 enableStatusFilter?: boolean;
113 119 enableFilter: boolean;
114   - enableStickyAction: boolean;
115 120 displayDetails: boolean;
116 121 allowAcknowledgment: boolean;
117 122 allowClear: boolean;
... ... @@ -139,6 +144,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
139 144
140 145 public enableSelection = true;
141 146 public displayPagination = true;
  147 + public enableStickyHeader = true;
142 148 public enableStickyAction = false;
143 149 public pageSizeOptions;
144 150 public pageLink: AlarmDataPageLink;
... ... @@ -165,6 +171,10 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
165 171 private contentsInfo: {[key: string]: CellContentInfo} = {};
166 172 private stylesInfo: {[key: string]: CellStyleInfo} = {};
167 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 179 private searchAction: WidgetAction = {
170 180 name: 'action.search',
... ... @@ -311,6 +321,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
311 321
312 322 this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true;
313 323 this.displayPagination = isDefined(this.settings.displayPagination) ? this.settings.displayPagination : true;
  324 + this.enableStickyHeader = isDefined(this.settings.enableStickyHeader) ? this.settings.enableStickyHeader : true;
314 325 this.enableStickyAction = isDefined(this.settings.enableStickyAction) ? this.settings.enableStickyAction : false;
315 326 this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true;
316 327 let enableFilter;
... ... @@ -323,6 +334,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
323 334 }
324 335 this.alarmFilterAction.show = enableFilter;
325 336
  337 + this.rowStylesInfo = getRowStyleInfo(this.settings, 'alarm, ctx');
  338 +
326 339 const pageSize = this.settings.defaultPageSize;
327 340 if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) {
328 341 this.defaultPageSize = pageSize;
... ... @@ -383,18 +396,21 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
383 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 400 this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx');
388 401 this.contentsInfo[dataKey.def].units = dataKey.units;
389 402 this.contentsInfo[dataKey.def].decimals = dataKey.decimals;
390 403 this.columnWidth[dataKey.def] = getColumnWidth(keySettings);
  404 + this.columnDefaultVisibility[dataKey.def] = getColumnDefaultVisibility(keySettings);
  405 + this.columnSelectionAvailability[dataKey.def] = getColumnSelectionAvailability(keySettings);
391 406 this.columns.push(dataKey);
392 407
393 408 if (dataKey.type !== DataKeyType.alarm) {
394 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 415 if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) {
400 416 this.defaultSortOrder = this.utils.customTranslation(this.settings.defaultSortOrder, this.settings.defaultSortOrder);
... ... @@ -450,7 +466,8 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
450 466 return {
451 467 title: column.title,
452 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 481 if (this.enableSelection) {
465 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 488 } as DisplayColumnsPanelData
470 489 },
... ... @@ -590,6 +609,30 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
590 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 636 public cellStyle(alarm: AlarmDataInfo, key: EntityColumn): any {
594 637 let style: any = {};
595 638 if (alarm && key) {
... ... @@ -597,7 +640,7 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
597 640 const value = getAlarmValue(alarm, key);
598 641 if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) {
599 642 try {
600   - style = styleInfo.cellStyleFunction(value);
  643 + style = styleInfo.cellStyleFunction(value, alarm, this.ctx);
601 644 if (!isObject(style)) {
602 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 682 return '';
640 683
641 684 } else {
  685 + content = this.utils.customTranslation(content, content);
642 686 switch (typeof content) {
643 687 case 'string':
644 688 return this.domSanitizer.bypassSecurityTrustHtml(content);
... ...
... ... @@ -17,7 +17,7 @@
17 17 -->
18 18 <div fxLayout="column" class="mat-content mat-padding">
19 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 21 (ngModelChange)="update()">
22 22 {{ column.title }}
23 23 </mat-checkbox>
... ...
... ... @@ -47,7 +47,7 @@
47 47 [ngStyle]="cellStyle(entity, column)">
48 48 </mat-cell>
49 49 </ng-container>
50   - <ng-container matColumnDef="actions" stickyEnd>
  50 + <ng-container matColumnDef="actions" [stickyEnd]="enableStickyAction">
51 51 <mat-header-cell *matHeaderCellDef [ngStyle.gt-md]="{ minWidth: (actionCellDescriptors.length * 40) + 'px',
52 52 maxWidth: (actionCellDescriptors.length * 40) + 'px',
53 53 width: (actionCellDescriptors.length * 40) + 'px' }">
... ... @@ -81,10 +81,11 @@
81 81 </div>
82 82 </mat-cell>
83 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 85 <mat-row [ngClass]="{'tb-current-entity': entityDatasource.isCurrentEntity(entity),
86 86 'invisible': entityDatasource.dataLoading}"
87 87 *matRowDef="let entity; columns: displayedColumns;"
  88 + [ngStyle]="rowStyle(entity)"
88 89 (click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row>
89 90 </table>
90 91 <span [fxShow]="(entityDatasource.isEmpty() | async) && !entityDatasource.dataLoading"
... ...
... ... @@ -65,8 +65,12 @@ import {
65 65 fromEntityColumnDef,
66 66 getCellContentInfo,
67 67 getCellStyleInfo,
  68 + getColumnDefaultVisibility,
  69 + getColumnSelectionAvailability,
68 70 getColumnWidth,
69 71 getEntityValue,
  72 + getRowStyleInfo,
  73 + RowStyleInfo,
70 74 TableWidgetDataKeySettings,
71 75 TableWidgetSettings,
72 76 widthStyle
... ... @@ -92,6 +96,8 @@ import { DatePipe } from '@angular/common';
92 96
93 97 interface EntitiesTableWidgetSettings extends TableWidgetSettings {
94 98 entitiesTitle: string;
  99 + enableSelectColumnDisplay: boolean;
  100 + defaultSortOrder: string;
95 101 displayEntityName: boolean;
96 102 entityNameColumnTitle: string;
97 103 displayEntityLabel: boolean;
... ... @@ -114,6 +120,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
114 120 @ViewChild(MatSort) sort: MatSort;
115 121
116 122 public displayPagination = true;
  123 + public enableStickyHeader = true;
  124 + public enableStickyAction = true;
117 125 public pageSizeOptions;
118 126 public pageLink: EntityDataPageLink;
119 127 public sortOrderProperty: string;
... ... @@ -135,6 +143,10 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
135 143 private contentsInfo: {[key: string]: CellContentInfo} = {};
136 144 private stylesInfo: {[key: string]: CellStyleInfo} = {};
137 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 151 private searchAction: WidgetAction = {
140 152 name: 'action.search',
... ... @@ -231,8 +243,12 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
231 243
232 244 this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true;
233 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 248 this.columnDisplayAction.show = isDefined(this.settings.enableSelectColumnDisplay) ? this.settings.enableSelectColumnDisplay : true;
235 249
  250 + this.rowStylesInfo = getRowStyleInfo(this.settings, 'entity, ctx');
  251 +
236 252 const pageSize = this.settings.defaultPageSize;
237 253 if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) {
238 254 this.defaultPageSize = pageSize;
... ... @@ -297,6 +313,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
297 313 useCellStyleFunction: false
298 314 };
299 315 this.columnWidth.entityName = '0px';
  316 + this.columnDefaultVisibility.entityName = true;
  317 + this.columnSelectionAvailability.entityName = true;
300 318 }
301 319 if (displayEntityLabel) {
302 320 this.columns.push(
... ... @@ -318,6 +336,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
318 336 useCellStyleFunction: false
319 337 };
320 338 this.columnWidth.entityLabel = '0px';
  339 + this.columnDefaultVisibility.entityLabel = true;
  340 + this.columnSelectionAvailability.entityLabel = true;
321 341 }
322 342 if (displayEntityType) {
323 343 this.columns.push(
... ... @@ -339,6 +359,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
339 359 useCellStyleFunction: false
340 360 };
341 361 this.columnWidth.entityType = '0px';
  362 + this.columnDefaultVisibility.entityType = true;
  363 + this.columnSelectionAvailability.entityType = true;
342 364 }
343 365
344 366 const dataKeys: Array<DataKey> = [];
... ... @@ -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 392 this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, entity, ctx');
371 393 this.contentsInfo[dataKey.def].units = dataKey.units;
372 394 this.contentsInfo[dataKey.def].decimals = dataKey.decimals;
373 395 this.columnWidth[dataKey.def] = getColumnWidth(keySettings);
  396 + this.columnDefaultVisibility[dataKey.def] = getColumnDefaultVisibility(keySettings);
  397 + this.columnSelectionAvailability[dataKey.def] = getColumnSelectionAvailability(keySettings);
374 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 404 if (this.settings.defaultSortOrder && this.settings.defaultSortOrder.length) {
... ... @@ -420,7 +445,8 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
420 445 return {
421 446 title: column.title,
422 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 457 columns,
432 458 columnsUpdated: (newColumns) => {
433 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 464 } as DisplayColumnsPanelData
437 465 },
... ... @@ -507,6 +535,30 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
507 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 562 public cellStyle(entity: EntityData, key: EntityColumn): any {
511 563 let style: any = {};
512 564 if (entity && key) {
... ... @@ -514,7 +566,7 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
514 566 const value = getEntityValue(entity, key);
515 567 if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) {
516 568 try {
517   - style = styleInfo.cellStyleFunction(value);
  569 + style = styleInfo.cellStyleFunction(value, entity, this.ctx);
518 570 if (!isObject(style)) {
519 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 608 return '';
557 609
558 610 } else {
  611 + content = this.utils.customTranslation(content, content);
559 612 switch (typeof content) {
560 613 case 'string':
561 614 return this.domSanitizer.bypassSecurityTrustHtml(content);
... ...
... ... @@ -24,20 +24,28 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
24 24
25 25 const tinycolor = tinycolor_;
26 26
  27 +type ColumnVisibilityOptions = 'visible' | 'hidden';
  28 +
  29 +type ColumnSelectionOptions = 'enabled' | 'disabled';
  30 +
27 31 export interface TableWidgetSettings {
28 32 enableSearch: boolean;
29   - enableSelectColumnDisplay: boolean;
  33 + enableStickyAction: boolean;
  34 + enableStickyHeader: boolean;
30 35 displayPagination: boolean;
31 36 defaultPageSize: number;
32   - defaultSortOrder: string;
  37 + useRowStyleFunction: boolean;
  38 + rowStyleFunction?: string;
33 39 }
34 40
35 41 export interface TableWidgetDataKeySettings {
36 42 columnWidth?: string;
37 43 useCellStyleFunction: boolean;
38   - cellStyleFunction: string;
  44 + cellStyleFunction?: string;
39 45 useCellContentFunction: boolean;
40   - cellContentFunction: string;
  46 + cellContentFunction?: string;
  47 + defaultColumnVisibility: ColumnVisibilityOptions;
  48 + columnSelectionToDisplay: ColumnSelectionOptions;
41 49 }
42 50
43 51 export interface EntityData {
... ... @@ -58,6 +66,7 @@ export interface DisplayColumn {
58 66 title: string;
59 67 def: string;
60 68 display: boolean;
  69 + selectable: boolean;
61 70 }
62 71
63 72 export type CellContentFunction = (...args: any[]) => string;
... ... @@ -69,13 +78,20 @@ export interface CellContentInfo {
69 78 decimals?: number;
70 79 }
71 80
72   -export type CellStyleFunction = (value: any) => any;
  81 +export type CellStyleFunction = (...args: any[]) => any;
73 82
74 83 export interface CellStyleInfo {
75 84 useCellStyleFunction: boolean;
76 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 96 export function entityDataSortOrderFromString(strSortOrder: string, columns: EntityColumn[]): EntityDataSortOrder {
81 97 if (!strSortOrder && !strSortOrder.length) {
... ... @@ -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 237 let cellStyleFunction: CellStyleFunction = null;
201 238 let useCellStyleFunction = false;
202 239
203 240 if (keySettings.useCellStyleFunction === true) {
204 241 if (isDefined(keySettings.cellStyleFunction) && keySettings.cellStyleFunction.length > 0) {
205 242 try {
206   - cellStyleFunction = new Function('value', keySettings.cellStyleFunction) as CellStyleFunction;
  243 + cellStyleFunction = new Function(...args, keySettings.cellStyleFunction) as CellStyleFunction;
207 244 useCellStyleFunction = true;
208 245 } catch (e) {
209 246 cellStyleFunction = null;
... ... @@ -251,6 +288,13 @@ export function widthStyle(width: string): any {
251 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 300 export function constructTableCssString(widgetConfig: WidgetConfig): string {
... ...
... ... @@ -48,17 +48,17 @@
48 48 <mat-header-cell *matHeaderCellDef mat-sort-header>Timestamp</mat-header-cell>
49 49 <mat-cell *matCellDef="let row;"
50 50 [innerHTML]="cellContent(source, 0, row, row[0])"
51   - [ngStyle]="cellStyle(source, 0, row[0])">
  51 + [ngStyle]="cellStyle(source, 0, row, row[0])">
52 52 </mat-cell>
53 53 </ng-container>
54 54 <ng-container [matColumnDef]="h.index + ''" *ngFor="let h of source.header; trackBy: trackByColumnIndex;">
55 55 <mat-header-cell *matHeaderCellDef mat-sort-header> {{ h.dataKey.label }} </mat-header-cell>
56 56 <mat-cell *matCellDef="let row;"
57 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 59 </mat-cell>
60 60 </ng-container>
61   - <ng-container matColumnDef="actions" stickyEnd>
  61 + <ng-container matColumnDef="actions" [stickyEnd]="enableStickyAction">
62 62 <mat-header-cell *matHeaderCellDef [ngStyle.gt-md]="{ minWidth: (actionCellDescriptors.length * 40) + 'px',
63 63 maxWidth: (actionCellDescriptors.length * 40) + 'px',
64 64 width: (actionCellDescriptors.length * 40) + 'px' }">
... ... @@ -92,8 +92,9 @@
92 92 </div>
93 93 </mat-cell>
94 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 96 <mat-row *matRowDef="let row; columns: source.displayedColumns;"
  97 + [ngStyle]="rowStyle(source, row)"
97 98 (click)="onRowClick($event, row)"></mat-row>
98 99 </table>
99 100 <span [fxShow]="source.timeseriesDatasource.isEmpty() | async"
... ...
... ... @@ -57,17 +57,17 @@ import {
57 57 constructTableCssString,
58 58 getCellContentInfo,
59 59 getCellStyleInfo,
60   - TableWidgetDataKeySettings
  60 + getRowStyleInfo,
  61 + RowStyleInfo,
  62 + TableWidgetDataKeySettings, TableWidgetSettings
61 63 } from '@home/components/widget/lib/table-widget.models';
62 64 import { Overlay } from '@angular/cdk/overlay';
63 65 import { SubscriptionEntityInfo } from '@core/api/widget-api.models';
64 66 import { DatePipe } from '@angular/common';
65 67
66   -interface TimeseriesTableWidgetSettings {
  68 +export interface TimeseriesTableWidgetSettings extends TableWidgetSettings {
67 69 showTimestamp: boolean;
68 70 showMilliseconds: boolean;
69   - displayPagination: boolean;
70   - defaultPageSize: number;
71 71 hideEmptyLines: boolean;
72 72 }
73 73
... ... @@ -111,6 +111,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
111 111 @ViewChildren(MatSort) sorts: QueryList<MatSort>;
112 112
113 113 public displayPagination = true;
  114 + public enableStickyHeader = true;
  115 + public enableStickyAction = true;
114 116 public pageSizeOptions;
115 117 public textSearchMode = false;
116 118 public textSearch: string = null;
... ... @@ -129,6 +131,8 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
129 131 public showTimestamp = true;
130 132 private dateFormatFilter: string;
131 133
  134 + private rowStylesInfo: RowStyleInfo;
  135 +
132 136 private subscriptions: Subscription[] = [];
133 137
134 138 private searchAction: WidgetAction = {
... ... @@ -196,11 +200,16 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
196 200
197 201 this.actionCellDescriptors = this.ctx.actionsApi.getActionDescriptors('actionCellButton');
198 202
  203 + this.searchAction.show = isDefined(this.settings.enableSearch) ? this.settings.enableSearch : true;
199 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 207 this.hideEmptyLines = isDefined(this.settings.hideEmptyLines) ? this.settings.hideEmptyLines : false;
201 208 this.showTimestamp = this.settings.showTimestamp !== false;
202 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 213 const pageSize = this.settings.defaultPageSize;
205 214 if (isDefined(pageSize) && isNumber(pageSize) && pageSize > 0) {
206 215 this.defaultPageSize = pageSize;
... ... @@ -258,13 +267,15 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
258 267 });
259 268 source.displayedColumns.push(index + '');
260 269 source.rowDataTemplate[dataKey.label] = null;
261   - source.stylesInfo.push(getCellStyleInfo(keySettings));
  270 + source.stylesInfo.push(getCellStyleInfo(keySettings, 'value, rowData, ctx'));
262 271 const cellContentInfo = getCellContentInfo(keySettings, 'value, rowData, ctx');
263 272 cellContentInfo.units = dataKey.units;
264 273 cellContentInfo.decimals = dataKey.decimals;
265 274 source.contentsInfo.push(cellContentInfo);
266 275 }
267   - source.displayedColumns.push('actions');
  276 + if (this.actionCellDescriptors.length) {
  277 + source.displayedColumns.push('actions');
  278 + }
268 279 const tsDatasource = new TimeseriesDatasource(source, this.hideEmptyLines, this.dateFormatFilter, this.datePipe);
269 280 tsDatasource.dataUpdated(this.data);
270 281 this.sources.push(source);
... ... @@ -376,13 +387,43 @@ export class TimeseriesTableWidgetComponent extends PageComponent implements OnI
376 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 416 let style: any = {};
381 417 if (index > 0) {
382 418 const styleInfo = source.stylesInfo[index - 1];
383 419 if (styleInfo.useCellStyleFunction && styleInfo.cellStyleFunction) {
384 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 427 if (!isObject(style)) {
387 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 466 if (!isDefined(content)) {
426 467 return '';
427 468 } else {
  469 + content = this.utils.customTranslation(content, content);
428 470 switch (typeof content) {
429 471 case 'string':
430 472 return this.domSanitizer.bypassSecurityTrustHtml(content);
... ...
... ... @@ -21,3 +21,4 @@ export * from './milliseconds-to-time-string.pipe';
21 21 export * from './nospace.pipe';
22 22 export * from './truncate.pipe';
23 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 139 import { TimezoneSelectComponent } from '@shared/components/time/timezone-select.component';
140 140 import { FileSizePipe } from '@shared/pipe/file-size.pipe';
141 141 import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-search.component';
  142 +import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe';
142 143
143 144 @NgModule({
144 145 providers: [
... ... @@ -223,6 +224,7 @@ import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-
223 224 TruncatePipe,
224 225 TbJsonPipe,
225 226 FileSizePipe,
  227 + SelectableColumnsPipe,
226 228 KeyboardShortcutPipe,
227 229 TbJsonToStringDirective,
228 230 JsonObjectEditDialogComponent,
... ... @@ -393,6 +395,7 @@ import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-
393 395 TbJsonPipe,
394 396 KeyboardShortcutPipe,
395 397 FileSizePipe,
  398 + SelectableColumnsPipe,
396 399 TranslateModule,
397 400 JsonObjectEditDialogComponent,
398 401 HistorySelectorComponent,
... ...