mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2024-12-23 23:02:22 +00:00
Merge branch 'master' into Extend-context-menu-with-option-to-duplicate
This commit is contained in:
commit
d9341fa60a
@ -188,6 +188,8 @@ import { NodeCreatedLabelStylesFixer } from './components/project-map/helpers/no
|
||||
import { NonNegativeValidator } from './validators/non-negative-validator';
|
||||
import { RotationValidator } from './validators/rotation-validator';
|
||||
import { DuplicateActionComponent } from './components/project-map/context-menu/actions/duplicate-action/duplicate-action.component';
|
||||
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';
|
||||
|
||||
if (environment.production) {
|
||||
@ -309,6 +311,7 @@ if (environment.production) {
|
||||
ConsoleDeviceActionComponent,
|
||||
ConsoleComponent,
|
||||
NodesMenuComponent,
|
||||
ProjectMapMenuComponent,
|
||||
HelpComponent
|
||||
],
|
||||
imports: [
|
||||
@ -384,7 +387,8 @@ if (environment.production) {
|
||||
DefaultConsoleService,
|
||||
NodeCreatedLabelStylesFixer,
|
||||
NonNegativeValidator,
|
||||
RotationValidator
|
||||
RotationValidator,
|
||||
MapSettingService
|
||||
],
|
||||
entryComponents: [
|
||||
AddServerDialogComponent,
|
||||
|
@ -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();
|
||||
|
@ -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<any>) => {
|
||||
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<any>) => {
|
||||
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<MapNode>(item, evt.dx, evt.dy));
|
||||
});
|
||||
|
||||
selected
|
||||
.filter(item => item instanceof MapDrawing)
|
||||
.forEach((item: MapDrawing) => {
|
||||
this.drawingsEventSource.dragged.emit(new DraggedDataEvent<MapDrawing>(item, evt.dx, evt.dy));
|
||||
selectedNodes.forEach((item: MapNode) => {
|
||||
this.nodesEventSource.dragged.emit(new DraggedDataEvent<MapNode>(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<MapDrawing>(item, evt.dx, evt.dy));
|
||||
});
|
||||
|
||||
this.nodesEventSource.labelDragged.emit(new DraggedDataEvent<MapLabel>(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<MapLinkNode>(label, evt.dx, evt.dy));
|
||||
});
|
||||
this.nodesEventSource.labelDragged.emit(new DraggedDataEvent<MapLabel>(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<MapLinkNode>(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();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,65 @@
|
||||
<button
|
||||
matTooltip="Add a note"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
[color]="drawTools.isTextChosen ? 'primary' : 'basic'"
|
||||
(click)="addDrawing('text')">
|
||||
<mat-icon>create</mat-icon>
|
||||
</button>
|
||||
<input
|
||||
type="file"
|
||||
accept=".svg, .bmp, .jpeg, .jpg, .gif, .png"
|
||||
class="non-visible"
|
||||
#file
|
||||
(change)="uploadImageFile($event)"/>
|
||||
<button
|
||||
matTooltip="Insert a picture"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
(click)="file.click()">
|
||||
<mat-icon>image</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Draw a rectangle"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
[color]="drawTools.isRectangleChosen ? 'primary' : 'basic'"
|
||||
(click)="addDrawing('rectangle')">
|
||||
<mat-icon>crop_3_2</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Draw an ellipse"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
[color]="drawTools.isEllipseChosen ? 'primary' : 'basic'"
|
||||
(click)="addDrawing('ellipse')">
|
||||
<mat-icon>panorama_fish_eye</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Draw a line"
|
||||
mat-icon-button class="menu-button"
|
||||
(click)="addDrawing('line')">
|
||||
<svg height="40" width="40">
|
||||
<line
|
||||
[ngClass]="{ selected: drawTools.isLineChosen }"
|
||||
x1="30"
|
||||
y1="10"
|
||||
x2="10"
|
||||
y2="30"
|
||||
style="stroke:white;stroke-width:2"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Lock or unlock all items"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
[color]="isLocked ? 'primary' : 'basic'"
|
||||
(click)="changeLockValue()">
|
||||
<mat-icon>lock</mat-icon>
|
||||
</button>
|
||||
<app-drawing-added
|
||||
[server]="server"
|
||||
[project]="project"
|
||||
[selectedDrawing]="selectedDrawing"
|
||||
(drawingSaved)="onDrawingSaved()">
|
||||
</app-drawing-added>
|
@ -0,0 +1,24 @@
|
||||
.menu-button {
|
||||
outline: 0 !important;
|
||||
transition: 0.5s;
|
||||
margin-bottom: 16px;
|
||||
width: 40px;
|
||||
margin-right: 12px !important;
|
||||
margin-left: 12px !important;
|
||||
background: #263238;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
mat-divider.divider {
|
||||
height: 40px;
|
||||
margin-left: 1px;
|
||||
margin-right: 7px;
|
||||
width: 10px;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.non-visible {
|
||||
display: none;
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
import { ProjectMapMenuComponent } from "./project-map-menu.component";
|
||||
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
||||
import { MockedDrawingService } from '../project-map.component.spec';
|
||||
import { MapSettingService } from '../../../services/mapsettings.service';
|
||||
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule } from '@angular/material';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { DrawingService } from '../../../services/drawing.service';
|
||||
import { ToolsService } from '../../../services/tools.service';
|
||||
import { D3MapComponent } from '../../../cartography/components/d3-map/d3-map.component';
|
||||
import { ANGULAR_MAP_DECLARATIONS } from '../../../cartography/angular-map.imports';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
|
||||
describe('ProjectMapMenuComponent', () => {
|
||||
let component: ProjectMapMenuComponent;
|
||||
let fixture: ComponentFixture<ProjectMapMenuComponent>;
|
||||
let drawingService = new MockedDrawingService();
|
||||
let mapSettingService = new MapSettingService();
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule],
|
||||
providers: [
|
||||
{ provide: DrawingService, useValue: drawingService },
|
||||
{ provide: ToolsService },
|
||||
{ provide: MapSettingService, useValue: mapSettingService }
|
||||
],
|
||||
declarations: [ProjectMapMenuComponent, D3MapComponent, ...ANGULAR_MAP_DECLARATIONS],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ProjectMapMenuComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should reset choice on draw menu after saving drawing', () => {
|
||||
spyOn(component, 'resetDrawToolChoice');
|
||||
|
||||
component.onDrawingSaved();
|
||||
|
||||
expect(component.resetDrawToolChoice).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call map settings service when lock value was changed', () => {
|
||||
spyOn(mapSettingService, 'changeMapLockValue');
|
||||
|
||||
component.changeLockValue();
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
@ -0,0 +1,110 @@
|
||||
import { Component, OnInit, OnDestroy, Input } from '@angular/core';
|
||||
import { Project } from '../../../models/project';
|
||||
import { Server } from '../../../models/server';
|
||||
import { ToolsService } from '../../../services/tools.service';
|
||||
import { MapSettingService } from '../../../services/mapsettings.service';
|
||||
import { DrawingService } from '../../../services/drawing.service';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-project-map-menu',
|
||||
templateUrl: './project-map-menu.component.html',
|
||||
styleUrls: ['./project-map-menu.component.scss']
|
||||
})
|
||||
export class ProjectMapMenuComponent implements OnInit, OnDestroy {
|
||||
@Input() project: Project;
|
||||
@Input() server: Server;
|
||||
|
||||
public selectedDrawing: string;
|
||||
public drawTools = {
|
||||
isRectangleChosen: false,
|
||||
isEllipseChosen: false,
|
||||
isLineChosen: false,
|
||||
isTextChosen: false
|
||||
};
|
||||
public isLocked: boolean = false;
|
||||
|
||||
constructor(
|
||||
private toolsService: ToolsService,
|
||||
private mapSettingsService: MapSettingService,
|
||||
private drawingService: DrawingService
|
||||
) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
public addDrawing(selectedObject: string) {
|
||||
switch (selectedObject) {
|
||||
case 'rectangle':
|
||||
this.drawTools.isTextChosen = false;
|
||||
this.drawTools.isEllipseChosen = false;
|
||||
this.drawTools.isRectangleChosen = !this.drawTools.isRectangleChosen;
|
||||
this.drawTools.isLineChosen = false;
|
||||
break;
|
||||
case 'ellipse':
|
||||
this.drawTools.isTextChosen = false;
|
||||
this.drawTools.isEllipseChosen = !this.drawTools.isEllipseChosen;
|
||||
this.drawTools.isRectangleChosen = false;
|
||||
this.drawTools.isLineChosen = false;
|
||||
break;
|
||||
case 'line':
|
||||
this.drawTools.isTextChosen = false;
|
||||
this.drawTools.isEllipseChosen = false;
|
||||
this.drawTools.isRectangleChosen = false;
|
||||
this.drawTools.isLineChosen = !this.drawTools.isLineChosen;
|
||||
break;
|
||||
case 'text':
|
||||
this.drawTools.isTextChosen = !this.drawTools.isTextChosen;
|
||||
this.drawTools.isEllipseChosen = false;
|
||||
this.drawTools.isRectangleChosen = false;
|
||||
this.drawTools.isLineChosen = false;
|
||||
this.toolsService.textAddingToolActivation(this.drawTools.isTextChosen);
|
||||
break;
|
||||
}
|
||||
|
||||
this.selectedDrawing = this.selectedDrawing === selectedObject ? '' : selectedObject;
|
||||
}
|
||||
|
||||
public onDrawingSaved() {
|
||||
this.resetDrawToolChoice();
|
||||
}
|
||||
|
||||
public resetDrawToolChoice() {
|
||||
this.drawTools.isRectangleChosen = false;
|
||||
this.drawTools.isEllipseChosen = false;
|
||||
this.drawTools.isLineChosen = false;
|
||||
this.drawTools.isTextChosen = false;
|
||||
this.selectedDrawing = '';
|
||||
this.toolsService.textAddingToolActivation(this.drawTools.isTextChosen);
|
||||
}
|
||||
|
||||
public changeLockValue() {
|
||||
this.isLocked = !this.isLocked;
|
||||
this.mapSettingsService.changeMapLockValue(this.isLocked);
|
||||
}
|
||||
|
||||
public uploadImageFile(event) {
|
||||
this.readImageFile(event.target);
|
||||
}
|
||||
|
||||
private readImageFile(fileInput) {
|
||||
let file: File = fileInput.files[0];
|
||||
let fileReader: FileReader = new FileReader();
|
||||
let imageToUpload = new Image();
|
||||
|
||||
fileReader.onloadend = () => {
|
||||
let image = fileReader.result;
|
||||
let svg = this.createSvgFileForImage(image, imageToUpload);
|
||||
this.drawingService.add(this.server, this.project.project_id, -(imageToUpload.width/2), -(imageToUpload.height/2), svg).subscribe(() => {});
|
||||
}
|
||||
|
||||
imageToUpload.onload = () => { fileReader.readAsDataURL(file) };
|
||||
imageToUpload.src = window.URL.createObjectURL(file);
|
||||
}
|
||||
|
||||
private createSvgFileForImage(image: string|ArrayBuffer, imageToUpload: HTMLImageElement) {
|
||||
return `<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" height=\"${imageToUpload.height}\"
|
||||
width=\"${imageToUpload.width}\">\n<image height=\"${imageToUpload.height}\" width=\"${imageToUpload.width}\" xlink:href=\"${image}\"/>\n</svg>`
|
||||
}
|
||||
|
||||
ngOnDestroy() {}
|
||||
}
|
@ -87,67 +87,14 @@
|
||||
</mat-toolbar>
|
||||
</div>
|
||||
|
||||
<div id="show-menu-wrapper" [ngClass]="{ shadowed: !drawTools.visibility }" *ngIf="!readonly">
|
||||
<div id="show-menu-wrapper" [ngClass]="{ shadowed: !isProjectMapMenuVisible }" *ngIf="!readonly">
|
||||
<button class="arrow-button" mat-icon-button (click)="showMenu()"><mat-icon>keyboard_arrow_right</mat-icon></button>
|
||||
</div>
|
||||
|
||||
<div id="menu-wrapper" [ngClass]="{ extended: drawTools.visibility }">
|
||||
<div id="menu-wrapper" [ngClass]="{ extended: isProjectMapMenuVisible }">
|
||||
<app-nodes-menu [server]="server" [project]="project"></app-nodes-menu>
|
||||
<mat-divider class="divider" [vertical]="true"></mat-divider>
|
||||
<button
|
||||
matTooltip="Add a note"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
[color]="drawTools.isTextChosen ? 'primary' : 'basic'"
|
||||
(click)="addDrawing('text')"
|
||||
>
|
||||
<mat-icon>create</mat-icon>
|
||||
</button>
|
||||
<input
|
||||
type="file"
|
||||
accept=".svg, .bmp, .jpeg, .jpg, .gif, .png"
|
||||
class="non-visible"
|
||||
#file
|
||||
(change)="uploadImageFile($event)"
|
||||
/>
|
||||
<button
|
||||
matTooltip="Insert a picture"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
(click)="file.click()"
|
||||
>
|
||||
<mat-icon>image</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Draw a rectangle"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
[color]="drawTools.isRectangleChosen ? 'primary' : 'basic'"
|
||||
(click)="addDrawing('rectangle')"
|
||||
>
|
||||
<mat-icon>crop_3_2</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Draw an ellipse"
|
||||
mat-icon-button
|
||||
class="menu-button"
|
||||
[color]="drawTools.isEllipseChosen ? 'primary' : 'basic'"
|
||||
(click)="addDrawing('ellipse')"
|
||||
>
|
||||
<mat-icon>panorama_fish_eye</mat-icon>
|
||||
</button>
|
||||
<button matTooltip="Draw a line" mat-icon-button class="menu-button" (click)="addDrawing('line')">
|
||||
<svg height="40" width="40">
|
||||
<line
|
||||
[ngClass]="{ selected: drawTools.isLineChosen }"
|
||||
x1="30"
|
||||
y1="10"
|
||||
x2="10"
|
||||
y2="30"
|
||||
style="stroke:white;stroke-width:2"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<app-project-map-menu [server]="server" [project]="project"></app-project-map-menu>
|
||||
<button class="arrow-button" mat-icon-button (click)="hideMenu()"><mat-icon>keyboard_arrow_left</mat-icon></button>
|
||||
</div>
|
||||
|
||||
@ -161,18 +108,9 @@
|
||||
</div>
|
||||
|
||||
<app-progress></app-progress>
|
||||
|
||||
<app-project-map-shortcuts *ngIf="project" [project]="project" [server]="server"> </app-project-map-shortcuts>
|
||||
|
||||
<app-project-map-shortcuts *ngIf="project" [project]="project" [server]="server"></app-project-map-shortcuts>
|
||||
<app-draw-link-tool [links]="links" *ngIf="tools.draw_link"></app-draw-link-tool>
|
||||
|
||||
<app-drawing-added
|
||||
[server]="server"
|
||||
[project]="project"
|
||||
[selectedDrawing]="selectedDrawing"
|
||||
(drawingSaved)="onDrawingSaved()"
|
||||
>
|
||||
</app-drawing-added>
|
||||
<app-drawing-dragged [server]="server"></app-drawing-dragged>
|
||||
<app-drawing-resized [server]="server"></app-drawing-resized>
|
||||
<app-interface-label-dragged [server]="server"></app-interface-label-dragged>
|
||||
|
@ -80,7 +80,7 @@ g.node:hover {
|
||||
}
|
||||
|
||||
.extended {
|
||||
width: 640px !important;
|
||||
width: 720px !important;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
@ -45,6 +45,8 @@ import { CapturingSettings } from '../../models/capturingSettings';
|
||||
import { LinkWidget } from '../../cartography/widgets/link';
|
||||
import { MapScaleService } from '../../services/mapScale.service';
|
||||
import { NodeCreatedLabelStylesFixer } from './helpers/node-created-label-styles-fixer';
|
||||
import { MapSettingService } from '../../services/mapsettings.service';
|
||||
import { ProjectMapMenuComponent } from './project-map-menu/project-map-menu.component';
|
||||
|
||||
export class MockedProgressService {
|
||||
public activate() {}
|
||||
@ -231,10 +233,11 @@ describe('ProjectMapComponent', () => {
|
||||
provide: RecentlyOpenedProjectService,
|
||||
useClass: RecentlyOpenedProjectService
|
||||
},
|
||||
{ provide: NodeCreatedLabelStylesFixer, useValue: nodeCreatedLabelStylesFixer},
|
||||
{ provide: MapScaleService },
|
||||
{ provide: NodeCreatedLabelStylesFixer, useValue: nodeCreatedLabelStylesFixer}
|
||||
],
|
||||
declarations: [ProjectMapComponent, D3MapComponent, ...ANGULAR_MAP_DECLARATIONS],
|
||||
declarations: [ProjectMapComponent, ProjectMapMenuComponent, D3MapComponent, ...ANGULAR_MAP_DECLARATIONS],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
@ -242,6 +245,9 @@ describe('ProjectMapComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ProjectMapComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.projectMapMenuComponent = {
|
||||
resetDrawToolChoice(){}
|
||||
} as ProjectMapMenuComponent;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@ -255,20 +261,12 @@ describe('ProjectMapComponent', () => {
|
||||
it('should hide draw tools when hide menu is called', () => {
|
||||
var dummyElement = document.createElement('map');
|
||||
document.getElementsByClassName = jasmine.createSpy('HTML element').and.callFake(() => {
|
||||
return [dummyElement];
|
||||
return [dummyElement];
|
||||
});
|
||||
spyOn(component, 'resetDrawToolChoice');
|
||||
spyOn(component.projectMapMenuComponent, 'resetDrawToolChoice').and.returnValue();
|
||||
|
||||
component.hideMenu();
|
||||
|
||||
expect(component.resetDrawToolChoice).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should reset choice on draw menu after saving drawing', () => {
|
||||
spyOn(component, 'resetDrawToolChoice');
|
||||
|
||||
component.onDrawingSaved();
|
||||
|
||||
expect(component.resetDrawToolChoice).toHaveBeenCalled();
|
||||
expect(component.projectMapMenuComponent.resetDrawToolChoice).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
@ -46,6 +46,7 @@ import { MovingEventSource } from '../../cartography/events/moving-event-source'
|
||||
import { LinkWidget } from '../../cartography/widgets/link';
|
||||
import { MapScaleService } from '../../services/mapScale.service';
|
||||
import { NodeCreatedLabelStylesFixer } from './helpers/node-created-label-styles-fixer';
|
||||
import { ProjectMapMenuComponent } from './project-map-menu/project-map-menu.component';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -61,8 +62,8 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
public symbols: Symbol[] = [];
|
||||
public project: Project;
|
||||
public server: Server;
|
||||
public selectedDrawing: string;
|
||||
private ws: Subject<any>;
|
||||
public isProjectMapMenuVisible: boolean = false;
|
||||
|
||||
tools = {
|
||||
selection: true,
|
||||
@ -72,19 +73,11 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
};
|
||||
|
||||
protected settings: Settings;
|
||||
|
||||
protected drawTools = {
|
||||
isRectangleChosen: false,
|
||||
isEllipseChosen: false,
|
||||
isLineChosen: false,
|
||||
isTextChosen: false,
|
||||
visibility: false
|
||||
};
|
||||
|
||||
private inReadOnlyMode = false;
|
||||
|
||||
@ViewChild(ContextMenuComponent) contextMenu: ContextMenuComponent;
|
||||
@ViewChild(D3MapComponent) mapChild: D3MapComponent;
|
||||
@ViewChild(ProjectMapMenuComponent) projectMapMenuComponent: ProjectMapMenuComponent;
|
||||
|
||||
private subscriptions: Subscription[] = [];
|
||||
|
||||
@ -304,7 +297,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public onDrawingSaved() {
|
||||
this.resetDrawToolChoice();
|
||||
this.projectMapMenuComponent.resetDrawToolChoice();
|
||||
}
|
||||
|
||||
public set readonly(value) {
|
||||
@ -341,54 +334,13 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
this.project.show_interface_labels = enabled;
|
||||
}
|
||||
|
||||
public addDrawing(selectedObject: string) {
|
||||
switch (selectedObject) {
|
||||
case 'rectangle':
|
||||
this.drawTools.isTextChosen = false;
|
||||
this.drawTools.isEllipseChosen = false;
|
||||
this.drawTools.isRectangleChosen = !this.drawTools.isRectangleChosen;
|
||||
this.drawTools.isLineChosen = false;
|
||||
break;
|
||||
case 'ellipse':
|
||||
this.drawTools.isTextChosen = false;
|
||||
this.drawTools.isEllipseChosen = !this.drawTools.isEllipseChosen;
|
||||
this.drawTools.isRectangleChosen = false;
|
||||
this.drawTools.isLineChosen = false;
|
||||
break;
|
||||
case 'line':
|
||||
this.drawTools.isTextChosen = false;
|
||||
this.drawTools.isEllipseChosen = false;
|
||||
this.drawTools.isRectangleChosen = false;
|
||||
this.drawTools.isLineChosen = !this.drawTools.isLineChosen;
|
||||
break;
|
||||
case 'text':
|
||||
this.drawTools.isTextChosen = !this.drawTools.isTextChosen;
|
||||
this.drawTools.isEllipseChosen = false;
|
||||
this.drawTools.isRectangleChosen = false;
|
||||
this.drawTools.isLineChosen = false;
|
||||
this.toolsService.textAddingToolActivation(this.drawTools.isTextChosen);
|
||||
break;
|
||||
}
|
||||
|
||||
this.selectedDrawing = this.selectedDrawing === selectedObject ? '' : selectedObject;
|
||||
}
|
||||
|
||||
public resetDrawToolChoice() {
|
||||
this.drawTools.isRectangleChosen = false;
|
||||
this.drawTools.isEllipseChosen = false;
|
||||
this.drawTools.isLineChosen = false;
|
||||
this.drawTools.isTextChosen = false;
|
||||
this.selectedDrawing = '';
|
||||
this.toolsService.textAddingToolActivation(this.drawTools.isTextChosen);
|
||||
}
|
||||
|
||||
public hideMenu() {
|
||||
this.resetDrawToolChoice();
|
||||
this.drawTools.visibility = false;
|
||||
this.projectMapMenuComponent.resetDrawToolChoice()
|
||||
this.isProjectMapMenuVisible = false;
|
||||
}
|
||||
|
||||
public showMenu() {
|
||||
this.drawTools.visibility = true;
|
||||
this.isProjectMapMenuVisible = true;
|
||||
}
|
||||
|
||||
zoomIn() {
|
||||
|
13
src/app/services/mapsettings.service.ts
Normal file
13
src/app/services/mapsettings.service.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class MapSettingService {
|
||||
public isMapLocked = new Subject<boolean>();
|
||||
|
||||
constructor() {}
|
||||
|
||||
changeMapLockValue(value: boolean) {
|
||||
this.isMapLocked.next(value);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user