diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4242bce2..cbe37e82 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -191,6 +191,7 @@ import { DuplicateActionComponent } from './components/project-map/context-menu/ import { MapSettingService } from './services/mapsettings.service'; import { ProjectMapMenuComponent } from './components/project-map/project-map-menu/project-map-menu.component'; import { HelpComponent } from './components/help/help.component'; +import { BringToFrontActionComponent } from './components/project-map/context-menu/actions/bring-to-front-action/bring-to-front-action.component'; if (environment.production) { Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', { @@ -312,7 +313,8 @@ if (environment.production) { ConsoleComponent, NodesMenuComponent, ProjectMapMenuComponent, - HelpComponent + HelpComponent, + BringToFrontActionComponent ], imports: [ BrowserModule, diff --git a/src/app/common/progress/progress.component.spec.ts b/src/app/common/progress/progress.component.spec.ts index e5c8f4bb..735354d2 100644 --- a/src/app/common/progress/progress.component.spec.ts +++ b/src/app/common/progress/progress.component.spec.ts @@ -7,7 +7,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { Router } from '@angular/router'; import { BehaviorSubject, Observable } from 'rxjs'; -class MockedRouter { +export class MockedRouter { events: BehaviorSubject; constructor() { diff --git a/src/app/components/help/help.component.html b/src/app/components/help/help.component.html index 0eda8204..eab6611f 100644 --- a/src/app/components/help/help.component.html +++ b/src/app/components/help/help.component.html @@ -3,6 +3,19 @@
+ + + Useful shortcuts + + + ctrl + + to zoom in + ctrl + - to zoom out + ctrl + 0 to reset zoom + ctrl + a to select all items on map + ctrl + shift + a to deselect all items on map + ctrl + shift + s to go to preferences + + Third party components diff --git a/src/app/components/project-map/context-menu/actions/bring-to-front-action/bring-to-front-action.component.html b/src/app/components/project-map/context-menu/actions/bring-to-front-action/bring-to-front-action.component.html new file mode 100644 index 00000000..2a3518ab --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/bring-to-front-action/bring-to-front-action.component.html @@ -0,0 +1,4 @@ + diff --git a/src/app/components/project-map/context-menu/actions/bring-to-front-action/bring-to-front-action.component.spec.ts b/src/app/components/project-map/context-menu/actions/bring-to-front-action/bring-to-front-action.component.spec.ts new file mode 100644 index 00000000..74a264f5 --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/bring-to-front-action/bring-to-front-action.component.spec.ts @@ -0,0 +1,66 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { BringToFrontActionComponent } from './bring-to-front-action.component'; +import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material'; +import { CommonModule } from '@angular/common'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { MockedDrawingService, MockedDrawingsDataSource, MockedNodesDataSource, MockedNodeService } from '../../../project-map.component.spec'; +import { DrawingService } from '../../../../../services/drawing.service'; +import { NodesDataSource } from '../../../../../cartography/datasources/nodes-datasource'; +import { DrawingsDataSource } from '../../../../../cartography/datasources/drawings-datasource'; +import { NodeService } from '../../../../../services/node.service'; +import { Node } from '../../../../../cartography/models/node'; +import { of } from 'rxjs'; +import { ComponentFactoryResolver } from '@angular/core'; +import { Drawing } from '../../../../../cartography/models/drawing'; + +describe('BringToFrontActionComponent', () => { + let component: BringToFrontActionComponent; + let fixture: ComponentFixture; + let drawingService = new MockedDrawingService(); + let drawingsDataSource = new MockedDrawingsDataSource(); + let nodeService = new MockedNodeService(); + let nodesDataSource = new MockedNodesDataSource(); + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule], + providers: [ + { provide: DrawingService, useValue: drawingService }, + { provide: DrawingsDataSource, useValue: drawingsDataSource }, + { provide: NodeService, useValue: nodeService }, + { provide: NodesDataSource, useValue: nodesDataSource }, + ], + declarations: [BringToFrontActionComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(BringToFrontActionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should call node service when bring to front action called', () => { + spyOn(nodeService, 'update').and.returnValue(of()); + component.nodes = [{z: 0} as Node]; + component.drawings = []; + + component.bringToFront(); + + expect(nodeService.update).toHaveBeenCalled(); + }); + + it('should call drawing service when bring to front action called', () => { + spyOn(drawingService, 'update').and.returnValue(of()); + component.nodes = []; + component.drawings = [{z: 0} as Drawing]; + + component.bringToFront(); + + expect(drawingService.update).toHaveBeenCalled(); + }); +}); diff --git a/src/app/components/project-map/context-menu/actions/bring-to-front-action/bring-to-front-action.component.ts b/src/app/components/project-map/context-menu/actions/bring-to-front-action/bring-to-front-action.component.ts new file mode 100644 index 00000000..b72848fb --- /dev/null +++ b/src/app/components/project-map/context-menu/actions/bring-to-front-action/bring-to-front-action.component.ts @@ -0,0 +1,43 @@ +import { Component, OnInit, Input } from '@angular/core'; +import { Server } from '../../../../../models/server'; +import { Node } from '../../../../../cartography/models/node'; +import { NodesDataSource } from '../../../../../cartography/datasources/nodes-datasource'; +import { NodeService } from '../../../../../services/node.service'; +import { Drawing } from '../../../../../cartography/models/drawing'; +import { DrawingsDataSource } from '../../../../../cartography/datasources/drawings-datasource'; +import { DrawingService } from '../../../../../services/drawing.service'; + +@Component({ + selector: 'app-bring-to-front-action', + templateUrl: './bring-to-front-action.component.html' +}) +export class BringToFrontActionComponent implements OnInit { + @Input() server: Server; + @Input() nodes: Node[]; + @Input() drawings: Drawing[]; + + constructor( + private nodesDataSource: NodesDataSource, + private drawingsDataSource: DrawingsDataSource, + private nodeService: NodeService, + private drawingService: DrawingService + ) {} + + ngOnInit() {} + + bringToFront() { + this.nodes.forEach((node) => { + node.z = 100; + this.nodesDataSource.update(node); + + this.nodeService.update(this.server, node).subscribe((node: Node) => {}); + }); + + this.drawings.forEach((drawing) => { + drawing.z = 100; + this.drawingsDataSource.update(drawing); + + this.drawingService.update(this.server, drawing).subscribe((drawing: Drawing) => {}); + }); + } +} diff --git a/src/app/components/project-map/context-menu/context-menu.component.html b/src/app/components/project-map/context-menu/context-menu.component.html index 2ebfeb6a..1824f2ad 100644 --- a/src/app/components/project-map/context-menu/context-menu.component.html +++ b/src/app/components/project-map/context-menu/context-menu.component.html @@ -44,6 +44,12 @@ [nodes]="nodes" [drawings]="drawings" > + { { provide: MapScaleService }, { provide: NodeCreatedLabelStylesFixer, useValue: nodeCreatedLabelStylesFixer}, { provide: ToasterService, useValue: mockedToasterService }, - { provide: Router, useValue: mockedRouter } + { provide: Router, useValue: mockedRouter }, + { provide: MapNodesDataSource, useClass: MapNodesDataSource }, + { provide: MapLinksDataSource, useClass: LinksDataSource }, + { provide: MapDrawingsDataSource, useClass: MapDrawingsDataSource }, + { provide: MapSymbolsDataSource, useClass: MapSymbolsDataSource } ], declarations: [ProjectMapComponent, ProjectMapMenuComponent, D3MapComponent, ...ANGULAR_MAP_DECLARATIONS], schemas: [NO_ERRORS_SCHEMA] diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index 369524b1..5a2439ba 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -51,6 +51,7 @@ import { LabelWidget } from '../../cartography/widgets/label'; import { MapLinkNodeToLinkNodeConverter } from '../../cartography/converters/map/map-link-node-to-link-node-converter'; import { ProjectMapMenuComponent } from './project-map-menu/project-map-menu.component'; import { ToasterService } from '../../services/toaster.service'; +import { MapNodesDataSource, MapLinksDataSource, MapDrawingsDataSource, MapSymbolsDataSource, Indexed } from '../../cartography/datasources/map-datasource'; @Component({ @@ -117,6 +118,10 @@ export class ProjectMapComponent implements OnInit, OnDestroy { private nodeCreatedLabelStylesFixer: NodeCreatedLabelStylesFixer, private toasterService: ToasterService, private router: Router, + private mapNodesDataSource: MapNodesDataSource, + private mapLinksDataSource: MapLinksDataSource, + private mapDrawingsDataSource: MapDrawingsDataSource, + private mapSymbolsDataSource: MapSymbolsDataSource ) {} ngOnInit() { @@ -197,11 +202,37 @@ export class ProjectMapComponent implements OnInit, OnDestroy { addKeyboardListeners() { Mousetrap.bind('ctrl++', (event: Event) => { event.preventDefault(); + this.zoomIn(); }); Mousetrap.bind('ctrl+-', (event: Event) => { event.preventDefault(); - });; + this.zoomOut(); + }); + + Mousetrap.bind('ctrl+0', (event: Event) => { + event.preventDefault(); + this.resetZoom(); + }); + + Mousetrap.bind('ctrl+a', (event: Event) => { + event.preventDefault(); + let allNodes: Indexed[] = this.mapNodesDataSource.getItems(); + let allDrawings: Indexed[] = this.mapDrawingsDataSource.getItems(); + let allLinks: Indexed[] = this.mapLinksDataSource.getItems(); + let allSymbols: Indexed[] = this.mapSymbolsDataSource.getItems(); + this.selectionManager.setSelected(allNodes.concat(allDrawings).concat(allLinks).concat(allSymbols)); + }); + + Mousetrap.bind('ctrl+shift+a', (event: Event) => { + event.preventDefault(); + this.selectionManager.setSelected([]); + }); + + Mousetrap.bind('ctrl+shift+s', (event: Event) => { + event.preventDefault(); + this.router.navigate(['/server', this.server.id, 'preferences']); + }); } onProjectLoad(project: Project) {