diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 5827e364..1483c83a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -37,6 +37,7 @@ import { ProjectMapComponent } from './components/project-map/project-map.compon import { ServersComponent } from './components/servers/servers.component'; import { AddServerDialogComponent } from './components/servers/add-server-dialog/add-server-dialog.component'; import { ContextMenuComponent } from './components/project-map/context-menu/context-menu.component'; +import { ContextConsoleMenuComponent } from './components/project-map/context-console-menu/context-console-menu.component'; import { StartNodeActionComponent } from './components/project-map/context-menu/actions/start-node-action/start-node-action.component'; import { StopNodeActionComponent } from './components/project-map/context-menu/actions/stop-node-action/stop-node-action.component'; import { TemplateComponent } from './components/template/template.component'; @@ -299,6 +300,7 @@ import { TemplateNameDialogComponent } from './components/project-map/new-templa DefaultLayoutComponent, ProgressDialogComponent, ContextMenuComponent, + ContextConsoleMenuComponent, StartNodeActionComponent, StopNodeActionComponent, TemplateComponent, diff --git a/src/app/cartography/widgets/node.ts b/src/app/cartography/widgets/node.ts index 908f93b3..de604d4b 100644 --- a/src/app/cartography/widgets/node.ts +++ b/src/app/cartography/widgets/node.ts @@ -15,6 +15,7 @@ import { MapSettingsService } from '../../services/mapsettings.service'; @Injectable() export class NodeWidget implements Widget { public onContextMenu = new EventEmitter(); + public onContextConsoleMenu = new EventEmitter(); public onNodeClicked = new EventEmitter(); constructor( @@ -85,6 +86,10 @@ export class NodeWidget implements Widget { event.preventDefault(); self.onContextMenu.emit(new NodeContextMenu(event, n)); }) + .on('dblclick', function(n: MapNode, i: number) { + event.preventDefault(); + self.onContextConsoleMenu.emit(new NodeContextMenu(event, n)); + }) .attr('xnode:href', (n: MapNode) => n.symbolUrl) .attr('width', (n: MapNode) => { if (n.nodeType === 'cloud' || n.nodeType === 'nat') return 120; diff --git a/src/app/components/project-map/context-console-menu/context-console-menu.component.html b/src/app/components/project-map/context-console-menu/context-console-menu.component.html new file mode 100644 index 00000000..51d98b61 --- /dev/null +++ b/src/app/components/project-map/context-console-menu/context-console-menu.component.html @@ -0,0 +1,22 @@ +
+ + + + Choose default behavior for double click on node + + + + + +
+ + \ No newline at end of file diff --git a/src/app/components/project-map/context-console-menu/context-console-menu.component.scss b/src/app/components/project-map/context-console-menu/context-console-menu.component.scss new file mode 100644 index 00000000..d84da4fb --- /dev/null +++ b/src/app/components/project-map/context-console-menu/context-console-menu.component.scss @@ -0,0 +1,12 @@ +.context-menu { + position: absolute; + min-height: 0px; +} + +.mat-menu-panel ng-trigger ng-trigger-transformMenu ng-tns-c7-5 context-menu-items mat-menu-after mat-menu-below ng-star-inserted mat-elevation-z4 { + min-height: 0px!important; +} + +.title { + margin: 10px; +} \ No newline at end of file diff --git a/src/app/components/project-map/context-console-menu/context-console-menu.component.spec.ts b/src/app/components/project-map/context-console-menu/context-console-menu.component.spec.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/app/components/project-map/context-console-menu/context-console-menu.component.ts b/src/app/components/project-map/context-console-menu/context-console-menu.component.ts new file mode 100644 index 00000000..ec2f9ffb --- /dev/null +++ b/src/app/components/project-map/context-console-menu/context-console-menu.component.ts @@ -0,0 +1,116 @@ +import { ChangeDetectorRef, Component, ComponentFactory, ComponentRef, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core'; +import { MatMenuTrigger } from '@angular/material/menu'; +import { DomSanitizer } from '@angular/platform-browser'; +import { Node } from '../../../cartography/models/node'; +import { Server } from '../../../models/server'; +import { Project } from '../../../models/project'; +import { MapSettingsService } from '../../../services/mapsettings.service'; +import { ElectronService } from 'ngx-electron'; +import { NodeConsoleService } from '../../../services/nodeConsole.service'; +import { ToasterService } from '../../../services/toaster.service'; +import { Router } from '@angular/router'; +import { ComponentFactoryResolver } from '@angular/core'; +import { ConsoleDeviceActionComponent } from '../context-menu/actions/console-device-action/console-device-action.component'; +import { ConsoleDeviceActionBrowserComponent } from '../context-menu/actions/console-device-action-browser/console-device-action-browser.component'; + +@Component({ + selector: 'app-context-console-menu', + templateUrl: './context-console-menu.component.html', + styleUrls: ['./context-console-menu.component.scss'] +}) +export class ContextConsoleMenuComponent implements OnInit { + @Input() project: Project; + @Input() server: Server; + @ViewChild(MatMenuTrigger) contextConsoleMenu: MatMenuTrigger; + @ViewChild("container", { read: ViewContainerRef }) container; + componentRef: ComponentRef; + componentBrowserRef: ComponentRef; + + topPosition; + leftPosition; + isElectronApp = false; + node: Node;; + + constructor( + private sanitizer: DomSanitizer, + private changeDetector: ChangeDetectorRef, + private mapSettingsService: MapSettingsService, + private electronService: ElectronService, + private consoleService: NodeConsoleService, + private toasterService: ToasterService, + private router: Router, + private resolver: ComponentFactoryResolver + ) {} + + ngOnInit() { + this.setPosition(0, 0); + this.isElectronApp = this.electronService.isElectronApp; + } + + public setPosition(top: number, left: number) { + this.topPosition = this.sanitizer.bypassSecurityTrustStyle(top + 'px'); + this.leftPosition = this.sanitizer.bypassSecurityTrustStyle(left + 'px'); + this.changeDetector.detectChanges(); + } + + public openMenu(node: Node, top: number, left: number) { + this.node = node; + let action = this.mapSettingsService.getConsoleContextManuAction(); + if (action) { + if (action === 'web console') { + this.openWebConsole(); + } else if (action === 'web console in new tab') { + this.openWebConsoleInNewTab(); + } else if (action === 'console') { + this.openConsole(); + } + } else { + this.setPosition(top, left); + this.contextConsoleMenu.openMenu(); + } + } + + openConsole() { + this.mapSettingsService.setConsoleContextMenuAction('console'); + if (this.isElectronApp) { + const factory: ComponentFactory = this.resolver.resolveComponentFactory(ConsoleDeviceActionComponent); + this.componentRef = this.container.createComponent(factory); + this.componentRef.instance.server = this.server; + this.componentRef.instance.nodes = [this.node]; + + this.componentRef.instance.console(); + } else { + const factory: ComponentFactory = this.resolver.resolveComponentFactory(ConsoleDeviceActionBrowserComponent); + this.componentBrowserRef = this.container.createComponent(factory); + this.componentBrowserRef.instance.server = this.server; + this.componentBrowserRef.instance.node = this.node; + + this.componentBrowserRef.instance.openConsole(); + } + } + + openWebConsole() { + this.mapSettingsService.setConsoleContextMenuAction('web console'); + if (this.node.status === 'started') { + this.mapSettingsService.logConsoleSubject.next(true); + this.consoleService.openConsoleForNode(this.node); + } else { + this.toasterService.error('To open console please start the node'); + } + } + + openWebConsoleInNewTab() { + this.mapSettingsService.setConsoleContextMenuAction('web console in new tab'); + if (this.node.status === 'started') { + let url = this.router.url.split('/'); + let urlString = `/static/web-ui/${url[1]}/${url[2]}/${url[3]}/${url[4]}/nodes/${this.node.node_id}` + window.open(urlString); + } else { + this.toasterService.error('To open console please start the node'); + } + } + + ngOnDestroy() { + this.componentRef.destroy(); + } +} \ No newline at end of file diff --git a/src/app/components/project-map/context-menu/context-menu.component.scss b/src/app/components/project-map/context-menu/context-menu.component.scss index 2e24dcaf..1226043f 100644 --- a/src/app/components/project-map/context-menu/context-menu.component.scss +++ b/src/app/components/project-map/context-menu/context-menu.component.scss @@ -5,4 +5,4 @@ .mat-menu-panel ng-trigger ng-trigger-transformMenu ng-tns-c7-5 context-menu-items mat-menu-after mat-menu-below ng-star-inserted mat-elevation-z4 { min-height: 0px!important; -} +} \ No newline at end of file diff --git a/src/app/components/project-map/project-map.component.html b/src/app/components/project-map/project-map.component.html index 4b8a6f68..b2b9b0c8 100644 --- a/src/app/components/project-map/project-map.component.html +++ b/src/app/components/project-map/project-map.component.html @@ -179,6 +179,7 @@ +
diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index ea709981..f1374782 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -13,6 +13,7 @@ import { ProjectService } from '../../services/project.service'; import { Server } from '../../models/server'; import { Drawing } from '../../cartography/models/drawing'; import { ContextMenuComponent } from './context-menu/context-menu.component'; +import { ContextConsoleMenuComponent } from './context-console-menu/context-console-menu.component'; import { Template } from '../../models/template'; import { NodeService } from '../../services/node.service'; import { Symbol } from '../../models/symbol'; @@ -112,6 +113,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { public isLightThemeEnabled: boolean = false; @ViewChild(ContextMenuComponent) contextMenu: ContextMenuComponent; + @ViewChild(ContextConsoleMenuComponent) consoleContextMenu: ContextConsoleMenuComponent; @ViewChild(D3MapComponent) mapChild: D3MapComponent; @ViewChild(ProjectMapMenuComponent) projectMapMenuComponent: ProjectMapMenuComponent; @@ -437,6 +439,11 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.contextMenu.openMenuForListOfElements(drawings, nodes, labels, links, event.pageY, event.pageX); }); + const onContextConsoleMenu = this.nodeWidget.onContextConsoleMenu.subscribe((eventNode: NodeContextMenu) => { + const node = this.mapNodeToNode.convert(eventNode.node); + this.consoleContextMenu.openMenu(node, eventNode.event.pageY, eventNode.event.pageX); + }); + this.projectMapSubscription.add(onLinkContextMenu); this.projectMapSubscription.add(onEthernetLinkContextMenu); this.projectMapSubscription.add(onSerialLinkContextMenu); @@ -445,6 +452,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { this.projectMapSubscription.add(onContextMenu); this.projectMapSubscription.add(onLabelContextMenu); this.projectMapSubscription.add(onInterfaceLabelContextMenu); + this.projectMapSubscription.add(onContextConsoleMenu); this.mapChangeDetectorRef.detectChanges(); } diff --git a/src/app/services/mapsettings.service.ts b/src/app/services/mapsettings.service.ts index ec772d27..4170a075 100644 --- a/src/app/services/mapsettings.service.ts +++ b/src/app/services/mapsettings.service.ts @@ -22,6 +22,14 @@ export class MapSettingsService { this.isMapLocked.next(value); } + setConsoleContextMenuAction(action: string) { + localStorage.setItem('consoleContextMenu', action); + } + + getConsoleContextManuAction(): string { + return localStorage.getItem('consoleContextMenu'); + } + toggleTopologySummary(value: boolean) { this.isTopologySummaryVisible = value; }