From ba478d48945f872b69a4455afae96ce5d2f6a791 Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Wed, 19 Jun 2019 06:38:50 -0700 Subject: [PATCH 1/4] Initial implementation --- .../components/project-map/project-map.component.html | 10 ++++++++++ .../components/project-map/project-map.component.scss | 2 +- .../components/project-map/project-map.component.ts | 8 ++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/app/components/project-map/project-map.component.html b/src/app/components/project-map/project-map.component.html index c1fc69a6..41dcf2df 100644 --- a/src/app/components/project-map/project-map.component.html +++ b/src/app/components/project-map/project-map.component.html @@ -148,6 +148,16 @@ /> + + diff --git a/src/app/components/project-map/project-map.component.scss b/src/app/components/project-map/project-map.component.scss index a40f4c6d..11274830 100644 --- a/src/app/components/project-map/project-map.component.scss +++ b/src/app/components/project-map/project-map.component.scss @@ -80,7 +80,7 @@ g.node:hover { } .extended { - width: 640px !important; + width: 720px !important; height: 100%; overflow: hidden; } diff --git a/src/app/components/project-map/project-map.component.ts b/src/app/components/project-map/project-map.component.ts index 1d9ebc27..cbc87083 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -44,6 +44,7 @@ import { MapLink } from '../../cartography/models/map/map-link'; import { MapLinkToLinkConverter } from '../../cartography/converters/map/map-link-to-link-converter'; import { LinkWidget } from '../../cartography/widgets/link'; import { NodeCreatedLabelStylesFixer } from './helpers/node-created-label-styles-fixer'; +import { MovingTool } from '../../cartography/tools/moving-tool'; @Component({ @@ -78,6 +79,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { isTextChosen: false, visibility: false }; + protected isLocked: boolean = false; private inReadOnlyMode = false; @@ -109,6 +111,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { private toolsService: ToolsService, private selectionManager: SelectionManager, private selectionTool: SelectionTool, + private movingTool: MovingTool, private recentlyOpenedProjectService: RecentlyOpenedProjectService, private nodeCreatedLabelStylesFixer: NodeCreatedLabelStylesFixer ) {} @@ -395,6 +398,11 @@ export class ProjectMapComponent implements OnInit, OnDestroy { imageToUpload.src = window.URL.createObjectURL(file); } + public changeLockValue() { + this.isLocked = !this.isLocked; + this.movingTool.setEnabled(this.isLocked); + } + public ngOnDestroy() { this.drawingsDataSource.clear(); this.nodesDataSource.clear(); From b4bbd97a406359fc303f37b0a8ae032a21a5d703 Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Mon, 24 Jun 2019 06:24:38 -0700 Subject: [PATCH 2/4] Locking items added --- src/app/app.module.ts | 4 +- .../draggable-selection.component.spec.ts | 4 +- .../draggable-selection.component.ts | 199 ++++++++++-------- .../project-map/project-map.component.spec.ts | 4 +- .../project-map/project-map.component.ts | 8 +- src/app/services/mapsettings.service.ts | 13 ++ 6 files changed, 132 insertions(+), 100 deletions(-) create mode 100644 src/app/services/mapsettings.service.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index be596dd9..cd101275 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -186,6 +186,7 @@ import { DefaultConsoleService } from './services/settings/default-console.servi import { NodeCreatedLabelStylesFixer } from './components/project-map/helpers/node-created-label-styles-fixer'; import { NonNegativeValidator } from './validators/non-negative-validator'; import { RotationValidator } from './validators/rotation-validator'; +import { MapSettingService } from './services/mapsettings.service'; if (environment.production) { Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', { @@ -378,7 +379,8 @@ if (environment.production) { DefaultConsoleService, NodeCreatedLabelStylesFixer, NonNegativeValidator, - RotationValidator + RotationValidator, + MapSettingService ], entryComponents: [ AddServerDialogComponent, diff --git a/src/app/cartography/components/draggable-selection/draggable-selection.component.spec.ts b/src/app/cartography/components/draggable-selection/draggable-selection.component.spec.ts index 53193ad5..fb362b7e 100644 --- a/src/app/cartography/components/draggable-selection/draggable-selection.component.spec.ts +++ b/src/app/cartography/components/draggable-selection/draggable-selection.component.spec.ts @@ -21,6 +21,7 @@ import { MapLabel } from '../../models/map/map-label'; import { MapLinkNode } from '../../models/map/map-link-node'; import { select } from 'd3-selection'; import { MapLink } from '../../models/map/map-link'; +import { MapSettingService } from '../../../services/mapsettings.service'; describe('DraggableSelectionComponent', () => { let component: DraggableSelectionComponent; @@ -121,7 +122,8 @@ describe('DraggableSelectionComponent', () => { { provide: NodesEventSource, useValue: nodesEventSourceStub }, { provide: DrawingsEventSource, useValue: drawingsEventSourceStub }, { provide: GraphDataManager, useValue: mockedGraphDataManager }, - { provide: LinksEventSource, useValue: linksEventSourceStub } + { provide: LinksEventSource, useValue: linksEventSourceStub }, + { provide: MapSettingService, useClass: MapSettingService } ], declarations: [DraggableSelectionComponent] }).compileComponents(); diff --git a/src/app/cartography/components/draggable-selection/draggable-selection.component.ts b/src/app/cartography/components/draggable-selection/draggable-selection.component.ts index 37dce99b..5c284725 100644 --- a/src/app/cartography/components/draggable-selection/draggable-selection.component.ts +++ b/src/app/cartography/components/draggable-selection/draggable-selection.component.ts @@ -17,6 +17,7 @@ import { LabelWidget } from '../../widgets/label'; import { InterfaceLabelWidget } from '../../widgets/interface-label'; import { MapLinkNode } from '../../models/map/map-link-node'; import { LinksEventSource } from '../../events/links-event-source'; +import { MapSettingService } from '../../../services/mapsettings.service'; @Component({ selector: 'app-draggable-selection', @@ -27,6 +28,8 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy { private start: Subscription; private drag: Subscription; private end: Subscription; + private mapSettingsSubscription: Subscription; + private isMapLocked: boolean = false; @Input('svg') svg: SVGSVGElement; @@ -40,12 +43,17 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy { private nodesEventSource: NodesEventSource, private drawingsEventSource: DrawingsEventSource, private graphDataManager: GraphDataManager, - private linksEventSource: LinksEventSource + private linksEventSource: LinksEventSource, + private mapSettingsService: MapSettingService ) {} ngOnInit() { const svg = select(this.svg); + this.mapSettingsService.isMapLocked.subscribe((value) => { + this.isMapLocked = value; + }); + this.start = merge( this.nodesWidget.draggable.start, this.drawingsWidget.draggable.start, @@ -84,75 +92,77 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy { this.labelWidget.draggable.drag, this.interfaceWidget.draggable.drag ).subscribe((evt: DraggableDrag) => { - const selected = this.selectionManager.getSelected(); - const selectedNodes = selected.filter(item => item instanceof MapNode); - // update nodes - selectedNodes.forEach((node: MapNode) => { - node.x += evt.dx; - node.y += evt.dy; + if (!this.isMapLocked) { + const selected = this.selectionManager.getSelected(); + const selectedNodes = selected.filter(item => item instanceof MapNode); + // update nodes + selectedNodes.forEach((node: MapNode) => { + node.x += evt.dx; + node.y += evt.dy; - this.nodesWidget.redrawNode(svg, node); + this.nodesWidget.redrawNode(svg, node); - const links = this.graphDataManager - .getLinks() - .filter( - link => - (link.target !== undefined && link.target.id === node.id) || - (link.source !== undefined && link.source.id === node.id) - ); - - links.forEach(link => { - this.linksWidget.redrawLink(svg, link); - }); - }); - - // update drawings - selected - .filter(item => item instanceof MapDrawing) - .forEach((drawing: MapDrawing) => { - drawing.x += evt.dx; - drawing.y += evt.dy; - this.drawingsWidget.redrawDrawing(svg, drawing); - }); - - // update labels - selected - .filter(item => item instanceof MapLabel) - .forEach((label: MapLabel) => { - const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; - if (isParentNodeSelected) { - return; - } - - const node = this.graphDataManager.getNodes().filter(node => node.id === label.nodeId)[0]; - node.label.x += evt.dx; - node.label.y += evt.dy; - this.labelWidget.redrawLabel(svg, label); - }); - - // update interface labels - selected - .filter(item => item instanceof MapLinkNode) - .forEach((interfaceLabel: MapLinkNode) => { - const isParentNodeSelected = selectedNodes.filter(node => node.id === interfaceLabel.nodeId).length > 0; - if (isParentNodeSelected) { - return; - } - - const link = this.graphDataManager + const links = this.graphDataManager .getLinks() - .filter(link => link.nodes[0].id === interfaceLabel.id || link.nodes[1].id === interfaceLabel.id)[0]; - if (link.nodes[0].id === interfaceLabel.id) { - link.nodes[0].label.x += evt.dx; - link.nodes[0].label.y += evt.dy; - } - if (link.nodes[1].id === interfaceLabel.id) { - link.nodes[1].label.x += evt.dx; - link.nodes[1].label.y += evt.dy; - } + .filter( + link => + (link.target !== undefined && link.target.id === node.id) || + (link.source !== undefined && link.source.id === node.id) + ); - this.linksWidget.redrawLink(svg, link); + links.forEach(link => { + this.linksWidget.redrawLink(svg, link); + }); }); + + // update drawings + selected + .filter(item => item instanceof MapDrawing) + .forEach((drawing: MapDrawing) => { + drawing.x += evt.dx; + drawing.y += evt.dy; + this.drawingsWidget.redrawDrawing(svg, drawing); + }); + + // update labels + selected + .filter(item => item instanceof MapLabel) + .forEach((label: MapLabel) => { + const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; + if (isParentNodeSelected) { + return; + } + + const node = this.graphDataManager.getNodes().filter(node => node.id === label.nodeId)[0]; + node.label.x += evt.dx; + node.label.y += evt.dy; + this.labelWidget.redrawLabel(svg, label); + }); + + // update interface labels + selected + .filter(item => item instanceof MapLinkNode) + .forEach((interfaceLabel: MapLinkNode) => { + const isParentNodeSelected = selectedNodes.filter(node => node.id === interfaceLabel.nodeId).length > 0; + if (isParentNodeSelected) { + return; + } + + const link = this.graphDataManager + .getLinks() + .filter(link => link.nodes[0].id === interfaceLabel.id || link.nodes[1].id === interfaceLabel.id)[0]; + if (link.nodes[0].id === interfaceLabel.id) { + link.nodes[0].label.x += evt.dx; + link.nodes[0].label.y += evt.dy; + } + if (link.nodes[1].id === interfaceLabel.id) { + link.nodes[1].label.x += evt.dx; + link.nodes[1].label.y += evt.dy; + } + + this.linksWidget.redrawLink(svg, link); + }); + } }); this.end = merge( @@ -161,39 +171,41 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy { this.labelWidget.draggable.end, this.interfaceWidget.draggable.end ).subscribe((evt: DraggableEnd) => { - const selected = this.selectionManager.getSelected(); - const selectedNodes = selected.filter(item => item instanceof MapNode); + if (!this.isMapLocked) { + const selected = this.selectionManager.getSelected(); + const selectedNodes = selected.filter(item => item instanceof MapNode); - selectedNodes.forEach((item: MapNode) => { - this.nodesEventSource.dragged.emit(new DraggedDataEvent(item, evt.dx, evt.dy)); - }); - - selected - .filter(item => item instanceof MapDrawing) - .forEach((item: MapDrawing) => { - this.drawingsEventSource.dragged.emit(new DraggedDataEvent(item, evt.dx, evt.dy)); + selectedNodes.forEach((item: MapNode) => { + this.nodesEventSource.dragged.emit(new DraggedDataEvent(item, evt.dx, evt.dy)); }); - selected - .filter(item => item instanceof MapLabel) - .forEach((label: MapLabel) => { - const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; - if (isParentNodeSelected) { - return; - } + selected + .filter(item => item instanceof MapDrawing) + .forEach((item: MapDrawing) => { + this.drawingsEventSource.dragged.emit(new DraggedDataEvent(item, evt.dx, evt.dy)); + }); - this.nodesEventSource.labelDragged.emit(new DraggedDataEvent(label, evt.dx, evt.dy)); - }); + selected + .filter(item => item instanceof MapLabel) + .forEach((label: MapLabel) => { + const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; + if (isParentNodeSelected) { + return; + } - selected - .filter(item => item instanceof MapLinkNode) - .forEach((label: MapLinkNode) => { - const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; - if (isParentNodeSelected) { - return; - } - this.linksEventSource.interfaceDragged.emit(new DraggedDataEvent(label, evt.dx, evt.dy)); - }); + this.nodesEventSource.labelDragged.emit(new DraggedDataEvent(label, evt.dx, evt.dy)); + }); + + selected + .filter(item => item instanceof MapLinkNode) + .forEach((label: MapLinkNode) => { + const isParentNodeSelected = selectedNodes.filter(node => node.id === label.nodeId).length > 0; + if (isParentNodeSelected) { + return; + } + this.linksEventSource.interfaceDragged.emit(new DraggedDataEvent(label, evt.dx, evt.dy)); + }); + } }); } @@ -201,5 +213,6 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy { this.start.unsubscribe(); this.drag.unsubscribe(); this.end.unsubscribe(); + this.mapSettingsSubscription.unsubscribe(); } } diff --git a/src/app/components/project-map/project-map.component.spec.ts b/src/app/components/project-map/project-map.component.spec.ts index bd399179..a8898837 100644 --- a/src/app/components/project-map/project-map.component.spec.ts +++ b/src/app/components/project-map/project-map.component.spec.ts @@ -43,6 +43,7 @@ import { Project } from '../../models/project'; import { CapturingSettings } from '../../models/capturingSettings'; import { LinkWidget } from '../../cartography/widgets/link'; import { NodeCreatedLabelStylesFixer } from './helpers/node-created-label-styles-fixer'; +import { MapSettingService } from '../../services/mapsettings.service'; export class MockedProgressService { public activate() {} @@ -220,7 +221,8 @@ describe('ProjectMapComponent', () => { provide: RecentlyOpenedProjectService, useClass: RecentlyOpenedProjectService }, - { provide: NodeCreatedLabelStylesFixer, useValue: nodeCreatedLabelStylesFixer} + { provide: NodeCreatedLabelStylesFixer, useValue: nodeCreatedLabelStylesFixer}, + { provide: MapSettingService } ], declarations: [ProjectMapComponent, 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 cbc87083..c9a84372 100644 --- a/src/app/components/project-map/project-map.component.ts +++ b/src/app/components/project-map/project-map.component.ts @@ -44,7 +44,7 @@ import { MapLink } from '../../cartography/models/map/map-link'; import { MapLinkToLinkConverter } from '../../cartography/converters/map/map-link-to-link-converter'; import { LinkWidget } from '../../cartography/widgets/link'; import { NodeCreatedLabelStylesFixer } from './helpers/node-created-label-styles-fixer'; -import { MovingTool } from '../../cartography/tools/moving-tool'; +import { MapSettingService } from '../../services/mapsettings.service'; @Component({ @@ -111,9 +111,9 @@ export class ProjectMapComponent implements OnInit, OnDestroy { private toolsService: ToolsService, private selectionManager: SelectionManager, private selectionTool: SelectionTool, - private movingTool: MovingTool, private recentlyOpenedProjectService: RecentlyOpenedProjectService, - private nodeCreatedLabelStylesFixer: NodeCreatedLabelStylesFixer + private nodeCreatedLabelStylesFixer: NodeCreatedLabelStylesFixer, + private mapSettingsService: MapSettingService ) {} ngOnInit() { @@ -400,7 +400,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy { public changeLockValue() { this.isLocked = !this.isLocked; - this.movingTool.setEnabled(this.isLocked); + this.mapSettingsService.changeMapLockValue(this.isLocked); } public ngOnDestroy() { diff --git a/src/app/services/mapsettings.service.ts b/src/app/services/mapsettings.service.ts new file mode 100644 index 00000000..640c5671 --- /dev/null +++ b/src/app/services/mapsettings.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from "@angular/core"; +import { Subject } from 'rxjs'; + +@Injectable() +export class MapSettingService { + public isMapLocked = new Subject(); + + constructor() {} + + changeMapLockValue(value: boolean) { + this.isMapLocked.next(value); + } +} From 6dbce19481c06f4dab0de270ce880f8ebd24a1dd Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Mon, 24 Jun 2019 06:44:51 -0700 Subject: [PATCH 3/4] Tests added --- .../project-map/project-map.component.spec.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/app/components/project-map/project-map.component.spec.ts b/src/app/components/project-map/project-map.component.spec.ts index a8898837..4da0afb2 100644 --- a/src/app/components/project-map/project-map.component.spec.ts +++ b/src/app/components/project-map/project-map.component.spec.ts @@ -185,6 +185,7 @@ describe('ProjectMapComponent', () => { let nodesDataSource = new MockedNodesDataSource(); let linksDataSource = new MockedLinksDataSource(); let nodeCreatedLabelStylesFixer; + let mapSettingService = new MapSettingService(); beforeEach(async(() => { nodeCreatedLabelStylesFixer = { @@ -222,7 +223,7 @@ describe('ProjectMapComponent', () => { useClass: RecentlyOpenedProjectService }, { provide: NodeCreatedLabelStylesFixer, useValue: nodeCreatedLabelStylesFixer}, - { provide: MapSettingService } + { provide: MapSettingService, useValue: mapSettingService } ], declarations: [ProjectMapComponent, D3MapComponent, ...ANGULAR_MAP_DECLARATIONS], schemas: [NO_ERRORS_SCHEMA] @@ -261,4 +262,12 @@ describe('ProjectMapComponent', () => { expect(component.resetDrawToolChoice).toHaveBeenCalled(); }); + + it('should call map settings service when lock value was changed', () => { + spyOn(mapSettingService, 'changeMapLockValue'); + + component.changeLockValue(); + + expect(mapSettingService.changeMapLockValue).toHaveBeenCalled(); + }); }); From 6defbdb9dfaa6c27ef74c11f3c13742209682344 Mon Sep 17 00:00:00 2001 From: Piotr Pekala Date: Tue, 25 Jun 2019 01:51:26 -0700 Subject: [PATCH 4/4] unit tests added --- .../project-map/project-map.component.spec.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/app/components/project-map/project-map.component.spec.ts b/src/app/components/project-map/project-map.component.spec.ts index 4da0afb2..2d34ba48 100644 --- a/src/app/components/project-map/project-map.component.spec.ts +++ b/src/app/components/project-map/project-map.component.spec.ts @@ -270,4 +270,14 @@ describe('ProjectMapComponent', () => { expect(mapSettingService.changeMapLockValue).toHaveBeenCalled(); }); + + it('should call map settings service with proper value', () => { + spyOn(mapSettingService, 'changeMapLockValue'); + + component.changeLockValue(); + expect(mapSettingService.changeMapLockValue).toHaveBeenCalledWith(true); + + component.changeLockValue(); + expect(mapSettingService.changeMapLockValue).toHaveBeenCalledWith(false); + }); });