Showing
4 changed files
with
54 additions
and
466 deletions
... | ... | @@ -15,11 +15,11 @@ |
15 | 15 | "resources": [], |
16 | 16 | "templateHtml": "<tb-edges-overview-widget \n [ctx]=\"ctx\">\n</tb-edges-overview-widget>", |
17 | 17 | "templateCss": "", |
18 | - "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.edgesOverviewWidget.onDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n dataKeysOptional: true\n };\n}\n\nself.actionSources = function() {\n return {\n 'nodeSelected': {\n name: 'widget-action.node-selected',\n multiple: false\n }\n };\n}\n\nself.onDestroy = function() {\n}\n", | |
19 | - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EdgesOverviewSettings\",\n \"properties\": {\n \"nodeRelationQueryFunction\": {\n \"title\": \"Node relations query function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeHasChildrenFunction\": {\n \"title\": \"Node has children function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeOpenedFunction\": {\n \"title\": \"Default node opened function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeDisabledFunction\": {\n \"title\": \"Node disabled function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeIconFunction\": {\n \"title\": \"Node icon function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodeTextFunction\": {\n \"title\": \"Node text function: f(nodeCtx)\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"nodesSortFunction\": {\n \"title\": \"Nodes sort function: f(nodeCtx1, nodeCtx2)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": []\n },\n \"form\": [\n {\n \"key\": \"nodeRelationQueryFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeHasChildrenFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeOpenedFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeDisabledFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeIconFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodeTextFunction\",\n \"type\": \"javascript\"\n },\n {\n \"key\": \"nodesSortFunction\",\n \"type\": \"javascript\"\n }\n ]\n}", | |
20 | - "dataKeySettingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"DataKeySettings\",\n \"properties\": {},\n \"required\": []\n },\n \"form\": []\n}", | |
21 | - "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\":{\"nodeRelationQueryFunction\":\"/**\\n\\n// Function should return relations query object for current node used to fetch entity children.\\n// Function can return 'default' string value. In this case default relations query will be used.\\n\\n// The following example code will construct simple relations query that will fetch relations of type 'Contains'\\n// from the current entity.\\n\\nvar entity = nodeCtx.entity;\\nvar query = {\\n parameters: {\\n rootId: entity.id.id,\\n rootType: entity.id.entityType,\\n direction: \\\"FROM\\\",\\n maxLevel: 1\\n },\\n filters: [{\\n relationType: \\\"Contains\\\",\\n entityTypes: []\\n }]\\n};\\nreturn query;\\n\\n**/\\n\",\"nodeHasChildrenFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node has children (whether it can be expanded).\\n\\n// The following example code will restrict entities hierarchy expansion up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n// The next example code will restrict entities expansion according to the value of example 'nodeHasChildren' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeHasChildren') && data['nodeHasChildren'] !== null) {\\n return data['nodeHasChildren'] === 'true';\\n} else {\\n return true;\\n}\\n \\n**/\\n \",\"nodeTextFunction\":\"/**\\n\\n// Function should return text (can be HTML code) for the current node.\\n\\n// The following example code will generate node text consisting of entity name and temperature if temperature value is present in entity attributes/timeseries.\\n\\nvar data = nodeCtx.data;\\nvar entity = nodeCtx.entity;\\nvar text = entity.name;\\nif (data.hasOwnProperty('temperature') && data['temperature'] !== null) {\\n text += \\\" <b>\\\"+ data['temperature'] +\\\" °C</b>\\\";\\n}\\nreturn text;\\n\\n**/\",\"nodeIconFunction\":\"/** \\n\\n// Function should return node icon info object.\\n// Resulting object should contain either 'materialIcon' or 'iconUrl' property. \\n// Where:\\n - 'materialIcon' - name of the material icon to be used from the Material Icons Library (https://material.io/tools/icons);\\n - 'iconUrl' - url of the external image to be used as node icon.\\n// Function can return 'default' string value. In this case default icons according to entity type will be used.\\n\\n// The following example code shows how to use external image for devices which name starts with 'Test' and use \\n// default icons for the rest of entities.\\n\\nvar entity = nodeCtx.entity;\\nif (entity.id.entityType === 'DEVICE' && entity.name.startsWith('Test')) {\\n return {iconUrl: 'https://avatars1.githubusercontent.com/u/14793288?v=4&s=117'};\\n} else {\\n return 'default';\\n}\\n \\n**/\",\"nodeDisabledFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be disabled (not selectable).\\n\\n// The following example code will disable current node according to the value of example 'nodeDisabled' attribute.\\n\\nvar data = nodeCtx.data;\\nif (data.hasOwnProperty('nodeDisabled') && data['nodeDisabled'] !== null) {\\n return data['nodeDisabled'] === 'true';\\n} else {\\n return false;\\n}\\n \\n**/\\n\",\"nodesSortFunction\":\"/**\\n\\n// This function is used to sort nodes of the same level. Function should compare two nodes and return \\n// integer value: \\n// - less than 0 - sort nodeCtx1 to an index lower than nodeCtx2\\n// - 0 - leave nodeCtx1 and nodeCtx2 unchanged with respect to each other\\n// - greater than 0 - sort nodeCtx2 to an index lower than nodeCtx1\\n\\n// The following example code will sort entities first by entity type in alphabetical order then\\n// by entity name in alphabetical order.\\n\\nvar result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType);\\nif (result === 0) {\\n result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name);\\n}\\nreturn result;\\n \\n**/\",\"nodeOpenedFunction\":\"/**\\n\\n// Function should return boolean value indicating whether current node should be opened (expanded) when it first loaded.\\n\\n// The following example code will open by default nodes up to third level.\\n\\nreturn nodeCtx.level <= 2;\\n\\n**/\\n \"},\"title\":\"Edge Quick Overview Widget\",\"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;\"}]}],\"widgetStyle\":{},\"actions\":{}}" | |
18 | + "controllerScript": "self.onInit = function() {\n};\n\nself.onDestroy = function() {\n};\n", | |
19 | + "settingsSchema": "{}", | |
20 | + "dataKeySettingsSchema": "{}\n", | |
21 | + "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\":{},\"title\":\"Edges Quick Overview\",\"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;\"}]}],\"widgetStyle\":{},\"actions\":{}}" | |
22 | 22 | } |
23 | 23 | } |
24 | 24 | ] |
25 | -} | |
\ No newline at end of file | ||
25 | +} | ... | ... |
... | ... | @@ -17,37 +17,13 @@ |
17 | 17 | --> |
18 | 18 | <div class="tb-edges-overview tb-absolute-fill" tb-toast toastTarget="{{ toastTargetId }}"> |
19 | 19 | <div fxFlex fxLayout="column" class="tb-absolute-fill"> |
20 | - <mat-toolbar class="mat-table-toolbar" [fxShow]="textSearchMode"> | |
21 | - <div class="mat-toolbar-tools"> | |
22 | - <button mat-button mat-icon-button | |
23 | - matTooltip="{{ 'action.search' | translate }}" | |
24 | - matTooltipPosition="above"> | |
25 | - <mat-icon>search</mat-icon> | |
26 | - </button> | |
27 | - <mat-form-field fxFlex> | |
28 | - <mat-label> </mat-label> | |
29 | - <input #searchInput matInput | |
30 | - [(ngModel)]="textSearch" | |
31 | - placeholder="{{ 'entity.search' | translate }}"/> | |
32 | - </mat-form-field> | |
33 | - <button mat-button mat-icon-button (click)="exitFilterMode()" | |
34 | - matTooltip="{{ 'action.close' | translate }}" | |
35 | - matTooltipPosition="above"> | |
36 | - <mat-icon>close</mat-icon> | |
37 | - </button> | |
38 | - </div> | |
39 | - </mat-toolbar> | |
40 | 20 | <div *ngIf="customerTitle" fxLayout="row" class="customer-info"> |
41 | 21 | <span>{{ customerTitle }}</span> |
42 | 22 | </div> |
43 | 23 | <div fxFlex class="tb-entities-nav-tree-panel"> |
44 | 24 | <tb-nav-tree |
45 | 25 | [loadNodes]="loadNodes" |
46 | - [onNodeSelected]="onNodeSelected" | |
47 | - [onNodesInserted]="onNodesInserted" | |
48 | - [editCallbacks]="nodeEditCallbacks" | |
49 | 26 | enableSearch="true" |
50 | - [searchCallback]="searchCallback" | |
51 | 27 | ></tb-nav-tree> |
52 | 28 | </div> |
53 | 29 | </div> | ... | ... |
... | ... | @@ -14,63 +14,46 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | -import { ChangeDetectorRef, AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; | |
17 | +import { | |
18 | + AfterViewInit, | |
19 | + ChangeDetectorRef, | |
20 | + Component, | |
21 | + ElementRef, | |
22 | + Input, | |
23 | + OnInit, | |
24 | + ViewChild, | |
25 | + ViewContainerRef | |
26 | +} from '@angular/core'; | |
18 | 27 | import { PageComponent } from '@shared/components/page.component'; |
19 | 28 | import { Store } from '@ngrx/store'; |
20 | 29 | import { AppState } from '@core/core.state'; |
21 | 30 | import { WidgetAction, WidgetContext } from '@home/models/widget-component.models'; |
22 | -import { DatasourceData, DatasourceType, WidgetConfig, widgetType } from '@shared/models/widget.models'; | |
23 | -import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models'; | |
31 | +import { WidgetConfig } from '@shared/models/widget.models'; | |
32 | +import { IWidgetSubscription } from '@core/api/widget-api.models'; | |
24 | 33 | import { UtilsService } from '@core/services/utils.service'; |
25 | 34 | import cssjs from '@core/css/css'; |
26 | -import { fromEvent, Observable } from 'rxjs'; | |
27 | -import { debounceTime, distinctUntilChanged, map, mergeMap, tap } from 'rxjs/operators'; | |
35 | +import { fromEvent } from 'rxjs'; | |
36 | +import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'; | |
28 | 37 | import { constructTableCssString } from '@home/components/widget/lib/table-widget.models'; |
29 | 38 | import { Overlay } from '@angular/cdk/overlay'; |
30 | 39 | import { |
31 | 40 | LoadNodesCallback, |
32 | - NavTreeEditCallbacks, | |
33 | - NodesCallback, | |
34 | - NodeSearchCallback, | |
35 | - NodeSelectedCallback, | |
36 | - NodesInsertedCallback | |
41 | + NavTreeEditCallbacks | |
37 | 42 | } from '@shared/components/nav-tree.component'; |
38 | 43 | import { EntityType } from '@shared/models/entity-type.models'; |
39 | -import { deepClone, hashCode } from '@core/utils'; | |
44 | +import { hashCode } from '@core/utils'; | |
40 | 45 | import { |
41 | - defaultNodeIconFunction, | |
42 | - defaultNodeOpenedFunction, | |
43 | - defaultNodeRelationQueryFunction, | |
44 | - defaultNodesSortFunction, | |
45 | - EdgeGroupsNodeData, | |
46 | + EdgeGroupNodeData, | |
46 | 47 | edgeGroupsNodeText, |
47 | - edgeGroupsTypes, | |
48 | - EdgeNodeData, | |
48 | + edgeGroupsTypes, EntityNodeData, | |
49 | 49 | edgeNodeText, |
50 | - EdgeOverviewNode, EdgesOverviewWidgetSettings, | |
51 | - HierarchyNavTreeNode, | |
52 | - HierarchyNodeContext, | |
53 | - HierarchyNodeDatasource, | |
54 | - iconUrlHtml, | |
55 | - loadNodeCtxFunction, | |
56 | - materialIconHtml, | |
57 | - NodeDisabledFunction, | |
58 | - NodeHasChildrenFunction, | |
59 | - NodeIconFunction, | |
60 | - NodeOpenedFunction, | |
61 | - NodeRelationQueryFunction, | |
62 | - NodesSortFunction, | |
63 | - NodeTextFunction | |
50 | + EdgeOverviewNode, EntityNodeDatasource | |
64 | 51 | } from '@home/components/widget/lib/edges-overview-widget.models'; |
65 | -import { EntityRelationsQuery } from '@shared/models/relation.models'; | |
66 | -import { AliasFilterType, RelationsQueryFilter } from '@shared/models/alias.models'; | |
67 | -import { EntityFilter } from '@shared/models/query/query.models'; | |
68 | 52 | import { EdgeService } from "@core/http/edge.service"; |
69 | 53 | import { EntityService } from "@core/http/entity.service"; |
70 | 54 | import { TranslateService } from "@ngx-translate/core"; |
71 | -import { Direction, SortOrder } from "@shared/models/page/sort-order"; | |
72 | 55 | import { PageLink } from "@shared/models/page/page-link"; |
73 | -import { Edge, EdgeInfo } from "@shared/models/edge.models"; | |
56 | +import { Edge } from "@shared/models/edge.models"; | |
74 | 57 | import { BaseData } from "@shared/models/base-data"; |
75 | 58 | import { EntityId } from "@shared/models/id/entity-id"; |
76 | 59 | |
... | ... | @@ -79,7 +62,7 @@ import { EntityId } from "@shared/models/id/entity-id"; |
79 | 62 | templateUrl: './edges-overview-widget.component.html', |
80 | 63 | styleUrls: ['./edges-overview-widget.component.scss'] |
81 | 64 | }) |
82 | -export class EdgesOverviewWidgetComponent extends PageComponent implements OnInit, AfterViewInit { | |
65 | +export class EdgesOverviewWidgetComponent extends PageComponent implements OnInit { | |
83 | 66 | |
84 | 67 | @Input() |
85 | 68 | ctx: WidgetContext; |
... | ... | @@ -87,44 +70,17 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni |
87 | 70 | @ViewChild('searchInput') searchInputField: ElementRef; |
88 | 71 | |
89 | 72 | public toastTargetId = 'edges-overview-' + this.utils.guid(); |
90 | - | |
91 | - public textSearchMode = false; | |
92 | - public textSearch = null; | |
93 | 73 | public customerTitle: string = null; |
94 | 74 | |
95 | - public nodeEditCallbacks: NavTreeEditCallbacks = {}; | |
96 | - | |
97 | - private settings: EdgesOverviewWidgetSettings; | |
98 | 75 | private widgetConfig: WidgetConfig; |
99 | 76 | private subscription: IWidgetSubscription; |
100 | - private datasources: Array<HierarchyNodeDatasource>; | |
101 | - private data: Array<Array<DatasourceData>>; | |
77 | + private datasources: Array<EntityNodeDatasource>; | |
102 | 78 | |
103 | - private nodesMap: {[nodeId: string]: HierarchyNavTreeNode} = {}; | |
104 | - private pendingUpdateNodeTasks: {[nodeId: string]: () => void} = {}; | |
105 | 79 | private nodeIdCounter = 0; |
106 | 80 | |
107 | - private nodeRelationQueryFunction: NodeRelationQueryFunction; | |
108 | - private nodeIconFunction: NodeIconFunction; | |
109 | - private nodeTextFunction: NodeTextFunction; | |
110 | - private nodeDisabledFunction: NodeDisabledFunction; | |
111 | - private nodeOpenedFunction: NodeOpenedFunction; | |
112 | - private nodeHasChildrenFunction: NodeHasChildrenFunction; | |
113 | - private nodesSortFunction: NodesSortFunction; | |
114 | - | |
115 | 81 | private edgeNodesMap: {[parentNodeId: string]: {[edgeId: string]: string}} = {}; |
116 | 82 | private edgeGroupsNodesMap: {[edgeNodeId: string]: {[groupType: string]: string}} = {}; |
117 | 83 | |
118 | - | |
119 | - private searchAction: WidgetAction = { | |
120 | - name: 'action.search', | |
121 | - show: false, | |
122 | - icon: 'search', | |
123 | - onAction: () => { | |
124 | - this.enterFilterMode(); | |
125 | - } | |
126 | - }; | |
127 | - | |
128 | 84 | constructor(protected store: Store<AppState>, |
129 | 85 | private elementRef: ElementRef, |
130 | 86 | private edgeService: EdgeService, |
... | ... | @@ -138,72 +94,19 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni |
138 | 94 | } |
139 | 95 | |
140 | 96 | ngOnInit(): void { |
141 | - this.ctx.$scope.edgesOverviewWidget = this; | |
142 | - this.settings = this.ctx.settings; | |
143 | 97 | this.widgetConfig = this.ctx.widgetConfig; |
144 | 98 | this.subscription = this.ctx.defaultSubscription; |
145 | - this.datasources = this.subscription.datasources as Array<HierarchyNodeDatasource>; | |
146 | - this.data = this.subscription.dataPages[0].data; | |
147 | - this.ctx.widgetTitle = this.datasources[0].entity.name; | |
148 | - this.getCustomerTitle(this.datasources[0].entity.id.id); | |
99 | + this.datasources = this.subscription.datasources as Array<EntityNodeDatasource>; | |
100 | + if (this.datasources.length > 0 && this.datasources[0].entity.id.entityType === EntityType.EDGE) { | |
101 | + let selectedEdge = this.datasources[0].entity; | |
102 | + this.getCustomerTitle(selectedEdge.id.id); | |
103 | + this.ctx.widgetTitle = selectedEdge.name; | |
104 | + } | |
149 | 105 | this.initializeConfig(); |
150 | 106 | this.ctx.updateWidgetParams(); |
151 | 107 | } |
152 | 108 | |
153 | - ngAfterViewInit(): void { | |
154 | - fromEvent(this.searchInputField.nativeElement, 'keyup') | |
155 | - .pipe( | |
156 | - debounceTime(150), | |
157 | - distinctUntilChanged(), | |
158 | - tap(() => { | |
159 | - this.updateSearchNodes(); | |
160 | - }) | |
161 | - ) | |
162 | - .subscribe(); | |
163 | - } | |
164 | - | |
165 | - public onDataUpdated() { | |
166 | - this.updateNodeData(this.subscription.data); | |
167 | - } | |
168 | - | |
169 | 109 | private initializeConfig() { |
170 | - this.ctx.widgetActions = [this.searchAction]; | |
171 | - | |
172 | - const testNodeCtx: HierarchyNodeContext = { | |
173 | - entity: { | |
174 | - id: { | |
175 | - entityType: EntityType.DEVICE, | |
176 | - id: '123' | |
177 | - }, | |
178 | - name: 'TEST DEV1' | |
179 | - }, | |
180 | - data: {}, | |
181 | - level: 2 | |
182 | - }; | |
183 | - const parentNodeCtx = deepClone(testNodeCtx); | |
184 | - parentNodeCtx.level = 1; | |
185 | - testNodeCtx.parentNodeCtx = parentNodeCtx; | |
186 | - | |
187 | - this.nodeRelationQueryFunction = loadNodeCtxFunction(this.settings.nodeRelationQueryFunction, 'nodeCtx', testNodeCtx); | |
188 | - this.nodeIconFunction = loadNodeCtxFunction(this.settings.nodeIconFunction, 'nodeCtx', testNodeCtx); | |
189 | - this.nodeTextFunction = loadNodeCtxFunction(this.settings.nodeTextFunction, 'nodeCtx', testNodeCtx); | |
190 | - this.nodeDisabledFunction = loadNodeCtxFunction(this.settings.nodeDisabledFunction, 'nodeCtx', testNodeCtx); | |
191 | - this.nodeOpenedFunction = loadNodeCtxFunction(this.settings.nodeOpenedFunction, 'nodeCtx', testNodeCtx); | |
192 | - this.nodeHasChildrenFunction = loadNodeCtxFunction(this.settings.nodeHasChildrenFunction, 'nodeCtx', testNodeCtx); | |
193 | - | |
194 | - const testNodeCtx2 = deepClone(testNodeCtx); | |
195 | - testNodeCtx2.entity.name = 'TEST DEV2'; | |
196 | - | |
197 | - this.nodesSortFunction = loadNodeCtxFunction(this.settings.nodesSortFunction, 'nodeCtx1,nodeCtx2', testNodeCtx, testNodeCtx2); | |
198 | - | |
199 | - this.nodeRelationQueryFunction = this.nodeRelationQueryFunction || defaultNodeRelationQueryFunction; | |
200 | - this.nodeIconFunction = this.nodeIconFunction || defaultNodeIconFunction; | |
201 | - this.nodeTextFunction = this.nodeTextFunction || ((nodeCtx) => nodeCtx.entity.name); | |
202 | - this.nodeDisabledFunction = this.nodeDisabledFunction || (() => false); | |
203 | - this.nodeOpenedFunction = this.nodeOpenedFunction || defaultNodeOpenedFunction; | |
204 | - this.nodeHasChildrenFunction = this.nodeHasChildrenFunction || (() => true); | |
205 | - this.nodesSortFunction = this.nodesSortFunction || defaultNodesSortFunction; | |
206 | - | |
207 | 110 | const cssString = constructTableCssString(this.widgetConfig); |
208 | 111 | const cssParser = new cssjs(); |
209 | 112 | cssParser.testMode = false; |
... | ... | @@ -213,72 +116,14 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni |
213 | 116 | $(this.elementRef.nativeElement).addClass(namespace); |
214 | 117 | } |
215 | 118 | |
216 | - private enterFilterMode() { | |
217 | - this.textSearchMode = true; | |
218 | - this.textSearch = ''; | |
219 | - this.ctx.hideTitlePanel = true; | |
220 | - this.ctx.detectChanges(true); | |
221 | - setTimeout(() => { | |
222 | - this.searchInputField.nativeElement.focus(); | |
223 | - this.searchInputField.nativeElement.setSelectionRange(0, 0); | |
224 | - }, 10); | |
225 | - } | |
226 | - | |
227 | - exitFilterMode() { | |
228 | - this.textSearchMode = false; | |
229 | - this.textSearch = null; | |
230 | - this.updateSearchNodes(); | |
231 | - this.ctx.hideTitlePanel = false; | |
232 | - this.ctx.detectChanges(true); | |
233 | - } | |
234 | - | |
235 | - private updateSearchNodes() { | |
236 | - if (this.textSearch != null) { | |
237 | - this.nodeEditCallbacks.search(this.textSearch); | |
238 | - } else { | |
239 | - this.nodeEditCallbacks.clearSearch(); | |
240 | - } | |
241 | - } | |
242 | - | |
243 | - private updateNodeData(subscriptionData: Array<DatasourceData>) { | |
244 | - const affectedNodes: string[] = []; | |
245 | - if (subscriptionData) { | |
246 | - subscriptionData.forEach((datasourceData) => { | |
247 | - const datasource = datasourceData.datasource as HierarchyNodeDatasource; | |
248 | - if (datasource.nodeId) { | |
249 | - const node = this.nodesMap[datasource.nodeId]; | |
250 | - const key = datasourceData.dataKey.label; | |
251 | - let value; | |
252 | - if (datasourceData.data && datasourceData.data.length) { | |
253 | - value = datasourceData.data[0][1]; | |
254 | - } | |
255 | - if (node.data.nodeCtx.data[key] !== value) { | |
256 | - if (affectedNodes.indexOf(datasource.nodeId) === -1) { | |
257 | - affectedNodes.push(datasource.nodeId); | |
258 | - } | |
259 | - node.data.nodeCtx.data[key] = value; | |
260 | - } | |
261 | - } | |
262 | - }); | |
263 | - } | |
264 | - affectedNodes.forEach((nodeId) => { | |
265 | - const node: HierarchyNavTreeNode = this.nodeEditCallbacks.getNode(nodeId); | |
266 | - if (node) { | |
267 | - this.updateNodeStyle(this.nodesMap[nodeId]); | |
268 | - } else { | |
269 | - this.pendingUpdateNodeTasks[nodeId] = () => { | |
270 | - this.updateNodeStyle(this.nodesMap[nodeId]); | |
271 | - }; | |
272 | - } | |
273 | - }); | |
274 | - } | |
275 | - | |
276 | 119 | public loadNodes: LoadNodesCallback = (node, cb) => { |
277 | - if (node.id === '#') { | |
278 | - const edge: BaseData<EntityId> = this.datasources[0].entity; | |
279 | - cb(this.loadNodesForEdge(edge.id.id, edge)); | |
280 | - } | |
281 | - else if (node.data.type === 'edgeGroups') { | |
120 | + var selectedEdge: BaseData<EntityId> = null; | |
121 | + if (this.datasources.length > 0 && this.datasources[0].entity && this.datasources[0].entity.id.entityType === EntityType.EDGE) { | |
122 | + selectedEdge = this.datasources[0].entity; | |
123 | + } | |
124 | + if (node.id === '#' && selectedEdge) { | |
125 | + cb(this.loadNodesForEdge(selectedEdge.id.id, selectedEdge)); | |
126 | + } else if (node.data && node.data.type === 'edgeGroup') { | |
282 | 127 | const pageLink = new PageLink(100); |
283 | 128 | this.entityService.getAssignedToEdgeEntitiesByType(node, pageLink).subscribe( |
284 | 129 | (entities) => { |
... | ... | @@ -289,6 +134,8 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni |
289 | 134 | } |
290 | 135 | } |
291 | 136 | ) |
137 | + } else { | |
138 | + cb([]); | |
292 | 139 | } |
293 | 140 | } |
294 | 141 | |
... | ... | @@ -303,11 +150,11 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni |
303 | 150 | text: edgeGroupsNodeText(this.translateService, entityType), |
304 | 151 | children: true, |
305 | 152 | data: { |
306 | - type: 'edgeGroups', | |
153 | + type: 'edgeGroup', | |
307 | 154 | entityType, |
308 | 155 | edge, |
309 | 156 | internalId: edge.id.id + '_' + entityType |
310 | - } as EdgeGroupsNodeData | |
157 | + } as EdgeGroupNodeData | |
311 | 158 | }; |
312 | 159 | nodes.push(node); |
313 | 160 | nodesMap[entityType] = node.id; |
... | ... | @@ -330,10 +177,10 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni |
330 | 177 | disabled: false |
331 | 178 | }, |
332 | 179 | data: { |
333 | - type: 'edge', | |
180 | + type: 'entity', | |
334 | 181 | entity: edge, |
335 | 182 | internalId: edge.id.id |
336 | - } as EdgeNodeData | |
183 | + } as EntityNodeData | |
337 | 184 | }; |
338 | 185 | nodesMap[edge.id.id] = node.id; |
339 | 186 | return node; |
... | ... | @@ -351,94 +198,6 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni |
351 | 198 | return nodes; |
352 | 199 | } |
353 | 200 | |
354 | - public onNodeSelected: NodeSelectedCallback = (node, event) => { | |
355 | - let nodeId; | |
356 | - if (!node) { | |
357 | - nodeId = -1; | |
358 | - } else { | |
359 | - nodeId = node.id; | |
360 | - } | |
361 | - if (nodeId !== -1) { | |
362 | - const selectedNode = this.nodesMap[nodeId]; | |
363 | - if (selectedNode) { | |
364 | - const descriptors = this.ctx.actionsApi.getActionDescriptors('nodeSelected'); | |
365 | - if (descriptors.length) { | |
366 | - const entity = selectedNode.data.nodeCtx.entity; | |
367 | - this.ctx.actionsApi.handleWidgetAction(event, descriptors[0], entity.id, entity.name, { nodeCtx: selectedNode.data.nodeCtx }); | |
368 | - } | |
369 | - } | |
370 | - } | |
371 | - } | |
372 | - | |
373 | - public onNodesInserted: NodesInsertedCallback = (nodes) => { | |
374 | - if (nodes) { | |
375 | - nodes.forEach((nodeId) => { | |
376 | - const task = this.pendingUpdateNodeTasks[nodeId]; | |
377 | - if (task) { | |
378 | - task(); | |
379 | - delete this.pendingUpdateNodeTasks[nodeId]; | |
380 | - } | |
381 | - }); | |
382 | - } | |
383 | - } | |
384 | - | |
385 | - public searchCallback: NodeSearchCallback = (searchText, node) => { | |
386 | - const theNode = this.nodesMap[node.id]; | |
387 | - if (theNode && theNode.data.searchText) { | |
388 | - return theNode.data.searchText.includes(searchText.toLowerCase()); | |
389 | - } | |
390 | - return false; | |
391 | - } | |
392 | - | |
393 | - private updateNodeStyle(node: HierarchyNavTreeNode) { | |
394 | - const newText = this.prepareNodeText(node); | |
395 | - if (node.text !== newText) { | |
396 | - node.text = newText; | |
397 | - this.nodeEditCallbacks.updateNode(node.id, node.text); | |
398 | - } | |
399 | - const newDisabled = this.nodeDisabledFunction(node.data.nodeCtx); | |
400 | - if (node.state.disabled !== newDisabled) { | |
401 | - node.state.disabled = newDisabled; | |
402 | - if (node.state.disabled) { | |
403 | - this.nodeEditCallbacks.disableNode(node.id); | |
404 | - } else { | |
405 | - this.nodeEditCallbacks.enableNode(node.id); | |
406 | - } | |
407 | - } | |
408 | - const newHasChildren = this.nodeHasChildrenFunction(node.data.nodeCtx); | |
409 | - if (node.children !== newHasChildren) { | |
410 | - node.children = newHasChildren; | |
411 | - this.nodeEditCallbacks.setNodeHasChildren(node.id, node.children); | |
412 | - } | |
413 | - } | |
414 | - | |
415 | - private prepareNodeText(node: HierarchyNavTreeNode): string { | |
416 | - const nodeIcon = this.prepareNodeIcon(node.data.nodeCtx); | |
417 | - const nodeText = this.nodeTextFunction(node.data.nodeCtx); | |
418 | - node.data.searchText = nodeText ? nodeText.replace(/<[^>]+>/g, '').toLowerCase() : ''; | |
419 | - return nodeIcon + nodeText; | |
420 | - } | |
421 | - | |
422 | - private prepareNodeIcon(nodeCtx: HierarchyNodeContext): string { | |
423 | - let iconInfo = this.nodeIconFunction(nodeCtx); | |
424 | - if (iconInfo) { | |
425 | - if (iconInfo === 'default') { | |
426 | - iconInfo = defaultNodeIconFunction(nodeCtx); | |
427 | - } | |
428 | - if (iconInfo && iconInfo !== 'default' && (iconInfo.iconUrl || iconInfo.materialIcon)) { | |
429 | - if (iconInfo.materialIcon) { | |
430 | - return materialIconHtml(iconInfo.materialIcon); | |
431 | - } else { | |
432 | - return iconUrlHtml(iconInfo.iconUrl); | |
433 | - } | |
434 | - } else { | |
435 | - return ''; | |
436 | - } | |
437 | - } else { | |
438 | - return ''; | |
439 | - } | |
440 | - } | |
441 | - | |
442 | 201 | private getCustomerTitle(edgeId) { |
443 | 202 | this.edgeService.getEdgeInfo(edgeId).subscribe( |
444 | 203 | (edge) => { | ... | ... |
... | ... | @@ -14,83 +14,16 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | -import { BaseData } from '@shared/models/base-data'; | |
18 | -import { EntityId } from '@shared/models/id/entity-id'; | |
19 | -import { NavTreeNode, NodesCallback } from '@shared/components/nav-tree.component'; | |
17 | +import { NavTreeNode } from '@shared/components/nav-tree.component'; | |
20 | 18 | import { Datasource } from '@shared/models/widget.models'; |
21 | -import { isDefined, isUndefined } from '@core/utils'; | |
22 | -import { EntityRelationsQuery, EntitySearchDirection, RelationTypeGroup } from '@shared/models/relation.models'; | |
23 | 19 | import { EntityType } from '@shared/models/entity-type.models'; |
24 | 20 | import { Edge } from "@shared/models/edge.models"; |
25 | 21 | import { TranslateService } from "@ngx-translate/core"; |
26 | 22 | |
27 | -export interface EdgesOverviewWidgetSettings { | |
28 | - nodeRelationQueryFunction: string; | |
29 | - nodeHasChildrenFunction: string; | |
30 | - nodeOpenedFunction: string; | |
31 | - nodeDisabledFunction: string; | |
32 | - nodeIconFunction: string; | |
33 | - nodeTextFunction: string; | |
34 | - nodesSortFunction: string; | |
35 | -} | |
36 | - | |
37 | -export interface HierarchyNodeContext { | |
38 | - parentNodeCtx?: HierarchyNodeContext; | |
39 | - entity: BaseData<EntityId>; | |
40 | - childrenNodesLoaded?: boolean; | |
41 | - level?: number; | |
42 | - data: {[key: string]: any}; | |
43 | -} | |
44 | - | |
45 | -export interface HierarchyNavTreeNode extends NavTreeNode { | |
46 | - data?: { | |
47 | - datasource: HierarchyNodeDatasource; | |
48 | - nodeCtx: HierarchyNodeContext; | |
49 | - searchText?: string; | |
50 | - }; | |
51 | -} | |
52 | - | |
53 | -export interface HierarchyNodeDatasource extends Datasource { | |
23 | +export interface EntityNodeDatasource extends Datasource { | |
54 | 24 | nodeId: string; |
55 | 25 | } |
56 | 26 | |
57 | -export interface HierarchyNodeIconInfo { | |
58 | - iconUrl?: string; | |
59 | - materialIcon?: string; | |
60 | -} | |
61 | - | |
62 | -export type NodeRelationQueryFunction = (nodeCtx: HierarchyNodeContext) => EntityRelationsQuery | 'default'; | |
63 | -export type NodeTextFunction = (nodeCtx: HierarchyNodeContext) => string; | |
64 | -export type NodeDisabledFunction = (nodeCtx: HierarchyNodeContext) => boolean; | |
65 | -export type NodeIconFunction = (nodeCtx: HierarchyNodeContext) => HierarchyNodeIconInfo | 'default'; | |
66 | -export type NodeOpenedFunction = (nodeCtx: HierarchyNodeContext) => boolean; | |
67 | -export type NodeHasChildrenFunction = (nodeCtx: HierarchyNodeContext) => boolean; | |
68 | -export type NodesSortFunction = (nodeCtx1: HierarchyNodeContext, nodeCtx2: HierarchyNodeContext) => number; | |
69 | - | |
70 | -export function loadNodeCtxFunction<F extends (...args: any[]) => any>(functionBody: string, argNames: string, ...args: any[]): F { | |
71 | - let nodeCtxFunction: F = null; | |
72 | - if (isDefined(functionBody) && functionBody.length) { | |
73 | - try { | |
74 | - nodeCtxFunction = new Function(argNames, functionBody) as F; | |
75 | - const res = nodeCtxFunction.apply(null, args); | |
76 | - if (isUndefined(res)) { | |
77 | - nodeCtxFunction = null; | |
78 | - } | |
79 | - } catch (e) { | |
80 | - nodeCtxFunction = null; | |
81 | - } | |
82 | - } | |
83 | - return nodeCtxFunction; | |
84 | -} | |
85 | - | |
86 | -export function materialIconHtml(materialIcon: string): string { | |
87 | - return '<mat-icon class="node-icon material-icons" role="img" aria-hidden="false">' + materialIcon + '</mat-icon>'; | |
88 | -} | |
89 | - | |
90 | -export function iconUrlHtml(iconUrl: string): string { | |
91 | - return '<div class="node-icon" style="background-image: url(' + iconUrl + ');"> </div>'; | |
92 | -} | |
93 | - | |
94 | 27 | export function edgeGroupsNodeText(translate: TranslateService, entityType: EntityType): string { |
95 | 28 | const nodeIcon = materialIconByEntityType(entityType); |
96 | 29 | const nodeText = textForEdgeGroupsType(translate, entityType); |
... | ... | @@ -112,12 +45,6 @@ export function materialIconByEntityType(entityType: EntityType): string { |
112 | 45 | case EntityType.ASSET: |
113 | 46 | materialIcon = 'domain'; |
114 | 47 | break; |
115 | - case EntityType.CUSTOMER: | |
116 | - materialIcon = 'supervisor_account'; | |
117 | - break; | |
118 | - case EntityType.USER: | |
119 | - materialIcon = 'account_circle'; | |
120 | - break; | |
121 | 48 | case EntityType.DASHBOARD: |
122 | 49 | materialIcon = 'dashboards'; |
123 | 50 | break; |
... | ... | @@ -127,9 +54,6 @@ export function materialIconByEntityType(entityType: EntityType): string { |
127 | 54 | case EntityType.RULE_CHAIN: |
128 | 55 | materialIcon = 'settings_ethernet'; |
129 | 56 | break; |
130 | - case EntityType.EDGE: | |
131 | - materialIcon = 'router'; | |
132 | - break; | |
133 | 57 | } |
134 | 58 | return '<mat-icon class="node-icon material-icons" role="img" aria-hidden="false">' + materialIcon + '</mat-icon>'; |
135 | 59 | } |
... | ... | @@ -156,26 +80,6 @@ export function textForEdgeGroupsType(translate: TranslateService, entityType: E |
156 | 80 | return translate.instant(textForEdgeGroupsType); |
157 | 81 | } |
158 | 82 | |
159 | -export const defaultNodeRelationQueryFunction: NodeRelationQueryFunction = nodeCtx => { | |
160 | - const entity = nodeCtx.entity; | |
161 | - const query: EntityRelationsQuery = { | |
162 | - parameters: { | |
163 | - rootId: entity.id.id, | |
164 | - rootType: entity.id.entityType as EntityType, | |
165 | - direction: EntitySearchDirection.FROM, | |
166 | - relationTypeGroup: RelationTypeGroup.COMMON, | |
167 | - maxLevel: 1 | |
168 | - }, | |
169 | - filters: [ | |
170 | - { | |
171 | - relationType: 'Contains', | |
172 | - entityTypes: [] | |
173 | - } | |
174 | - ] | |
175 | - }; | |
176 | - return query; | |
177 | -}; | |
178 | - | |
179 | 83 | export const edgeGroupsTypes: EntityType[] = [ |
180 | 84 | EntityType.ASSET, |
181 | 85 | EntityType.DEVICE, |
... | ... | @@ -184,71 +88,20 @@ export const edgeGroupsTypes: EntityType[] = [ |
184 | 88 | EntityType.RULE_CHAIN |
185 | 89 | ] |
186 | 90 | |
187 | -export const defaultNodeIconFunction: NodeIconFunction = nodeCtx => { | |
188 | - let materialIcon = 'insert_drive_file'; | |
189 | - const entity = nodeCtx.entity; | |
190 | - if (entity && entity.id && entity.id.entityType) { | |
191 | - switch (entity.id.entityType as EntityType | string) { | |
192 | - case 'function': | |
193 | - materialIcon = 'functions'; | |
194 | - break; | |
195 | - case EntityType.DEVICE: | |
196 | - materialIcon = 'devices_other'; | |
197 | - break; | |
198 | - case EntityType.ASSET: | |
199 | - materialIcon = 'domain'; | |
200 | - break; | |
201 | - case EntityType.TENANT: | |
202 | - materialIcon = 'supervisor_account'; | |
203 | - break; | |
204 | - case EntityType.CUSTOMER: | |
205 | - materialIcon = 'supervisor_account'; | |
206 | - break; | |
207 | - case EntityType.USER: | |
208 | - materialIcon = 'account_circle'; | |
209 | - break; | |
210 | - case EntityType.DASHBOARD: | |
211 | - materialIcon = 'dashboards'; | |
212 | - break; | |
213 | - case EntityType.ALARM: | |
214 | - materialIcon = 'notifications_active'; | |
215 | - break; | |
216 | - case EntityType.ENTITY_VIEW: | |
217 | - materialIcon = 'view_quilt'; | |
218 | - break; | |
219 | - } | |
220 | - } | |
221 | - return { | |
222 | - materialIcon | |
223 | - }; | |
224 | -}; | |
225 | - | |
226 | -export const defaultNodeOpenedFunction: NodeOpenedFunction = nodeCtx => { | |
227 | - return nodeCtx.level <= 4; | |
228 | -}; | |
229 | - | |
230 | -export const defaultNodesSortFunction: NodesSortFunction = (nodeCtx1, nodeCtx2) => { | |
231 | - let result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType); | |
232 | - if (result === 0) { | |
233 | - result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name); | |
234 | - } | |
235 | - return result; | |
236 | -}; | |
237 | - | |
238 | 91 | export interface EdgeOverviewNode extends NavTreeNode { |
239 | 92 | data?: EdgeOverviewNodeData; |
240 | 93 | } |
241 | 94 | |
242 | -export type EdgeOverviewNodeData = EdgeGroupsNodeData | EdgeNodeData; | |
95 | +export type EdgeOverviewNodeData = EdgeGroupNodeData | EntityNodeData; | |
243 | 96 | |
244 | -export interface EdgeGroupsNodeData extends BaseEdgeOverviewNodeData { | |
245 | - type: 'edgeGroups'; | |
97 | +export interface EdgeGroupNodeData extends BaseEdgeOverviewNodeData { | |
98 | + type: 'edgeGroup'; | |
246 | 99 | entityType: EntityType; |
247 | 100 | edge: Edge; |
248 | 101 | } |
249 | 102 | |
250 | -export interface EdgeNodeData extends BaseEdgeOverviewNodeData { | |
251 | - type: 'edge'; | |
103 | +export interface EntityNodeData extends BaseEdgeOverviewNodeData { | |
104 | + type: 'entity'; | |
252 | 105 | entity: Edge; |
253 | 106 | } |
254 | 107 | |
... | ... | @@ -257,4 +110,4 @@ export interface BaseEdgeOverviewNodeData { |
257 | 110 | internalId: string; |
258 | 111 | } |
259 | 112 | |
260 | -export type EdgeOverviewNodeType = 'edge' | 'edgeGroups'; | |
113 | +export type EdgeOverviewNodeType = 'entity' | 'edgeGroup'; | ... | ... |