diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 65bade96..79b52ee1 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -40,6 +40,7 @@ import { AppComponent } from './app.component'; import { MapComponent } from './cartography/map/map.component'; import { CreateSnapshotDialogComponent, ProjectMapComponent } from './project-map/project-map.component'; import { ServersComponent, AddServerDialogComponent } from './servers/servers.component'; +import { ContextMenuComponent } from './shared/context-menu/context-menu.component'; @NgModule({ @@ -53,6 +54,7 @@ import { ServersComponent, AddServerDialogComponent } from './servers/servers.co ProjectsComponent, DefaultLayoutComponent, ProgressDialogComponent, + ContextMenuComponent, ], imports: [ NgbModule.forRoot(), diff --git a/src/app/cartography/map/map.component.html b/src/app/cartography/map/map.component.html index c56fc706..aaf936ff 100644 --- a/src/app/cartography/map/map.component.html +++ b/src/app/cartography/map/map.component.html @@ -1 +1,15 @@ + +
+ + + + + +
diff --git a/src/app/cartography/map/map.component.scss b/src/app/cartography/map/map.component.scss index c41075de..194c56d0 100644 --- a/src/app/cartography/map/map.component.scss +++ b/src/app/cartography/map/map.component.scss @@ -1,3 +1,7 @@ svg { display: block; } + +.context-menu { + position: absolute; +} diff --git a/src/app/cartography/map/map.component.ts b/src/app/cartography/map/map.component.ts index ca00d3c5..988ad28a 100644 --- a/src/app/cartography/map/map.component.ts +++ b/src/app/cartography/map/map.component.ts @@ -1,4 +1,8 @@ -import { Component, ElementRef, Input, OnChanges, OnDestroy, OnInit, SimpleChange } from '@angular/core'; +import { + ChangeDetectorRef, + Component, ElementRef, Input, NgZone, OnChanges, OnDestroy, OnInit, SimpleChange, + ViewChild +} from '@angular/core'; import { D3, D3Service } from 'd3-ng2-service'; import { Selection } from 'd3-selection'; @@ -8,12 +12,22 @@ import { GraphLayout } from "../shared/widgets/graph.widget"; import { Context } from "../../map/models/context"; import { Size } from "../shared/models/size.model"; import { Drawing } from "../shared/models/drawing.model"; +import {MatMenuTrigger} from "@angular/material"; +import {NodeOnContextMenuListener} from "../shared/widgets/nodes.widget"; +import {DomSanitizer} from "@angular/platform-browser"; +class NodeOnContextMenu implements NodeOnContextMenuListener { + constructor(private component: MapComponent) {} + + onContextMenu() { + this.component.contextMenu.openMenu(); + } +} @Component({ selector: 'app-map', - templateUrl: '../../map/map.component.html', - styleUrls: ['../../map/map.component.css'] + templateUrl: './map.component.html', + styleUrls: ['./map.component.scss'] }) export class MapComponent implements OnInit, OnChanges, OnDestroy { @Input() nodes: Node[] = []; @@ -21,10 +35,10 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy { @Input() drawings: Drawing[] = []; @Input() width = 1500; @Input() height = 600; - @Input() phylloRadius = 7; - @Input() pointRadius= 2; @Input() windowFullSize = true; + @ViewChild(MatMenuTrigger) contextMenu: MatMenuTrigger; + private d3: D3; private parentNativeElement: any; private svg: Selection; @@ -32,19 +46,25 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy { private graphLayout: GraphLayout; private graphContext: Context; + public contextMenuTop; + public contextMenuLeft; + constructor(protected element: ElementRef, - protected d3Service: D3Service + protected d3Service: D3Service, + protected sanitizer: DomSanitizer, + protected changeDetector: ChangeDetectorRef ) { this.d3 = d3Service.getD3(); this.parentNativeElement = element.nativeElement; + + this.contextMenuLeft = this.sanitizer.bypassSecurityTrustStyle("0"); + this.contextMenuTop = this.sanitizer.bypassSecurityTrustStyle("0"); } ngOnChanges(changes: { [propKey: string]: SimpleChange }) { if ( (changes['width'] && !changes['width'].isFirstChange()) || (changes['height'] && !changes['height'].isFirstChange()) || - (changes['phylloRadius'] && !changes['phylloRadius'].isFirstChange()) || - (changes['pointRadius'] && !changes['pointRadius'].isFirstChange()) || (changes['drawings'] && !changes['drawings'].isFirstChange()) || (changes['nodes'] && !changes['nodes'].isFirstChange()) || (changes['links'] && !changes['links'].isFirstChange()) @@ -89,7 +109,15 @@ export class MapComponent implements OnInit, OnChanges, OnDestroy { this.graphLayout = new GraphLayout(); this.graphLayout.draw(this.svg, this.graphContext); + // this.graphLayout.getNodesWidget().setOnContextMenuListener(new NodeOnContextMenu(self)); + this.graphLayout.getNodesWidget().setOnContextMenuCallback((event: any) => { + this.contextMenuLeft = this.sanitizer.bypassSecurityTrustStyle(event.clientX + "px"); + this.contextMenuTop = this.sanitizer.bypassSecurityTrustStyle(event.clientY + "px"); + this.changeDetector.detectChanges(); + + this.contextMenu.openMenu(); + }); } } diff --git a/src/app/cartography/shared/listeners/node-on-context-menu-listener.ts b/src/app/cartography/shared/listeners/node-on-context-menu-listener.ts new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/app/cartography/shared/listeners/node-on-context-menu-listener.ts @@ -0,0 +1 @@ + diff --git a/src/app/cartography/shared/widgets/graph.widget.ts b/src/app/cartography/shared/widgets/graph.widget.ts index d4c59a19..df91ce62 100644 --- a/src/app/cartography/shared/widgets/graph.widget.ts +++ b/src/app/cartography/shared/widgets/graph.widget.ts @@ -34,6 +34,10 @@ export class GraphLayout implements Widget { this.drawings = drawings; } + public getNodesWidget() { + return this.nodesWidget; + } + draw(view: SVGSelection, context: Context) { const self = this; @@ -59,7 +63,7 @@ export class GraphLayout implements Widget { this.linksWidget.draw(canvas, this.links); this.nodesWidget.draw(canvas, this.nodes); - this.drawingsWidget.draw(canvas, this.drawings) + this.drawingsWidget.draw(canvas, this.drawings); const onZoom = function(this: SVGSVGElement) { const e: D3ZoomEvent = event; diff --git a/src/app/cartography/shared/widgets/nodes.widget.ts b/src/app/cartography/shared/widgets/nodes.widget.ts index 53489d12..c06c9f05 100644 --- a/src/app/cartography/shared/widgets/nodes.widget.ts +++ b/src/app/cartography/shared/widgets/nodes.widget.ts @@ -1,13 +1,31 @@ import {Widget} from "./widget"; import {Node} from "../models/node.model"; import {SVGSelection} from "../../../map/models/types"; +import { event } from "d3-selection"; +import { D3} from "d3-ng2-service"; + import {D3DragEvent} from "d3-drag"; import {select} from "d3-selection"; +import {isUndefined} from "util"; +export interface NodeOnContextMenuListener { + onContextMenu(): void; +}; export class NodesWidget implements Widget { + private onContextMenuListener: NodeOnContextMenuListener; + private onContextMenuCallback: (event: any) => void; + constructor() {} + public setOnContextMenuListener(onContextMenuListener: NodeOnContextMenuListener) { + this.onContextMenuListener = onContextMenuListener; + } + + public setOnContextMenuCallback(onContextMenuCallback: (event: any) => void) { + this.onContextMenuCallback = onContextMenuCallback; + } + public draw(view: SVGSelection, nodes: Node[]) { const self = this; @@ -33,7 +51,17 @@ export class NodesWidget implements Widget { const node_image = node_enter.append('image') .attr('xlink:href', (n: Node) => 'data:image/svg+xml;base64,' + btoa(n.icon.raw)) .attr('width', (n: Node) => n.width) - .attr('height', (n: Node) => n.height); + .attr('height', (n: Node) => n.height) + .on("contextmenu", function (n: Node, i: number) { + // if (self.onContextMenuListener !== ) { + // self.onContextMenuListener.onContextMenu(); + // } + console.log(event); + event.preventDefault(); + if (self.onContextMenuCallback !== null) { + self.onContextMenuCallback(event); + } + }); node_enter.append('circle') .attr('class', 'node_point') diff --git a/src/app/map/map.component.css b/src/app/map/map.component.css deleted file mode 100644 index c41075de..00000000 --- a/src/app/map/map.component.css +++ /dev/null @@ -1,3 +0,0 @@ -svg { - display: block; -} diff --git a/src/app/map/map.component.html b/src/app/map/map.component.html deleted file mode 100644 index 5fe8c1c8..00000000 --- a/src/app/map/map.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/shared/context-menu/context-menu.component.html b/src/app/shared/context-menu/context-menu.component.html new file mode 100644 index 00000000..5c371fb9 --- /dev/null +++ b/src/app/shared/context-menu/context-menu.component.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/app/shared/context-menu/context-menu.component.scss b/src/app/shared/context-menu/context-menu.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/shared/context-menu/context-menu.component.spec.ts b/src/app/shared/context-menu/context-menu.component.spec.ts new file mode 100644 index 00000000..9343e130 --- /dev/null +++ b/src/app/shared/context-menu/context-menu.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ContextMenuComponent } from './context-menu.component'; + +describe('ContextMenuComponent', () => { + let component: ContextMenuComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ContextMenuComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ContextMenuComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/context-menu/context-menu.component.ts b/src/app/shared/context-menu/context-menu.component.ts new file mode 100644 index 00000000..e03e4966 --- /dev/null +++ b/src/app/shared/context-menu/context-menu.component.ts @@ -0,0 +1,24 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { MatMenuTrigger } from '@angular/material'; + + +@Component({ + selector: 'app-context-menu', + templateUrl: './context-menu.component.html', + styleUrls: ['./context-menu.component.scss'] +}) +export class ContextMenuComponent implements OnInit { + @ViewChild(MatMenuTrigger) contextMenu: MatMenuTrigger; + + constructor() { } + + ngOnInit() { + } + + openContextMenu(event) { + event.preventDefault(); + this.contextMenu.openMenu(); + } + +} +