Merge branch 'develop' into add-rectangle-change-border-position

This commit is contained in:
PiotrP 2018-11-28 04:29:15 -08:00
commit 42d1ce522e
42 changed files with 736 additions and 165 deletions

View File

@ -1,4 +1,4 @@
# Editor configuration, see http://editorconfig.org
# Editor configuration, see https://editorconfig.org
root = true
[*]

View File

@ -46,7 +46,7 @@ before_script:
- greenkeeper-lockfile-update
- npm install -g codecov
script: yarn ng test --watch=false --code-coverage
script: yarn coverage
after_success:
- codecov

16
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch localhost",
"type": "firefox",
"request": "launch",
"reAttach": true,
"url": "http://localhost:4200",
"webRoot": "${workspaceFolder}"
}
]
}

View File

@ -133,6 +133,9 @@
"assets": [
"src/assets",
"src/favicon.ico"
],
"codeCoverageExclude": [
"src/app/cartography/components/experimental-map/**/*"
]
}
},

View File

@ -22,7 +22,8 @@
"distlinux": "yarn buildforelectron && electron-builder --linux --x64",
"distwin": "yarn buildforelectron && electron-builder --win --x64",
"distmac": "yarn buildforelectron && electron-builder --mac --x64",
"release": "build"
"release": "build",
"coverage": "ng test --watch=false --code-coverage"
},
"private": true,
"dependencies": {

View File

@ -74,6 +74,7 @@ import { DrawingService } from './services/drawing.service';
import { ProjectNameValidator } from './components/projects/models/projectNameValidator';
import { MatSidenavModule } from '@angular/material';
import { NodeSelectInterfaceComponent } from './components/project-map/node-select-interface/node-select-interface.component';
import { DrawLinkToolComponent } from './components/project-map/draw-link-tool/draw-link-tool.component';
if (environment.production) {
@ -115,7 +116,8 @@ if (environment.production) {
LocalServerComponent,
ProgressComponent,
ServerDiscoveryComponent,
NodeSelectInterfaceComponent
NodeSelectInterfaceComponent,
DrawLinkToolComponent
],
imports: [
NgbModule.forRoot(),

View File

@ -1,5 +1,3 @@
import { DraggableComponent } from './components/draggable/draggable.component';
import { SelectionComponent } from './components/selection/selection.component';
import { NodeComponent } from './components/experimental-map/node/node.component';
import { LinkComponent } from './components/experimental-map/link/link.component';
import { StatusComponent } from './components/experimental-map/status/status.component';
@ -10,6 +8,8 @@ import { LineComponent } from './components/experimental-map/drawing/drawings/li
import { RectComponent } from './components/experimental-map/drawing/drawings/rect/rect.component';
import { TextComponent } from './components/experimental-map/drawing/drawings/text/text.component';
import { InterfaceLabelComponent } from './components/experimental-map/interface-label/interface-label.component';
import { DraggableComponent } from './components/experimental-map/draggable/draggable.component';
import { SelectionComponent } from './components/experimental-map/selection/selection.component';
export const ANGULAR_MAP_DECLARATIONS = [

View File

@ -2,9 +2,7 @@ import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatMenuModule, MatIconModule } from '@angular/material';
import { DrawLinkToolComponent } from './components/draw-link-tool/draw-link-tool.component';
import { DrawingResizingComponent } from './components/drawing-resizing/drawing-resizing.component';
import { CssFixer } from './helpers/css-fixer';
import { FontFixer } from './helpers/font-fixer';
import { MultiLinkCalculatorHelper } from './helpers/multi-link-calculator-helper';
@ -54,7 +52,6 @@ import { MapSettingsManager } from './managers/map-settings-manager';
declarations: [
D3MapComponent,
ExperimentalMapComponent,
DrawLinkToolComponent,
DrawingResizingComponent,
...ANGULAR_MAP_DECLARATIONS,
SelectionControlComponent,

View File

@ -8,7 +8,6 @@
</filter>
</svg>
<app-draw-link-tool *ngIf="drawLinkTool"></app-draw-link-tool>
<app-drawing-resizing></app-drawing-resizing>
<app-selection-control></app-selection-control>
<app-selection-select></app-selection-select>

Before

Width:  |  Height:  |  Size: 435 B

After

Width:  |  Height:  |  Size: 372 B

View File

@ -1,13 +1,129 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { DraggableSelectionComponent } from './draggable-selection.component';
import { NodesWidget } from '../../widgets/nodes';
import { DrawingsWidget } from '../../widgets/drawings';
import { LinksWidget } from '../../widgets/links';
import { LabelWidget } from '../../widgets/label';
import { InterfaceLabelWidget } from '../../widgets/interface-label';
import { SelectionManager } from '../../managers/selection-manager';
import { SelectionManagerMock } from '../../managers/selection-manager.spec';
import { NodesEventSource } from '../../events/nodes-event-source';
import { DrawingsEventSource } from '../../events/drawings-event-source';
import { GraphDataManager } from '../../managers/graph-data-manager';
import { MockedGraphDataManager } from '../../managers/graph-data-manager.spec';
import { LinksEventSource } from '../../events/links-event-source';
import { DraggableStart, DraggableDrag, DraggableEnd } from '../../events/draggable';
import { MapNode } from '../../models/map/map-node';
import { EventEmitter } from '@angular/core';
import { MapDrawing } from '../../models/map/map-drawing';
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';
describe('DraggableSelectionComponent', () => {
let component: DraggableSelectionComponent;
let fixture: ComponentFixture<DraggableSelectionComponent>;
let mockedGraphDataManager: MockedGraphDataManager;
let nodesStartEventEmitter: EventEmitter<DraggableStart<MapNode>>;
let nodesDragEventEmitter: EventEmitter<DraggableDrag<MapNode>>;
let nodesEndEventEmitter: EventEmitter<DraggableEnd<MapNode>>;
let drawingsStartEventEmitter: EventEmitter<DraggableStart<MapDrawing>>;
let drawingsDragEventEmitter: EventEmitter<DraggableDrag<MapDrawing>>;
let drawingsEndEventEmitter: EventEmitter<DraggableEnd<MapDrawing>>;
let labelStartEventEmitter: EventEmitter<DraggableStart<MapLabel>>;
let labelDragEventEmitter: EventEmitter<DraggableDrag<MapLabel>>;
let labelEndEventEmitter: EventEmitter<DraggableEnd<MapLabel>>;
let interfaceLabelStartEventEmitter: EventEmitter<DraggableStart<MapLinkNode>>;
let interfaceLabelDragEventEmitter: EventEmitter<DraggableDrag<MapLinkNode>>;
let interfaceLabelEndEventEmitter: EventEmitter<DraggableEnd<MapLinkNode>>;
beforeEach(async(() => {
mockedGraphDataManager = new MockedGraphDataManager();
nodesStartEventEmitter = new EventEmitter<DraggableStart<MapNode>>();
nodesDragEventEmitter = new EventEmitter<DraggableDrag<MapNode>>();
nodesEndEventEmitter = new EventEmitter<DraggableEnd<MapNode>>();
drawingsStartEventEmitter = new EventEmitter<DraggableStart<MapDrawing>>();
drawingsDragEventEmitter = new EventEmitter<DraggableDrag<MapDrawing>>();
drawingsEndEventEmitter = new EventEmitter<DraggableEnd<MapDrawing>>();
labelStartEventEmitter = new EventEmitter<DraggableStart<MapLabel>>();
labelDragEventEmitter = new EventEmitter<DraggableDrag<MapLabel>>();
labelEndEventEmitter = new EventEmitter<DraggableEnd<MapLabel>>();
interfaceLabelStartEventEmitter = new EventEmitter<DraggableStart<MapLinkNode>>();
interfaceLabelDragEventEmitter = new EventEmitter<DraggableDrag<MapLinkNode>>();
interfaceLabelEndEventEmitter = new EventEmitter<DraggableEnd<MapLinkNode>>();
const nodesWidgetStub = {
redrawNode: () => {},
draggable: {
start: nodesStartEventEmitter,
drag: nodesDragEventEmitter,
end: nodesEndEventEmitter
}
};
const drawingsWidgetStub = {
redrawDrawing: () => {},
draggable: {
start: drawingsStartEventEmitter,
drag: drawingsDragEventEmitter,
end: drawingsEndEventEmitter
}
};
const linksWidgetStub = {
redrawLink: () => {},
};
const labelWidgetStub = {
redrawLabel: () => {},
draggable: {
start: labelStartEventEmitter,
drag: labelDragEventEmitter,
end: labelEndEventEmitter
}
};
const interfaceLabelWidgetStub = {
draggable: {
start: interfaceLabelStartEventEmitter,
drag: interfaceLabelDragEventEmitter,
end: interfaceLabelEndEventEmitter
}
};
const nodesEventSourceStub = {
dragged: { emit: () => {}},
labelDragged: { emit: () => {}}
};
const drawingsEventSourceStub = {
dragged: { emit: () => {}}
};
const linksEventSourceStub = {
interfaceDragged: { emit: () => {}}
};
TestBed.configureTestingModule({
providers: [
{ provide: NodesWidget, useValue: nodesWidgetStub },
{ provide: DrawingsWidget, useValue: drawingsWidgetStub },
{ provide: LinksWidget, useValue: linksWidgetStub },
{ provide: LabelWidget, useValue: labelWidgetStub },
{ provide: InterfaceLabelWidget, useValue: interfaceLabelWidgetStub },
{ provide: SelectionManager, useValue: new SelectionManagerMock() },
{ provide: NodesEventSource, useValue: nodesEventSourceStub },
{ provide: DrawingsEventSource, useValue: drawingsEventSourceStub },
{ provide: GraphDataManager, useValue: mockedGraphDataManager },
{ provide: LinksEventSource, useValue: linksEventSourceStub },
],
declarations: [ DraggableSelectionComponent ]
})
.compileComponents();
@ -16,10 +132,418 @@ describe('DraggableSelectionComponent', () => {
beforeEach(() => {
fixture = TestBed.createComponent(DraggableSelectionComponent);
component = fixture.componentInstance;
component.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
fixture.detectChanges();
});
// it('should create', () => {
// expect(component).toBeTruthy();
// });
it('should create', () => {
expect(component).toBeTruthy();
});
describe('nodes dragging', () => {
let nodesWidgetStub: NodesWidget;
let linksWidgetStub: LinksWidget;
let selectionManagerStub: SelectionManager;
let node: MapNode;
beforeEach(() => {
nodesWidgetStub = fixture.debugElement.injector.get(NodesWidget);
linksWidgetStub = fixture.debugElement.injector.get(LinksWidget);
selectionManagerStub = fixture.debugElement.injector.get(SelectionManager);
node = new MapNode();
node.id = "nodeid";
node.x = 1;
node.y = 2;
});
it('should select node when started dragging', fakeAsync(() => {
nodesWidgetStub.draggable.start.emit(new DraggableStart<MapNode>(node));
tick();
expect(selectionManagerStub.getSelected().length).toEqual(1);
}));
it('should ignore node when started dragging and node is in selection', fakeAsync(() => {
selectionManagerStub.setSelected([node]);
nodesWidgetStub.draggable.start.emit(new DraggableStart<MapNode>(node));
tick();
expect(selectionManagerStub.getSelected().length).toEqual(1);
}));
it('should update node position when dragging', fakeAsync(() => {
spyOn(nodesWidgetStub, 'redrawNode');
selectionManagerStub.setSelected([node]);
const dragEvent = new DraggableDrag<MapNode>(node);
dragEvent.dx = 10;
dragEvent.dy = 20;
nodesWidgetStub.draggable.drag.emit(dragEvent);
tick();
expect(nodesWidgetStub.redrawNode).toHaveBeenCalledWith(select(fixture.componentInstance.svg), node);
expect(node.x).toEqual(11);
expect(node.y).toEqual(22);
}));
it('should redraw related links target when dragging node', fakeAsync(() => {
spyOn(nodesWidgetStub, 'redrawNode');
spyOn(linksWidgetStub, 'redrawLink');
const link = new MapLink();
link.target = node;
mockedGraphDataManager.setLinks([link]);
selectionManagerStub.setSelected([node]);
nodesWidgetStub.draggable.drag.emit(new DraggableDrag<MapNode>(node));
tick();
expect(linksWidgetStub.redrawLink).toHaveBeenCalledWith(select(fixture.componentInstance.svg), link);
}));
it('should redraw related links source when dragging node', fakeAsync(() => {
spyOn(nodesWidgetStub, 'redrawNode');
spyOn(linksWidgetStub, 'redrawLink');
const link = new MapLink();
link.source = node;
mockedGraphDataManager.setLinks([link]);
selectionManagerStub.setSelected([node]);
nodesWidgetStub.draggable.drag.emit(new DraggableDrag<MapNode>(node));
tick();
expect(linksWidgetStub.redrawLink).toHaveBeenCalledWith(select(fixture.componentInstance.svg), link);
}));
it('should emit event when node stopped dragging', fakeAsync(() => {
const nodesEventSourceStub = fixture.debugElement.injector.get(NodesEventSource);
const spyDragged = spyOn(nodesEventSourceStub.dragged, 'emit');
selectionManagerStub.setSelected([node]);
const dragEvent = new DraggableEnd<MapNode>(node);
dragEvent.dx = 10;
dragEvent.dy = 20;
nodesWidgetStub.draggable.end.emit(dragEvent);
tick();
expect(nodesEventSourceStub.dragged.emit).toHaveBeenCalled();
expect(spyDragged.calls.mostRecent().args[0].datum).toEqual(node);
expect(spyDragged.calls.mostRecent().args[0].dx).toEqual(10);
expect(spyDragged.calls.mostRecent().args[0].dy).toEqual(20);
}));
});
describe('drawings dragging', () => {
let drawingsWidgetStub: DrawingsWidget;
let selectionManagerStub: SelectionManager;
let drawing: MapDrawing;
beforeEach(() => {
drawingsWidgetStub = fixture.debugElement.injector.get(DrawingsWidget);
selectionManagerStub = fixture.debugElement.injector.get(SelectionManager);
drawing = new MapDrawing();
drawing.id = "drawingid";
drawing.x = 1;
drawing.y = 2;
});
it('should select drawing when started dragging', fakeAsync(() => {
drawingsWidgetStub.draggable.start.emit(new DraggableStart<MapDrawing>(drawing));
tick();
expect(selectionManagerStub.getSelected().length).toEqual(1);
}));
it('should ignore drawing when started dragging and node is in selection', fakeAsync(() => {
selectionManagerStub.setSelected([drawing]);
drawingsWidgetStub.draggable.start.emit(new DraggableStart<MapDrawing>(drawing));
tick();
expect(selectionManagerStub.getSelected().length).toEqual(1);
}));
it('should update drawing position when dragging', fakeAsync(() => {
spyOn(drawingsWidgetStub, 'redrawDrawing');
selectionManagerStub.setSelected([drawing]);
const dragEvent = new DraggableDrag<MapDrawing>(drawing);
dragEvent.dx = 10;
dragEvent.dy = 20;
drawingsWidgetStub.draggable.drag.emit(dragEvent);
tick();
expect(drawingsWidgetStub.redrawDrawing).toHaveBeenCalledWith(select(fixture.componentInstance.svg), drawing);
expect(drawing.x).toEqual(11);
expect(drawing.y).toEqual(22);
}));
it('should emit event when drawing stopped dragging', fakeAsync(() => {
const drawingsEventSourceStub = fixture.debugElement.injector.get(DrawingsEventSource);
const spyDragged = spyOn(drawingsEventSourceStub.dragged, 'emit');
selectionManagerStub.setSelected([drawing]);
const dragEvent = new DraggableEnd<MapDrawing>(drawing);
dragEvent.dx = 10;
dragEvent.dy = 20;
drawingsWidgetStub.draggable.end.emit(dragEvent);
tick();
expect(drawingsEventSourceStub.dragged.emit).toHaveBeenCalled();
expect(spyDragged.calls.mostRecent().args[0].datum).toEqual(drawing);
expect(spyDragged.calls.mostRecent().args[0].dx).toEqual(10);
expect(spyDragged.calls.mostRecent().args[0].dy).toEqual(20);
}));
});
describe('labels dragging', () => {
let labelWidgetStub: LabelWidget;
let selectionManagerStub: SelectionManager;
let label: MapLabel;
beforeEach(() => {
labelWidgetStub = fixture.debugElement.injector.get(LabelWidget);
selectionManagerStub = fixture.debugElement.injector.get(SelectionManager);
label = new MapLabel();
label.id = "labelid";
label.x = 1;
label.y = 2;
});
it('should select label when started dragging', fakeAsync(() => {
labelWidgetStub.draggable.start.emit(new DraggableStart<MapLabel>(label));
tick();
expect(selectionManagerStub.getSelected().length).toEqual(1);
}));
it('should ignore label when started dragging and node is in selection', fakeAsync(() => {
selectionManagerStub.setSelected([label]);
labelWidgetStub.draggable.start.emit(new DraggableStart<MapLabel>(label));
tick();
expect(selectionManagerStub.getSelected().length).toEqual(1);
}));
it('should update label position when dragging', fakeAsync(() => {
spyOn(labelWidgetStub, 'redrawLabel');
selectionManagerStub.setSelected([label]);
const node = new MapNode();
node.id = "nodeid";
node.label = label;
label.nodeId = node.id;
mockedGraphDataManager.setNodes([node]);
const dragEvent = new DraggableDrag<MapLabel>(label);
dragEvent.dx = 10;
dragEvent.dy = 20;
labelWidgetStub.draggable.drag.emit(dragEvent);
tick();
expect(labelWidgetStub.redrawLabel).toHaveBeenCalledWith(select(fixture.componentInstance.svg), label);
expect(label.x).toEqual(11);
expect(label.y).toEqual(22);
}));
it('should not update label position when dragging and parent is selected', fakeAsync(() => {
spyOn(labelWidgetStub, 'redrawLabel');
const node = new MapNode();
node.id = "nodeid";
node.label = label;
label.nodeId = node.id;
selectionManagerStub.setSelected([label, node]);
mockedGraphDataManager.setNodes([node]);
const dragEvent = new DraggableDrag<MapLabel>(label);
dragEvent.dx = 10;
dragEvent.dy = 20;
labelWidgetStub.draggable.drag.emit(dragEvent);
tick();
expect(labelWidgetStub.redrawLabel).not.toHaveBeenCalled();
expect(label.x).toEqual(1);
expect(label.y).toEqual(2);
}));
it('should emit event when label stopped dragging', fakeAsync(() => {
const nodesEventSourceStub = fixture.debugElement.injector.get(NodesEventSource);
const spyDragged = spyOn(nodesEventSourceStub.labelDragged, 'emit');
selectionManagerStub.setSelected([label]);
const dragEvent = new DraggableEnd<MapLabel>(label);
dragEvent.dx = 10;
dragEvent.dy = 20;
labelWidgetStub.draggable.end.emit(dragEvent);
tick();
expect(nodesEventSourceStub.labelDragged.emit).toHaveBeenCalled();
expect(spyDragged.calls.mostRecent().args[0].datum).toEqual(label);
expect(spyDragged.calls.mostRecent().args[0].dx).toEqual(10);
expect(spyDragged.calls.mostRecent().args[0].dy).toEqual(20);
}));
it('should not emit event when label stopped dragging and parent node is selected', fakeAsync(() => {
const nodesEventSourceStub = fixture.debugElement.injector.get(NodesEventSource);
spyOn(nodesEventSourceStub.labelDragged, 'emit');
const node = new MapNode();
node.id = "nodeid";
label.nodeId = node.id;
selectionManagerStub.setSelected([label, node]);
const dragEvent = new DraggableEnd<MapLabel>(label);
dragEvent.dx = 10;
dragEvent.dy = 20;
labelWidgetStub.draggable.end.emit(dragEvent);
tick();
expect(nodesEventSourceStub.labelDragged.emit).not.toHaveBeenCalled();
}));
});
describe('interfaces labels dragging', () => {
let linksWidgetStub: LinksWidget;
let interfaceLabelWidgetStub: InterfaceLabelWidget;
let selectionManagerStub: SelectionManager;
let linkNode: MapLinkNode;
beforeEach(() => {
interfaceLabelWidgetStub = fixture.debugElement.injector.get(InterfaceLabelWidget);
linksWidgetStub = fixture.debugElement.injector.get(LinksWidget);
selectionManagerStub = fixture.debugElement.injector.get(SelectionManager);
linkNode = new MapLinkNode();
linkNode.label = new MapLabel();
linkNode.label.x = 1;
linkNode.label.y = 2;
linkNode.id = "linknodeid";
});
it('should select interface label when started dragging', fakeAsync(() => {
interfaceLabelWidgetStub.draggable.start.emit(new DraggableStart<MapLinkNode>(linkNode));
tick();
expect(selectionManagerStub.getSelected().length).toEqual(1);
}));
it('should ignore interface label when started dragging and node is in selection', fakeAsync(() => {
selectionManagerStub.setSelected([linkNode]);
interfaceLabelWidgetStub.draggable.start.emit(new DraggableStart<MapLinkNode>(linkNode));
tick();
expect(selectionManagerStub.getSelected().length).toEqual(1);
}));
it('should update interface label position when dragging first node', fakeAsync(() => {
spyOn(linksWidgetStub, 'redrawLink');
selectionManagerStub.setSelected([linkNode]);
const node = new MapNode();
node.id = "nodeid";
linkNode.nodeId = node.id;
const secondLinkNode = new MapLinkNode();
secondLinkNode.label = new MapLabel();
secondLinkNode.label.x = 1;
secondLinkNode.label.y = 2;
secondLinkNode.id = "secondlinknodeid";
const link = new MapLink();
link.nodes = [linkNode, secondLinkNode];
mockedGraphDataManager.setLinks([link]);
const dragEvent = new DraggableDrag<MapLinkNode>(linkNode);
dragEvent.dx = 10;
dragEvent.dy = 20;
interfaceLabelWidgetStub.draggable.drag.emit(dragEvent);
tick();
expect(linksWidgetStub.redrawLink).toHaveBeenCalledWith(select(fixture.componentInstance.svg), link);
expect(linkNode.label.x).toEqual(11);
expect(linkNode.label.y).toEqual(22);
}));
it('should update interface label position when dragging second node', fakeAsync(() => {
spyOn(linksWidgetStub, 'redrawLink');
selectionManagerStub.setSelected([linkNode]);
const node = new MapNode();
node.id = "nodeid";
linkNode.nodeId = node.id;
const secondLinkNode = new MapLinkNode();
secondLinkNode.label = new MapLabel();
secondLinkNode.label.x = 1;
secondLinkNode.label.y = 2;
secondLinkNode.id = "secondlinknodeid";
const link = new MapLink();
link.nodes = [secondLinkNode, linkNode];
mockedGraphDataManager.setLinks([link]);
const dragEvent = new DraggableDrag<MapLinkNode>(linkNode);
dragEvent.dx = 10;
dragEvent.dy = 20;
interfaceLabelWidgetStub.draggable.drag.emit(dragEvent);
tick();
expect(linksWidgetStub.redrawLink).toHaveBeenCalledWith(select(fixture.componentInstance.svg), link);
expect(linkNode.label.x).toEqual(11);
expect(linkNode.label.y).toEqual(22);
}));
it('should not update interface label position when dragging and parent node is selected', fakeAsync(() => {
spyOn(linksWidgetStub, 'redrawLink');
const node = new MapNode();
node.id = "nodeid";
linkNode.nodeId = node.id;
selectionManagerStub.setSelected([linkNode, node]);
const secondLinkNode = new MapLinkNode();
secondLinkNode.label = new MapLabel();
secondLinkNode.label.x = 1;
secondLinkNode.label.y = 2;
secondLinkNode.id = "secondlinknodeid";
const link = new MapLink();
link.nodes = [linkNode, secondLinkNode];
mockedGraphDataManager.setLinks([link]);
const dragEvent = new DraggableDrag<MapLinkNode>(linkNode);
dragEvent.dx = 10;
dragEvent.dy = 20;
interfaceLabelWidgetStub.draggable.drag.emit(dragEvent);
tick();
expect(linksWidgetStub.redrawLink).not.toHaveBeenCalled();
expect(linkNode.label.x).toEqual(1);
expect(linkNode.label.y).toEqual(2);
}));
it('should emit event when interface label stopped dragging', fakeAsync(() => {
const linksEventSourceStub = fixture.debugElement.injector.get(LinksEventSource);
const spyDragged = spyOn(linksEventSourceStub.interfaceDragged, 'emit');
selectionManagerStub.setSelected([linkNode]);
const dragEvent = new DraggableEnd<MapLinkNode>(linkNode);
dragEvent.dx = 10;
dragEvent.dy = 20;
interfaceLabelWidgetStub.draggable.end.emit(dragEvent);
tick();
expect(linksEventSourceStub.interfaceDragged.emit).toHaveBeenCalled();
expect(spyDragged.calls.mostRecent().args[0].datum).toEqual(linkNode);
expect(spyDragged.calls.mostRecent().args[0].dx).toEqual(10);
expect(spyDragged.calls.mostRecent().args[0].dy).toEqual(20);
}));
it('should not emit event when interface label stopped dragging and parent node is selected', fakeAsync(() => {
const linksEventSourceStub = fixture.debugElement.injector.get(LinksEventSource);
spyOn(linksEventSourceStub.interfaceDragged, 'emit');
const node = new MapNode();
node.id = "nodeid";
linkNode.nodeId = node.id;
selectionManagerStub.setSelected([linkNode, node]);
const dragEvent = new DraggableEnd<MapLinkNode>(linkNode);
dragEvent.dx = 10;
dragEvent.dy = 20;
interfaceLabelWidgetStub.draggable.end.emit(dragEvent);
tick();
expect(linksEventSourceStub.interfaceDragged.emit).not.toHaveBeenCalled();
}));
});
});

View File

@ -94,7 +94,8 @@ export class DraggableSelectionComponent implements OnInit, OnDestroy {
this.nodesWidget.redrawNode(svg, node);
const links = this.graphDataManager.getLinks().filter(
(link) => link.target.id === node.id || link.source.id === node.id);
(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);
});

View File

@ -1 +0,0 @@
<!-- <app-node-select-interface (onChooseInterface)="onChooseInterface($event)"></app-node-select-interface> -->

View File

@ -1,61 +0,0 @@
import { Component, OnInit, Output, EventEmitter, OnDestroy, ViewChild } from '@angular/core';
import { DrawingLineWidget } from '../../widgets/drawing-line';
import { Subscription } from 'rxjs';
// import { NodeSelectInterfaceComponent } from '../node-select-interface/node-select-interface.component';
import { MapLinkCreated } from '../../events/links';
import { NodeClicked } from '../../events/nodes';
import { NodeWidget } from '../../widgets/node';
import { MapNode } from '../../models/map/map-node';
import { MapPort } from '../../models/map/map-port';
import { LinksEventSource } from '../../events/links-event-source';
@Component({
selector: 'app-draw-link-tool',
templateUrl: './draw-link-tool.component.html',
styleUrls: ['./draw-link-tool.component.scss']
})
export class DrawLinkToolComponent implements OnInit, OnDestroy {
// @ViewChild(NodeSelectInterfaceComponent) nodeSelectInterfaceMenu: NodeSelectInterfaceComponent;
// @Output('linkCreated') linkCreated = new EventEmitter<MapLinkCreated>();
private onNodeClicked: Subscription;
constructor(
private drawingLineTool: DrawingLineWidget,
private nodeWidget: NodeWidget,
private linksEventSource: LinksEventSource
) { }
ngOnInit() {
// this.onNodeClicked = this.nodeWidget.onNodeClicked.subscribe((eventNode: NodeClicked) => {
// this.nodeSelectInterfaceMenu.open(
// eventNode.node,
// eventNode.event.clientY,
// eventNode.event.clientX
// );
// });
}
ngOnDestroy() {
if (this.drawingLineTool.isDrawing()) {
this.drawingLineTool.stop();
}
// this.onNodeClicked.unsubscribe();
}
public onChooseInterface(event) {
const node: MapNode = event.node;
const port: MapPort = event.port;
if (this.drawingLineTool.isDrawing()) {
const data = this.drawingLineTool.stop();
this.linksEventSource.created.emit(new MapLinkCreated(data['node'], data['port'], node, port));
} else {
this.drawingLineTool.start(node.x + node.width / 2., node.y + node.height / 2., {
'node': node,
'port': port
});
}
}
}

View File

@ -1,6 +1,6 @@
import { Component, OnInit, ElementRef, AfterViewInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core';
import { Observable, Subscription } from 'rxjs';
import { Point } from '../../models/point';
import { Point } from '../../../models/point';
export class DraggableDraggedEvent {

View File

@ -1,6 +1,6 @@
import { Component, OnInit, Input, AfterViewInit, ChangeDetectorRef, Output, EventEmitter } from '@angular/core';
import { Observable, Subscription, Subject } from 'rxjs';
import { Rectangle } from '../../models/rectangle';
import { Rectangle } from '../../../models/rectangle';
@Component({
selector: '[app-selection]',

View File

@ -7,18 +7,18 @@ import { MapDrawing } from "../../models/map/map-drawing";
@Injectable()
export class DrawingToMapDrawingConverter implements Converter<Drawing, MapDrawing> {
constructor(
) {}
convert(drawing: Drawing) {
const mapDrawing = new MapDrawing();
mapDrawing.id = drawing.drawing_id;
mapDrawing.projectId = drawing.project_id;
mapDrawing.rotation = drawing.rotation;
mapDrawing.svg = drawing.svg;
mapDrawing.x = drawing.x;
mapDrawing.y = drawing.y;
mapDrawing.z = drawing.z;
return mapDrawing;
}
constructor(
) {}
convert(drawing: Drawing) {
const mapDrawing = new MapDrawing();
mapDrawing.id = drawing.drawing_id;
mapDrawing.projectId = drawing.project_id;
mapDrawing.rotation = drawing.rotation;
mapDrawing.svg = drawing.svg;
mapDrawing.x = drawing.x;
mapDrawing.y = drawing.y;
mapDrawing.z = drawing.z;
return mapDrawing;
}
}

View File

@ -7,17 +7,17 @@ import { MapLabel } from "../../models/map/map-label";
@Injectable()
export class LabelToMapLabelConverter implements Converter<Label, MapLabel> {
convert(label: Label, paramaters?: {[node_id: string]: string}) {
const mapLabel = new MapLabel();
mapLabel.rotation = label.rotation;
mapLabel.style = label.style;
mapLabel.text = label.text;
mapLabel.x = label.x;
mapLabel.y = label.y;
if (paramaters !== undefined) {
mapLabel.id = paramaters.node_id;
mapLabel.nodeId = paramaters.node_id;
}
return mapLabel;
convert(label: Label, paramaters?: {[node_id: string]: string}) {
const mapLabel = new MapLabel();
mapLabel.rotation = label.rotation;
mapLabel.style = label.style;
mapLabel.text = label.text;
mapLabel.x = label.x;
mapLabel.y = label.y;
if (paramaters !== undefined) {
mapLabel.id = paramaters.node_id;
mapLabel.nodeId = paramaters.node_id;
}
return mapLabel;
}
}

View File

@ -17,3 +17,11 @@ export class ResizedDataEvent<T> {
public height: number) {
}
}
export class ClickedDataEvent<T> {
constructor(
public datum: T,
public x: number,
public y: number
) {}
}

View File

@ -1,5 +1,5 @@
import { Injectable, EventEmitter } from "@angular/core";
import { DraggedDataEvent } from "./event-source";
import { DraggedDataEvent, ClickedDataEvent } from "./event-source";
import { MapNode } from "../models/map/map-node";
import { MapLabel } from "../models/map/map-label";
@ -8,4 +8,5 @@ import { MapLabel } from "../models/map/map-label";
export class NodesEventSource {
public dragged = new EventEmitter<DraggedDataEvent<MapNode>>();
public labelDragged = new EventEmitter<DraggedDataEvent<MapLabel>>();
public clicked = new EventEmitter<ClickedDataEvent<MapNode>>();
}

View File

@ -1,6 +1,15 @@
import { MapNode } from "../models/map/map-node";
import { SelectionManager } from "./selection-manager";
export class SelectionManagerMock {
public items = [];
setSelected(items: any) {
this.items = items;
}
getSelected() {
return this.items;
}
}
describe('SelectionManager', () => {
let manager: SelectionManager;

View File

@ -25,5 +25,4 @@ export class MapNode implements Indexed {
x: number;
y: number;
z: number;
isSelected = false;
}

View File

@ -9,6 +9,8 @@ import { MapNode } from "../models/map/map-node";
import { GraphDataManager } from "../managers/graph-data-manager";
import { SelectionManager } from "../managers/selection-manager";
import { LabelWidget } from "./label";
import { NodesEventSource } from '../events/nodes-event-source';
import { ClickedDataEvent } from '../events/event-source';
@Injectable()
@ -19,7 +21,8 @@ export class NodeWidget implements Widget {
constructor(
private graphDataManager: GraphDataManager,
private selectionManager: SelectionManager,
private labelWidget: LabelWidget
private labelWidget: LabelWidget,
private nodesEventSource: NodesEventSource,
) {}
public draw(view: SVGSelection) {
@ -42,8 +45,9 @@ export class NodeWidget implements Widget {
event.preventDefault();
self.onContextMenu.emit(new NodeContextMenu(event, n));
})
.on('click', (n: MapNode) => {
this.onNodeClicked.emit(new NodeClicked(event, n));
.on('click', (node: MapNode) => {
this.nodesEventSource.clicked.emit(new ClickedDataEvent<MapNode>(node, event.clientX, event.clientY))
// this.onNodeClicked.emit(new NodeClicked(event, n));
});
// update image of node

View File

@ -0,0 +1 @@
<app-node-select-interface (onChooseInterface)="onChooseInterface($event)"></app-node-select-interface>

View File

@ -0,0 +1,58 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Subscription } from 'rxjs';
import { NodeSelectInterfaceComponent } from '../../../components/project-map/node-select-interface/node-select-interface.component';
import { DrawingLineWidget } from '../../../cartography/widgets/drawing-line';
import { NodesEventSource } from '../../../cartography/events/nodes-event-source';
import { LinksEventSource } from '../../../cartography/events/links-event-source';
import { MapNode } from '../../../cartography/models/map/map-node';
import { MapPort } from '../../../cartography/models/map/map-port';
import { MapLinkCreated } from '../../../cartography/events/links';
@Component({
selector: 'app-draw-link-tool',
templateUrl: './draw-link-tool.component.html',
styleUrls: ['./draw-link-tool.component.scss']
})
export class DrawLinkToolComponent implements OnInit, OnDestroy {
@ViewChild(NodeSelectInterfaceComponent) nodeSelectInterfaceMenu: NodeSelectInterfaceComponent;
private nodeClicked$: Subscription;
constructor(
private drawingLineTool: DrawingLineWidget,
private nodesEventSource: NodesEventSource,
private linksEventSource: LinksEventSource
) { }
ngOnInit() {
this.nodeClicked$ = this.nodesEventSource.clicked.subscribe((clickedEvent) => {
this.nodeSelectInterfaceMenu.open(
clickedEvent.datum,
clickedEvent.y,
clickedEvent.x
);
});
}
ngOnDestroy() {
if (this.drawingLineTool.isDrawing()) {
this.drawingLineTool.stop();
}
this.nodeClicked$.unsubscribe();
}
public onChooseInterface(event) {
const node: MapNode = event.node;
const port: MapPort = event.port;
if (this.drawingLineTool.isDrawing()) {
const data = this.drawingLineTool.stop();
this.linksEventSource.created.emit(new MapLinkCreated(data['node'], data['port'], node, port));
} else {
this.drawingLineTool.start(node.x + node.width / 2., node.y + node.height / 2., {
'node': node,
'port': port
});
}
}
}

View File

@ -18,9 +18,9 @@ export class NodeContextMenuComponent implements OnInit {
@ViewChild(MatMenuTrigger) contextMenu: MatMenuTrigger;
protected topPosition;
protected leftPosition;
public node: Node;
topPosition;
leftPosition;
node: Node;
constructor(
private sanitizer: DomSanitizer,

View File

@ -1,6 +1,6 @@
<div class="context-menu" [style.left]="leftPosition" [style.top]="topPosition" *ngIf="node">
<span [matMenuTriggerFor]="selectInterfaceMenu"></span>
<mat-menu #selectInterfaceMenu="matMenu">
<mat-menu #selectInterfaceMenu="matMenu" class="context-menu-items">
<button mat-menu-item *ngFor="let port of node.ports" (click)="chooseInterface(port)">
<mat-icon>add_circle_outline</mat-icon>
<span>{{ port.name }}</span>

View File

@ -15,8 +15,8 @@ export class NodeSelectInterfaceComponent implements OnInit {
@ViewChild(MatMenuTrigger) contextMenu: MatMenuTrigger;
private topPosition;
private leftPosition;
protected topPosition;
protected leftPosition;
public node: Node;
constructor(

View File

@ -140,3 +140,5 @@
[project]="project"
[server]="server">
</app-project-map-shortcuts>
<app-draw-link-tool *ngIf="tools.draw_link"></app-draw-link-tool>

View File

@ -64,7 +64,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
private drawListener: Function;
private ws: Subject<any>;
protected tools = {
tools = {
'selection': true,
'moving': false,
'draw_link': false

View File

@ -1,10 +1,18 @@
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false,
electron: false,
githubio: false
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

View File

@ -8,4 +8,5 @@ if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

View File

@ -11,7 +11,7 @@
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
@ -34,29 +34,47 @@
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';
/**
* If the application will be indexed by Google Search, the following is required.
* Googlebot uses a renderer based on Chrome 41.
* https://developers.google.com/search/docs/guides/rendering
**/
// import 'core-js/es6/array';
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/** Evergreen browsers require these. **/
import 'core-js/es6/reflect';
import 'core-js/es7/reflect';
/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';
/**
* Required to support Web Animations `@angular/animation`.
* Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
**/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
*/
// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
// (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
/*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*/
// (window as any).__Zone_enable_cross_context_check = true;
/***************************************************************************************************
* Zone JS is required by Angular itself.
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/

View File

@ -1,24 +1,14 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/long-stack-trace-zone';
import 'zone.js/dist/proxy.js';
import 'zone.js/dist/sync-test';
import 'zone.js/dist/jasmine-patch';
import 'zone.js/dist/async-test';
import 'zone.js/dist/fake-async-test';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
declare const __karma__: any;
declare const require: any;
// Prevent Karma from running prematurely.
__karma__.loaded = function () {};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
@ -28,5 +18,3 @@ getTestBed().initTestEnvironment(
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
// Finally, start Karma to run the tests.
__karma__.start();

View File

@ -1,18 +1,21 @@
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"module": "es2015",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "es5",
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2016",
"es2018",
"dom"
],
},

View File

@ -11,10 +11,14 @@
"check-space"
],
"curly": true,
"deprecation": {
"severity": "warn"
},
"eofline": true,
"forin": true,
"import-blacklist": [
true
true,
"rxjs/Rx"
],
"import-spacing": true,
"indent": [
@ -61,11 +65,12 @@
],
"no-misused-new": true,
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-shadowed-variable": true,
"no-string-literal": false,
"no-string-throw": true,
"no-switch-case-fall-through": true,
"no-trailing-whitespace": false,
"no-trailing-whitespace": true,
"no-unnecessary-initializer": true,
"no-unused-expression": true,
"no-use-before-declare": true,
@ -80,7 +85,7 @@
],
"prefer-const": true,
"quotemark": [
false,
true,
"single"
],
"radix": true,
@ -102,7 +107,6 @@
"variable-declaration": "nospace"
}
],
"typeof-compare": true,
"unified-signatures": true,
"variable-name": false,
"whitespace": [
@ -113,18 +117,7 @@
"check-separator",
"check-type"
],
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
],
"no-output-on-prefix": true,
"use-input-property-decorator": true,
"use-output-property-decorator": true,
"use-host-property-decorator": true,
@ -133,9 +126,6 @@
"use-life-cycle-interface": true,
"use-pipe-transform-interface": true,
"component-class-suffix": true,
"directive-class-suffix": true,
"no-access-missing-member": true,
"templates-use-public": true,
"invoke-injectable": true
"directive-class-suffix": true
}
}