Browse Source

Edges Quick Overview Widget

pull/3811/head
Artem Babak 6 years ago
parent
commit
e4e5cb35dc
  1. 10
      application/src/main/data/json/system/widget_bundles/edge_widgets.json
  2. 24
      ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.html
  3. 323
      ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.ts
  4. 163
      ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.models.ts

10
application/src/main/data/json/system/widget_bundles/edge_widgets.json

File diff suppressed because one or more lines are too long

24
ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.html

@ -17,37 +17,13 @@
-->
<div class="tb-edges-overview tb-absolute-fill" tb-toast toastTarget="{{ toastTargetId }}">
<div fxFlex fxLayout="column" class="tb-absolute-fill">
<mat-toolbar class="mat-table-toolbar" [fxShow]="textSearchMode">
<div class="mat-toolbar-tools">
<button mat-button mat-icon-button
matTooltip="{{ 'action.search' | translate }}"
matTooltipPosition="above">
<mat-icon>search</mat-icon>
</button>
<mat-form-field fxFlex>
<mat-label>&nbsp;</mat-label>
<input #searchInput matInput
[(ngModel)]="textSearch"
placeholder="{{ 'entity.search' | translate }}"/>
</mat-form-field>
<button mat-button mat-icon-button (click)="exitFilterMode()"
matTooltip="{{ 'action.close' | translate }}"
matTooltipPosition="above">
<mat-icon>close</mat-icon>
</button>
</div>
</mat-toolbar>
<div *ngIf="customerTitle" fxLayout="row" class="customer-info">
<span>{{ customerTitle }}</span>
</div>
<div fxFlex class="tb-entities-nav-tree-panel">
<tb-nav-tree
[loadNodes]="loadNodes"
[onNodeSelected]="onNodeSelected"
[onNodesInserted]="onNodesInserted"
[editCallbacks]="nodeEditCallbacks"
enableSearch="true"
[searchCallback]="searchCallback"
></tb-nav-tree>
</div>
</div>

323
ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.component.ts

@ -14,63 +14,46 @@
/// limitations under the License.
///
import { ChangeDetectorRef, AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
import {
AfterViewInit,
ChangeDetectorRef,
Component,
ElementRef,
Input,
OnInit,
ViewChild,
ViewContainerRef
} from '@angular/core';
import { PageComponent } from '@shared/components/page.component';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { WidgetAction, WidgetContext } from '@home/models/widget-component.models';
import { DatasourceData, DatasourceType, WidgetConfig, widgetType } from '@shared/models/widget.models';
import { IWidgetSubscription, WidgetSubscriptionOptions } from '@core/api/widget-api.models';
import { WidgetConfig } from '@shared/models/widget.models';
import { IWidgetSubscription } from '@core/api/widget-api.models';
import { UtilsService } from '@core/services/utils.service';
import cssjs from '@core/css/css';
import { fromEvent, Observable } from 'rxjs';
import { debounceTime, distinctUntilChanged, map, mergeMap, tap } from 'rxjs/operators';
import { fromEvent } from 'rxjs';
import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators';
import { constructTableCssString } from '@home/components/widget/lib/table-widget.models';
import { Overlay } from '@angular/cdk/overlay';
import {
LoadNodesCallback,
NavTreeEditCallbacks,
NodesCallback,
NodeSearchCallback,
NodeSelectedCallback,
NodesInsertedCallback
NavTreeEditCallbacks
} from '@shared/components/nav-tree.component';
import { EntityType } from '@shared/models/entity-type.models';
import { deepClone, hashCode } from '@core/utils';
import { hashCode } from '@core/utils';
import {
defaultNodeIconFunction,
defaultNodeOpenedFunction,
defaultNodeRelationQueryFunction,
defaultNodesSortFunction,
EdgeGroupsNodeData,
EdgeGroupNodeData,
edgeGroupsNodeText,
edgeGroupsTypes,
EdgeNodeData,
edgeGroupsTypes, EntityNodeData,
edgeNodeText,
EdgeOverviewNode, EdgesOverviewWidgetSettings,
HierarchyNavTreeNode,
HierarchyNodeContext,
HierarchyNodeDatasource,
iconUrlHtml,
loadNodeCtxFunction,
materialIconHtml,
NodeDisabledFunction,
NodeHasChildrenFunction,
NodeIconFunction,
NodeOpenedFunction,
NodeRelationQueryFunction,
NodesSortFunction,
NodeTextFunction
EdgeOverviewNode, EntityNodeDatasource
} from '@home/components/widget/lib/edges-overview-widget.models';
import { EntityRelationsQuery } from '@shared/models/relation.models';
import { AliasFilterType, RelationsQueryFilter } from '@shared/models/alias.models';
import { EntityFilter } from '@shared/models/query/query.models';
import { EdgeService } from "@core/http/edge.service";
import { EntityService } from "@core/http/entity.service";
import { TranslateService } from "@ngx-translate/core";
import { Direction, SortOrder } from "@shared/models/page/sort-order";
import { PageLink } from "@shared/models/page/page-link";
import { Edge, EdgeInfo } from "@shared/models/edge.models";
import { Edge } from "@shared/models/edge.models";
import { BaseData } from "@shared/models/base-data";
import { EntityId } from "@shared/models/id/entity-id";
@ -79,7 +62,7 @@ import { EntityId } from "@shared/models/id/entity-id";
templateUrl: './edges-overview-widget.component.html',
styleUrls: ['./edges-overview-widget.component.scss']
})
export class EdgesOverviewWidgetComponent extends PageComponent implements OnInit, AfterViewInit {
export class EdgesOverviewWidgetComponent extends PageComponent implements OnInit {
@Input()
ctx: WidgetContext;
@ -87,44 +70,17 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
@ViewChild('searchInput') searchInputField: ElementRef;
public toastTargetId = 'edges-overview-' + this.utils.guid();
public textSearchMode = false;
public textSearch = null;
public customerTitle: string = null;
public nodeEditCallbacks: NavTreeEditCallbacks = {};
private settings: EdgesOverviewWidgetSettings;
private widgetConfig: WidgetConfig;
private subscription: IWidgetSubscription;
private datasources: Array<HierarchyNodeDatasource>;
private data: Array<Array<DatasourceData>>;
private datasources: Array<EntityNodeDatasource>;
private nodesMap: {[nodeId: string]: HierarchyNavTreeNode} = {};
private pendingUpdateNodeTasks: {[nodeId: string]: () => void} = {};
private nodeIdCounter = 0;
private nodeRelationQueryFunction: NodeRelationQueryFunction;
private nodeIconFunction: NodeIconFunction;
private nodeTextFunction: NodeTextFunction;
private nodeDisabledFunction: NodeDisabledFunction;
private nodeOpenedFunction: NodeOpenedFunction;
private nodeHasChildrenFunction: NodeHasChildrenFunction;
private nodesSortFunction: NodesSortFunction;
private edgeNodesMap: {[parentNodeId: string]: {[edgeId: string]: string}} = {};
private edgeGroupsNodesMap: {[edgeNodeId: string]: {[groupType: string]: string}} = {};
private searchAction: WidgetAction = {
name: 'action.search',
show: false,
icon: 'search',
onAction: () => {
this.enterFilterMode();
}
};
constructor(protected store: Store<AppState>,
private elementRef: ElementRef,
private edgeService: EdgeService,
@ -138,72 +94,19 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
}
ngOnInit(): void {
this.ctx.$scope.edgesOverviewWidget = this;
this.settings = this.ctx.settings;
this.widgetConfig = this.ctx.widgetConfig;
this.subscription = this.ctx.defaultSubscription;
this.datasources = this.subscription.datasources as Array<HierarchyNodeDatasource>;
this.data = this.subscription.dataPages[0].data;
this.ctx.widgetTitle = this.datasources[0].entity.name;
this.getCustomerTitle(this.datasources[0].entity.id.id);
this.datasources = this.subscription.datasources as Array<EntityNodeDatasource>;
if (this.datasources.length > 0 && this.datasources[0].entity.id.entityType === EntityType.EDGE) {
let selectedEdge = this.datasources[0].entity;
this.getCustomerTitle(selectedEdge.id.id);
this.ctx.widgetTitle = selectedEdge.name;
}
this.initializeConfig();
this.ctx.updateWidgetParams();
}
ngAfterViewInit(): void {
fromEvent(this.searchInputField.nativeElement, 'keyup')
.pipe(
debounceTime(150),
distinctUntilChanged(),
tap(() => {
this.updateSearchNodes();
})
)
.subscribe();
}
public onDataUpdated() {
this.updateNodeData(this.subscription.data);
}
private initializeConfig() {
this.ctx.widgetActions = [this.searchAction];
const testNodeCtx: HierarchyNodeContext = {
entity: {
id: {
entityType: EntityType.DEVICE,
id: '123'
},
name: 'TEST DEV1'
},
data: {},
level: 2
};
const parentNodeCtx = deepClone(testNodeCtx);
parentNodeCtx.level = 1;
testNodeCtx.parentNodeCtx = parentNodeCtx;
this.nodeRelationQueryFunction = loadNodeCtxFunction(this.settings.nodeRelationQueryFunction, 'nodeCtx', testNodeCtx);
this.nodeIconFunction = loadNodeCtxFunction(this.settings.nodeIconFunction, 'nodeCtx', testNodeCtx);
this.nodeTextFunction = loadNodeCtxFunction(this.settings.nodeTextFunction, 'nodeCtx', testNodeCtx);
this.nodeDisabledFunction = loadNodeCtxFunction(this.settings.nodeDisabledFunction, 'nodeCtx', testNodeCtx);
this.nodeOpenedFunction = loadNodeCtxFunction(this.settings.nodeOpenedFunction, 'nodeCtx', testNodeCtx);
this.nodeHasChildrenFunction = loadNodeCtxFunction(this.settings.nodeHasChildrenFunction, 'nodeCtx', testNodeCtx);
const testNodeCtx2 = deepClone(testNodeCtx);
testNodeCtx2.entity.name = 'TEST DEV2';
this.nodesSortFunction = loadNodeCtxFunction(this.settings.nodesSortFunction, 'nodeCtx1,nodeCtx2', testNodeCtx, testNodeCtx2);
this.nodeRelationQueryFunction = this.nodeRelationQueryFunction || defaultNodeRelationQueryFunction;
this.nodeIconFunction = this.nodeIconFunction || defaultNodeIconFunction;
this.nodeTextFunction = this.nodeTextFunction || ((nodeCtx) => nodeCtx.entity.name);
this.nodeDisabledFunction = this.nodeDisabledFunction || (() => false);
this.nodeOpenedFunction = this.nodeOpenedFunction || defaultNodeOpenedFunction;
this.nodeHasChildrenFunction = this.nodeHasChildrenFunction || (() => true);
this.nodesSortFunction = this.nodesSortFunction || defaultNodesSortFunction;
const cssString = constructTableCssString(this.widgetConfig);
const cssParser = new cssjs();
cssParser.testMode = false;
@ -213,72 +116,14 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
$(this.elementRef.nativeElement).addClass(namespace);
}
private enterFilterMode() {
this.textSearchMode = true;
this.textSearch = '';
this.ctx.hideTitlePanel = true;
this.ctx.detectChanges(true);
setTimeout(() => {
this.searchInputField.nativeElement.focus();
this.searchInputField.nativeElement.setSelectionRange(0, 0);
}, 10);
}
exitFilterMode() {
this.textSearchMode = false;
this.textSearch = null;
this.updateSearchNodes();
this.ctx.hideTitlePanel = false;
this.ctx.detectChanges(true);
}
private updateSearchNodes() {
if (this.textSearch != null) {
this.nodeEditCallbacks.search(this.textSearch);
} else {
this.nodeEditCallbacks.clearSearch();
}
}
private updateNodeData(subscriptionData: Array<DatasourceData>) {
const affectedNodes: string[] = [];
if (subscriptionData) {
subscriptionData.forEach((datasourceData) => {
const datasource = datasourceData.datasource as HierarchyNodeDatasource;
if (datasource.nodeId) {
const node = this.nodesMap[datasource.nodeId];
const key = datasourceData.dataKey.label;
let value;
if (datasourceData.data && datasourceData.data.length) {
value = datasourceData.data[0][1];
}
if (node.data.nodeCtx.data[key] !== value) {
if (affectedNodes.indexOf(datasource.nodeId) === -1) {
affectedNodes.push(datasource.nodeId);
}
node.data.nodeCtx.data[key] = value;
}
}
});
}
affectedNodes.forEach((nodeId) => {
const node: HierarchyNavTreeNode = this.nodeEditCallbacks.getNode(nodeId);
if (node) {
this.updateNodeStyle(this.nodesMap[nodeId]);
} else {
this.pendingUpdateNodeTasks[nodeId] = () => {
this.updateNodeStyle(this.nodesMap[nodeId]);
};
}
});
}
public loadNodes: LoadNodesCallback = (node, cb) => {
if (node.id === '#') {
const edge: BaseData<EntityId> = this.datasources[0].entity;
cb(this.loadNodesForEdge(edge.id.id, edge));
}
else if (node.data.type === 'edgeGroups') {
var selectedEdge: BaseData<EntityId> = null;
if (this.datasources.length > 0 && this.datasources[0].entity && this.datasources[0].entity.id.entityType === EntityType.EDGE) {
selectedEdge = this.datasources[0].entity;
}
if (node.id === '#' && selectedEdge) {
cb(this.loadNodesForEdge(selectedEdge.id.id, selectedEdge));
} else if (node.data && node.data.type === 'edgeGroup') {
const pageLink = new PageLink(100);
this.entityService.getAssignedToEdgeEntitiesByType(node, pageLink).subscribe(
(entities) => {
@ -289,6 +134,8 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
}
}
)
} else {
cb([]);
}
}
@ -303,11 +150,11 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
text: edgeGroupsNodeText(this.translateService, entityType),
children: true,
data: {
type: 'edgeGroups',
type: 'edgeGroup',
entityType,
edge,
internalId: edge.id.id + '_' + entityType
} as EdgeGroupsNodeData
} as EdgeGroupNodeData
};
nodes.push(node);
nodesMap[entityType] = node.id;
@ -330,10 +177,10 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
disabled: false
},
data: {
type: 'edge',
type: 'entity',
entity: edge,
internalId: edge.id.id
} as EdgeNodeData
} as EntityNodeData
};
nodesMap[edge.id.id] = node.id;
return node;
@ -351,94 +198,6 @@ export class EdgesOverviewWidgetComponent extends PageComponent implements OnIni
return nodes;
}
public onNodeSelected: NodeSelectedCallback = (node, event) => {
let nodeId;
if (!node) {
nodeId = -1;
} else {
nodeId = node.id;
}
if (nodeId !== -1) {
const selectedNode = this.nodesMap[nodeId];
if (selectedNode) {
const descriptors = this.ctx.actionsApi.getActionDescriptors('nodeSelected');
if (descriptors.length) {
const entity = selectedNode.data.nodeCtx.entity;
this.ctx.actionsApi.handleWidgetAction(event, descriptors[0], entity.id, entity.name, { nodeCtx: selectedNode.data.nodeCtx });
}
}
}
}
public onNodesInserted: NodesInsertedCallback = (nodes) => {
if (nodes) {
nodes.forEach((nodeId) => {
const task = this.pendingUpdateNodeTasks[nodeId];
if (task) {
task();
delete this.pendingUpdateNodeTasks[nodeId];
}
});
}
}
public searchCallback: NodeSearchCallback = (searchText, node) => {
const theNode = this.nodesMap[node.id];
if (theNode && theNode.data.searchText) {
return theNode.data.searchText.includes(searchText.toLowerCase());
}
return false;
}
private updateNodeStyle(node: HierarchyNavTreeNode) {
const newText = this.prepareNodeText(node);
if (node.text !== newText) {
node.text = newText;
this.nodeEditCallbacks.updateNode(node.id, node.text);
}
const newDisabled = this.nodeDisabledFunction(node.data.nodeCtx);
if (node.state.disabled !== newDisabled) {
node.state.disabled = newDisabled;
if (node.state.disabled) {
this.nodeEditCallbacks.disableNode(node.id);
} else {
this.nodeEditCallbacks.enableNode(node.id);
}
}
const newHasChildren = this.nodeHasChildrenFunction(node.data.nodeCtx);
if (node.children !== newHasChildren) {
node.children = newHasChildren;
this.nodeEditCallbacks.setNodeHasChildren(node.id, node.children);
}
}
private prepareNodeText(node: HierarchyNavTreeNode): string {
const nodeIcon = this.prepareNodeIcon(node.data.nodeCtx);
const nodeText = this.nodeTextFunction(node.data.nodeCtx);
node.data.searchText = nodeText ? nodeText.replace(/<[^>]+>/g, '').toLowerCase() : '';
return nodeIcon + nodeText;
}
private prepareNodeIcon(nodeCtx: HierarchyNodeContext): string {
let iconInfo = this.nodeIconFunction(nodeCtx);
if (iconInfo) {
if (iconInfo === 'default') {
iconInfo = defaultNodeIconFunction(nodeCtx);
}
if (iconInfo && iconInfo !== 'default' && (iconInfo.iconUrl || iconInfo.materialIcon)) {
if (iconInfo.materialIcon) {
return materialIconHtml(iconInfo.materialIcon);
} else {
return iconUrlHtml(iconInfo.iconUrl);
}
} else {
return '';
}
} else {
return '';
}
}
private getCustomerTitle(edgeId) {
this.edgeService.getEdgeInfo(edgeId).subscribe(
(edge) => {

163
ui-ngx/src/app/modules/home/components/widget/lib/edges-overview-widget.models.ts

@ -14,83 +14,16 @@
/// limitations under the License.
///
import { BaseData } from '@shared/models/base-data';
import { EntityId } from '@shared/models/id/entity-id';
import { NavTreeNode, NodesCallback } from '@shared/components/nav-tree.component';
import { NavTreeNode } from '@shared/components/nav-tree.component';
import { Datasource } from '@shared/models/widget.models';
import { isDefined, isUndefined } from '@core/utils';
import { EntityRelationsQuery, EntitySearchDirection, RelationTypeGroup } from '@shared/models/relation.models';
import { EntityType } from '@shared/models/entity-type.models';
import { Edge } from "@shared/models/edge.models";
import { TranslateService } from "@ngx-translate/core";
export interface EdgesOverviewWidgetSettings {
nodeRelationQueryFunction: string;
nodeHasChildrenFunction: string;
nodeOpenedFunction: string;
nodeDisabledFunction: string;
nodeIconFunction: string;
nodeTextFunction: string;
nodesSortFunction: string;
}
export interface HierarchyNodeContext {
parentNodeCtx?: HierarchyNodeContext;
entity: BaseData<EntityId>;
childrenNodesLoaded?: boolean;
level?: number;
data: {[key: string]: any};
}
export interface HierarchyNavTreeNode extends NavTreeNode {
data?: {
datasource: HierarchyNodeDatasource;
nodeCtx: HierarchyNodeContext;
searchText?: string;
};
}
export interface HierarchyNodeDatasource extends Datasource {
export interface EntityNodeDatasource extends Datasource {
nodeId: string;
}
export interface HierarchyNodeIconInfo {
iconUrl?: string;
materialIcon?: string;
}
export type NodeRelationQueryFunction = (nodeCtx: HierarchyNodeContext) => EntityRelationsQuery | 'default';
export type NodeTextFunction = (nodeCtx: HierarchyNodeContext) => string;
export type NodeDisabledFunction = (nodeCtx: HierarchyNodeContext) => boolean;
export type NodeIconFunction = (nodeCtx: HierarchyNodeContext) => HierarchyNodeIconInfo | 'default';
export type NodeOpenedFunction = (nodeCtx: HierarchyNodeContext) => boolean;
export type NodeHasChildrenFunction = (nodeCtx: HierarchyNodeContext) => boolean;
export type NodesSortFunction = (nodeCtx1: HierarchyNodeContext, nodeCtx2: HierarchyNodeContext) => number;
export function loadNodeCtxFunction<F extends (...args: any[]) => any>(functionBody: string, argNames: string, ...args: any[]): F {
let nodeCtxFunction: F = null;
if (isDefined(functionBody) && functionBody.length) {
try {
nodeCtxFunction = new Function(argNames, functionBody) as F;
const res = nodeCtxFunction.apply(null, args);
if (isUndefined(res)) {
nodeCtxFunction = null;
}
} catch (e) {
nodeCtxFunction = null;
}
}
return nodeCtxFunction;
}
export function materialIconHtml(materialIcon: string): string {
return '<mat-icon class="node-icon material-icons" role="img" aria-hidden="false">' + materialIcon + '</mat-icon>';
}
export function iconUrlHtml(iconUrl: string): string {
return '<div class="node-icon" style="background-image: url(' + iconUrl + ');">&nbsp;</div>';
}
export function edgeGroupsNodeText(translate: TranslateService, entityType: EntityType): string {
const nodeIcon = materialIconByEntityType(entityType);
const nodeText = textForEdgeGroupsType(translate, entityType);
@ -112,12 +45,6 @@ export function materialIconByEntityType(entityType: EntityType): string {
case EntityType.ASSET:
materialIcon = 'domain';
break;
case EntityType.CUSTOMER:
materialIcon = 'supervisor_account';
break;
case EntityType.USER:
materialIcon = 'account_circle';
break;
case EntityType.DASHBOARD:
materialIcon = 'dashboards';
break;
@ -127,9 +54,6 @@ export function materialIconByEntityType(entityType: EntityType): string {
case EntityType.RULE_CHAIN:
materialIcon = 'settings_ethernet';
break;
case EntityType.EDGE:
materialIcon = 'router';
break;
}
return '<mat-icon class="node-icon material-icons" role="img" aria-hidden="false">' + materialIcon + '</mat-icon>';
}
@ -156,26 +80,6 @@ export function textForEdgeGroupsType(translate: TranslateService, entityType: E
return translate.instant(textForEdgeGroupsType);
}
export const defaultNodeRelationQueryFunction: NodeRelationQueryFunction = nodeCtx => {
const entity = nodeCtx.entity;
const query: EntityRelationsQuery = {
parameters: {
rootId: entity.id.id,
rootType: entity.id.entityType as EntityType,
direction: EntitySearchDirection.FROM,
relationTypeGroup: RelationTypeGroup.COMMON,
maxLevel: 1
},
filters: [
{
relationType: 'Contains',
entityTypes: []
}
]
};
return query;
};
export const edgeGroupsTypes: EntityType[] = [
EntityType.ASSET,
EntityType.DEVICE,
@ -184,71 +88,20 @@ export const edgeGroupsTypes: EntityType[] = [
EntityType.RULE_CHAIN
]
export const defaultNodeIconFunction: NodeIconFunction = nodeCtx => {
let materialIcon = 'insert_drive_file';
const entity = nodeCtx.entity;
if (entity && entity.id && entity.id.entityType) {
switch (entity.id.entityType as EntityType | string) {
case 'function':
materialIcon = 'functions';
break;
case EntityType.DEVICE:
materialIcon = 'devices_other';
break;
case EntityType.ASSET:
materialIcon = 'domain';
break;
case EntityType.TENANT:
materialIcon = 'supervisor_account';
break;
case EntityType.CUSTOMER:
materialIcon = 'supervisor_account';
break;
case EntityType.USER:
materialIcon = 'account_circle';
break;
case EntityType.DASHBOARD:
materialIcon = 'dashboards';
break;
case EntityType.ALARM:
materialIcon = 'notifications_active';
break;
case EntityType.ENTITY_VIEW:
materialIcon = 'view_quilt';
break;
}
}
return {
materialIcon
};
};
export const defaultNodeOpenedFunction: NodeOpenedFunction = nodeCtx => {
return nodeCtx.level <= 4;
};
export const defaultNodesSortFunction: NodesSortFunction = (nodeCtx1, nodeCtx2) => {
let result = nodeCtx1.entity.id.entityType.localeCompare(nodeCtx2.entity.id.entityType);
if (result === 0) {
result = nodeCtx1.entity.name.localeCompare(nodeCtx2.entity.name);
}
return result;
};
export interface EdgeOverviewNode extends NavTreeNode {
data?: EdgeOverviewNodeData;
}
export type EdgeOverviewNodeData = EdgeGroupsNodeData | EdgeNodeData;
export type EdgeOverviewNodeData = EdgeGroupNodeData | EntityNodeData;
export interface EdgeGroupsNodeData extends BaseEdgeOverviewNodeData {
type: 'edgeGroups';
export interface EdgeGroupNodeData extends BaseEdgeOverviewNodeData {
type: 'edgeGroup';
entityType: EntityType;
edge: Edge;
}
export interface EdgeNodeData extends BaseEdgeOverviewNodeData {
type: 'edge';
export interface EntityNodeData extends BaseEdgeOverviewNodeData {
type: 'entity';
entity: Edge;
}
@ -257,4 +110,4 @@ export interface BaseEdgeOverviewNodeData {
internalId: string;
}
export type EdgeOverviewNodeType = 'edge' | 'edgeGroups';
export type EdgeOverviewNodeType = 'entity' | 'edgeGroup';

Loading…
Cancel
Save