mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2024-12-19 04:57:51 +00:00
Merge with master
This commit is contained in:
commit
71129e28cf
@ -74,7 +74,18 @@ import { ProjectNameValidator } from './components/projects/models/projectNameVa
|
|||||||
import { MatSidenavModule } from '@angular/material';
|
import { MatSidenavModule } from '@angular/material';
|
||||||
import { NodeSelectInterfaceComponent } from './components/project-map/node-select-interface/node-select-interface.component';
|
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';
|
import { DrawLinkToolComponent } from './components/project-map/draw-link-tool/draw-link-tool.component';
|
||||||
|
|
||||||
import { InstalledSoftwareComponent } from './components/installed-software/installed-software.component';
|
import { InstalledSoftwareComponent } from './components/installed-software/installed-software.component';
|
||||||
|
import { DrawingResizedComponent } from './components/drawings-listeners/drawing-resized/drawing-resized.component';
|
||||||
|
import { TextEditedComponent } from './components/drawings-listeners/text-edited/text-edited.component';
|
||||||
|
import { NodeDraggedComponent } from './components/drawings-listeners/node-dragged/node-dragged.component';
|
||||||
|
import { NodeLabelDraggedComponent } from './components/drawings-listeners/node-label-dragged/node-label-dragged.component';
|
||||||
|
import { DrawingDraggedComponent } from './components/drawings-listeners/drawing-dragged/drawing-dragged.component';
|
||||||
|
import { LinkCreatedComponent } from './components/drawings-listeners/link-created/link-created.component';
|
||||||
|
import { InterfaceLabelDraggedComponent } from './components/drawings-listeners/interface-label-dragged/interface-label-dragged.component';
|
||||||
|
import { ToolsService } from './services/tools.service';
|
||||||
|
import { TextAddedComponent } from './components/drawings-listeners/text-added/text-added.component';
|
||||||
|
import { DrawingAddedComponent } from './components/drawings-listeners/drawing-added/drawing-added.component';
|
||||||
|
|
||||||
|
|
||||||
if (environment.production) {
|
if (environment.production) {
|
||||||
@ -118,7 +129,16 @@ if (environment.production) {
|
|||||||
ServerDiscoveryComponent,
|
ServerDiscoveryComponent,
|
||||||
NodeSelectInterfaceComponent,
|
NodeSelectInterfaceComponent,
|
||||||
DrawLinkToolComponent,
|
DrawLinkToolComponent,
|
||||||
InstalledSoftwareComponent
|
InstalledSoftwareComponent,
|
||||||
|
DrawingAddedComponent,
|
||||||
|
DrawingResizedComponent,
|
||||||
|
TextAddedComponent,
|
||||||
|
TextEditedComponent,
|
||||||
|
NodeDraggedComponent,
|
||||||
|
NodeLabelDraggedComponent,
|
||||||
|
DrawingDraggedComponent,
|
||||||
|
LinkCreatedComponent,
|
||||||
|
InterfaceLabelDraggedComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@ -163,7 +183,8 @@ if (environment.production) {
|
|||||||
DrawingsDataSource,
|
DrawingsDataSource,
|
||||||
ServerErrorHandler,
|
ServerErrorHandler,
|
||||||
ServerDatabase,
|
ServerDatabase,
|
||||||
ProjectNameValidator
|
ProjectNameValidator,
|
||||||
|
ToolsService
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
AddServerDialogComponent,
|
AddServerDialogComponent,
|
||||||
|
@ -4,6 +4,7 @@ import { MatMenuModule, MatIconModule } from '@angular/material';
|
|||||||
|
|
||||||
import { CssFixer } from './helpers/css-fixer';
|
import { CssFixer } from './helpers/css-fixer';
|
||||||
import { FontFixer } from './helpers/font-fixer';
|
import { FontFixer } from './helpers/font-fixer';
|
||||||
|
import { DefaultDrawingsFactory } from './helpers/default-drawings-factory';
|
||||||
import { MultiLinkCalculatorHelper } from './helpers/multi-link-calculator-helper';
|
import { MultiLinkCalculatorHelper } from './helpers/multi-link-calculator-helper';
|
||||||
import { SvgToDrawingConverter } from './helpers/svg-to-drawing-converter';
|
import { SvgToDrawingConverter } from './helpers/svg-to-drawing-converter';
|
||||||
import { QtDasharrayFixer } from './helpers/qt-dasharray-fixer';
|
import { QtDasharrayFixer } from './helpers/qt-dasharray-fixer';
|
||||||
@ -41,10 +42,14 @@ import { SelectionSelectComponent } from './components/selection-select/selectio
|
|||||||
import { DraggableSelectionComponent } from './components/draggable-selection/draggable-selection.component';
|
import { DraggableSelectionComponent } from './components/draggable-selection/draggable-selection.component';
|
||||||
import { MapSettingsManager } from './managers/map-settings-manager';
|
import { MapSettingsManager } from './managers/map-settings-manager';
|
||||||
import { DrawingResizingComponent } from './components/drawing-resizing/drawing-resizing.component';
|
import { DrawingResizingComponent } from './components/drawing-resizing/drawing-resizing.component';
|
||||||
import { TextAddingComponent } from './components/text-adding/text-adding.component';
|
|
||||||
import { TextEditingComponent } from './components/text-editing/text-editing.component';
|
|
||||||
import { FontBBoxCalculator } from './helpers/font-bbox-calculator';
|
import { FontBBoxCalculator } from './helpers/font-bbox-calculator';
|
||||||
import { StylesToFontConverter } from './converters/styles-to-font-converter';
|
import { StylesToFontConverter } from './converters/styles-to-font-converter';
|
||||||
|
import { TextElementFactory } from './helpers/drawings-factory/text-element-factory';
|
||||||
|
import { EllipseElementFactory } from './helpers/drawings-factory/ellipse-element-factory';
|
||||||
|
import { RectangleElementFactory } from './helpers/drawings-factory/rectangle-element-factory';
|
||||||
|
import { LineElementFactory } from './helpers/drawings-factory/line-element-factory';
|
||||||
|
import { TextEditorComponent } from './components/text-editor/text-editor.component';
|
||||||
|
import { DrawingAddingComponent } from './components/drawing-adding/drawing-adding.component';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -56,9 +61,9 @@ import { StylesToFontConverter } from './converters/styles-to-font-converter';
|
|||||||
declarations: [
|
declarations: [
|
||||||
D3MapComponent,
|
D3MapComponent,
|
||||||
ExperimentalMapComponent,
|
ExperimentalMapComponent,
|
||||||
|
DrawingAddingComponent,
|
||||||
DrawingResizingComponent,
|
DrawingResizingComponent,
|
||||||
TextAddingComponent,
|
TextEditorComponent,
|
||||||
TextEditingComponent,
|
|
||||||
...ANGULAR_MAP_DECLARATIONS,
|
...ANGULAR_MAP_DECLARATIONS,
|
||||||
SelectionControlComponent,
|
SelectionControlComponent,
|
||||||
SelectionSelectComponent,
|
SelectionSelectComponent,
|
||||||
@ -67,6 +72,11 @@ import { StylesToFontConverter } from './converters/styles-to-font-converter';
|
|||||||
providers: [
|
providers: [
|
||||||
CssFixer,
|
CssFixer,
|
||||||
FontFixer,
|
FontFixer,
|
||||||
|
DefaultDrawingsFactory,
|
||||||
|
TextElementFactory,
|
||||||
|
EllipseElementFactory,
|
||||||
|
RectangleElementFactory,
|
||||||
|
LineElementFactory,
|
||||||
MultiLinkCalculatorHelper,
|
MultiLinkCalculatorHelper,
|
||||||
SvgToDrawingConverter,
|
SvgToDrawingConverter,
|
||||||
QtDasharrayFixer,
|
QtDasharrayFixer,
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
</filter>
|
</filter>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<app-drawing-adding [svg]="svg"></app-drawing-adding>
|
||||||
<app-drawing-resizing></app-drawing-resizing>
|
<app-drawing-resizing></app-drawing-resizing>
|
||||||
<app-selection-control></app-selection-control>
|
<app-selection-control></app-selection-control>
|
||||||
<app-selection-select></app-selection-select>
|
<app-selection-select></app-selection-select>
|
||||||
<app-text-adding></app-text-adding>
|
<app-text-editor #textEditor [svg]="svg"></app-text-editor>
|
||||||
<app-text-editing></app-text-editing>
|
|
||||||
<app-draggable-selection [svg]="svg"></app-draggable-selection>
|
<app-draggable-selection [svg]="svg"></app-draggable-selection>
|
||||||
|
Before Width: | Height: | Size: 446 B After Width: | Height: | Size: 486 B |
@ -11,18 +11,16 @@ import { InterfaceLabelWidget } from '../../widgets/interface-label';
|
|||||||
import { SelectionTool } from '../../tools/selection-tool';
|
import { SelectionTool } from '../../tools/selection-tool';
|
||||||
import { MovingTool } from '../../tools/moving-tool';
|
import { MovingTool } from '../../tools/moving-tool';
|
||||||
import { MapChangeDetectorRef } from '../../services/map-change-detector-ref';
|
import { MapChangeDetectorRef } from '../../services/map-change-detector-ref';
|
||||||
import { MapLinkCreated } from '../../events/links';
|
|
||||||
import { CanvasSizeDetector } from '../../helpers/canvas-size-detector';
|
import { CanvasSizeDetector } from '../../helpers/canvas-size-detector';
|
||||||
import { Node } from '../../models/node';
|
import { Node } from '../../models/node';
|
||||||
import { Link } from '../../../models/link';
|
import { Link } from '../../../models/link';
|
||||||
import { Drawing } from '../../models/drawing';
|
import { Drawing } from '../../models/drawing';
|
||||||
import { Symbol } from '../../../models/symbol';
|
import { Symbol } from '../../../models/symbol';
|
||||||
import { GraphDataManager } from '../../managers/graph-data-manager';
|
import { GraphDataManager } from '../../managers/graph-data-manager';
|
||||||
import { DraggedDataEvent } from '../../events/event-source';
|
|
||||||
import { MapSettingsManager } from '../../managers/map-settings-manager';
|
import { MapSettingsManager } from '../../managers/map-settings-manager';
|
||||||
import { TextEditingTool } from '../../tools/text-editing-tool';
|
|
||||||
import { TextAddingComponent } from '../text-adding/text-adding.component';
|
|
||||||
import { Server } from '../../../models/server';
|
import { Server } from '../../../models/server';
|
||||||
|
import { ToolsService } from '../../../services/tools.service';
|
||||||
|
import { TextEditorComponent } from '../text-editor/text-editor.component';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -41,21 +39,20 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
@Input() height = 600;
|
@Input() height = 600;
|
||||||
|
|
||||||
@ViewChild('svg') svgRef: ElementRef;
|
@ViewChild('svg') svgRef: ElementRef;
|
||||||
@ViewChild(TextAddingComponent) textAddingComponent: TextAddingComponent;
|
@ViewChild('textEditor') textEditor: TextEditorComponent;
|
||||||
|
|
||||||
private parentNativeElement: any;
|
private parentNativeElement: any;
|
||||||
private svg: Selection<SVGSVGElement, any, null, undefined>;
|
private svg: Selection<SVGSVGElement, any, null, undefined>;
|
||||||
|
|
||||||
private onChangesDetected: Subscription;
|
private onChangesDetected: Subscription;
|
||||||
|
private subscriptions: Subscription[] = [];
|
||||||
|
|
||||||
|
private drawLinkTool: boolean;
|
||||||
|
|
||||||
protected settings = {
|
protected settings = {
|
||||||
'show_interface_labels': true
|
'show_interface_labels': true
|
||||||
};
|
};
|
||||||
|
|
||||||
ngAfterInit(){
|
|
||||||
console.log(this.textAddingComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private graphDataManager: GraphDataManager,
|
private graphDataManager: GraphDataManager,
|
||||||
public context: Context,
|
public context: Context,
|
||||||
@ -66,8 +63,8 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
protected interfaceLabelWidget: InterfaceLabelWidget,
|
protected interfaceLabelWidget: InterfaceLabelWidget,
|
||||||
protected selectionToolWidget: SelectionTool,
|
protected selectionToolWidget: SelectionTool,
|
||||||
protected movingToolWidget: MovingTool,
|
protected movingToolWidget: MovingTool,
|
||||||
protected textEditingToolWidget: TextEditingTool,
|
|
||||||
public graphLayout: GraphLayout,
|
public graphLayout: GraphLayout,
|
||||||
|
private toolsService: ToolsService
|
||||||
) {
|
) {
|
||||||
this.parentNativeElement = element.nativeElement;
|
this.parentNativeElement = element.nativeElement;
|
||||||
}
|
}
|
||||||
@ -79,26 +76,6 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.mapChangeDetectorRef.detectChanges();
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Input('moving-tool')
|
|
||||||
set movingTool(value) {
|
|
||||||
this.movingToolWidget.setEnabled(value);
|
|
||||||
this.mapChangeDetectorRef.detectChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Input('selection-tool')
|
|
||||||
set selectionTool(value) {
|
|
||||||
this.selectionToolWidget.setEnabled(value);
|
|
||||||
this.mapChangeDetectorRef.detectChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Input('text-editing-tool')
|
|
||||||
set textEditingTool(value){
|
|
||||||
this.textEditingToolWidget.setEnabled(value);
|
|
||||||
this.mapChangeDetectorRef.detectChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Input('draw-link-tool') drawLinkTool: boolean;
|
|
||||||
|
|
||||||
@Input('readonly') set readonly(value) {
|
@Input('readonly') set readonly(value) {
|
||||||
this.mapSettings.isReadOnly = value;
|
this.mapSettings.isReadOnly = value;
|
||||||
}
|
}
|
||||||
@ -132,11 +109,34 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.redraw();
|
this.redraw();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.toolsService.isMovingToolActivated.subscribe((value: boolean) => {
|
||||||
|
this.movingToolWidget.setEnabled(value);
|
||||||
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.toolsService.isSelectionToolActivated.subscribe((value: boolean) => {
|
||||||
|
this.selectionToolWidget.setEnabled(value);
|
||||||
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.subscriptions.push(
|
||||||
|
this.toolsService.isDrawLinkToolActivated.subscribe((value: boolean) => {
|
||||||
|
this.drawLinkTool = value;
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.graphLayout.disconnect(this.svg);
|
this.graphLayout.disconnect(this.svg);
|
||||||
this.onChangesDetected.unsubscribe();
|
this.onChangesDetected.unsubscribe();
|
||||||
|
this.subscriptions.forEach((subscription: Subscription) => {
|
||||||
|
subscription.unsubscribe();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public createGraph(domElement: HTMLElement) {
|
public createGraph(domElement: HTMLElement) {
|
||||||
@ -168,6 +168,7 @@ export class D3MapComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.graphDataManager.setLinks(this.links);
|
this.graphDataManager.setLinks(this.links);
|
||||||
this.graphDataManager.setDrawings(this.drawings);
|
this.graphDataManager.setDrawings(this.drawings);
|
||||||
this.graphLayout.draw(this.svg, this.context);
|
this.graphLayout.draw(this.svg, this.context);
|
||||||
|
this.textEditor.activateTextEditing();
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('window:resize', ['$event'])
|
@HostListener('window:resize', ['$event'])
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
import { DrawingAddingComponent } from "./drawing-adding.component";
|
||||||
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { DrawingsEventSource } from '../../events/drawings-event-source';
|
||||||
|
import { Context } from '../../models/context';
|
||||||
|
|
||||||
|
describe('DrawingAddingComponent', () => {
|
||||||
|
let component: DrawingAddingComponent;
|
||||||
|
let fixture: ComponentFixture<DrawingAddingComponent>;
|
||||||
|
let drawingsEventSource = new DrawingsEventSource();
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
NoopAnimationsModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: DrawingsEventSource, useValue: drawingsEventSource },
|
||||||
|
{ provide: Context, useClass: Context }
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
DrawingAddingComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DrawingAddingComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should deactivate listener when none of the available drawings is selected', () => {
|
||||||
|
spyOn(component, 'deactivate');
|
||||||
|
|
||||||
|
drawingsEventSource.selected.emit("");
|
||||||
|
|
||||||
|
expect(component.deactivate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should activate listener when drawing is selected', () => {
|
||||||
|
spyOn(component, 'activate');
|
||||||
|
|
||||||
|
drawingsEventSource.selected.emit("rectangle");
|
||||||
|
|
||||||
|
expect(component.activate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,51 @@
|
|||||||
|
import { Component, Input, OnDestroy, OnInit } from "@angular/core";
|
||||||
|
import { Context } from '../../models/context';
|
||||||
|
import { DrawingsEventSource } from '../../events/drawings-event-source';
|
||||||
|
import { AddedDataEvent } from '../../events/event-source';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-drawing-adding',
|
||||||
|
templateUrl: './drawing-adding.component.html',
|
||||||
|
styleUrls: ['./drawing-adding.component.scss']
|
||||||
|
})
|
||||||
|
export class DrawingAddingComponent implements OnInit, OnDestroy {
|
||||||
|
@Input('svg') svg: SVGSVGElement;
|
||||||
|
|
||||||
|
private mapListener: Function;
|
||||||
|
private drawingSelected: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private drawingsEventSource: DrawingsEventSource,
|
||||||
|
private context: Context
|
||||||
|
){}
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
this.drawingSelected = this.drawingsEventSource.selected.subscribe((evt) => {
|
||||||
|
evt === "" ? this.deactivate() : this.activate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
activate(){
|
||||||
|
let listener = (event: MouseEvent) => {
|
||||||
|
let x = event.clientX - this.context.getZeroZeroTransformationPoint().x;
|
||||||
|
let y = event.clientY - this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
|
||||||
|
this.drawingsEventSource.pointToAddSelected.emit(new AddedDataEvent(x, y));
|
||||||
|
this.deactivate();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.deactivate();
|
||||||
|
this.mapListener = listener;
|
||||||
|
this.svg.addEventListener('click', this.mapListener as EventListenerOrEventListenerObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
deactivate(){
|
||||||
|
this.svg.removeEventListener('click', this.mapListener as EventListenerOrEventListenerObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(){
|
||||||
|
this.drawingSelected.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,7 @@ export class DrawingWidgetMock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('DrawizngResizingComponent', () => {
|
describe('DrawingResizingComponent', () => {
|
||||||
let component: DrawingResizingComponent;
|
let component: DrawingResizingComponent;
|
||||||
let fixture: ComponentFixture<DrawingResizingComponent>;
|
let fixture: ComponentFixture<DrawingResizingComponent>;
|
||||||
let drawingsWidgetMock = new DrawingWidgetMock;
|
let drawingsWidgetMock = new DrawingWidgetMock;
|
||||||
@ -47,15 +47,15 @@ describe('DrawizngResizingComponent', () => {
|
|||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(DrawingResizingComponent);
|
fixture = TestBed.createComponent(DrawingResizingComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit event after size changes', () => {
|
it('should emit event after size changes', () => {
|
||||||
spyOn(drawingsEventSource.resized, 'emit');
|
spyOn(drawingsEventSource.resized, 'emit');
|
||||||
|
@ -6,6 +6,7 @@ import { MapDrawing } from '../../models/map/map-drawing';
|
|||||||
import { ResizedDataEvent } from '../../events/event-source';
|
import { ResizedDataEvent } from '../../events/event-source';
|
||||||
import { ResizingEnd } from '../../events/resizing';
|
import { ResizingEnd } from '../../events/resizing';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-drawing-resizing',
|
selector: 'app-drawing-resizing',
|
||||||
template: `<ng-content></ng-content>`,
|
template: `<ng-content></ng-content>`,
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
import { Component, OnInit } from "@angular/core";
|
|
||||||
import { select } from 'd3-selection';
|
|
||||||
import { Context } from "../../models/context";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-text-adding',
|
|
||||||
template: `<ng-content></ng-content>`,
|
|
||||||
styleUrls: ['./text-adding.component.scss']
|
|
||||||
})
|
|
||||||
export class TextAddingComponent implements OnInit {
|
|
||||||
public isEnabled: boolean = true;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private context: Context
|
|
||||||
){}
|
|
||||||
|
|
||||||
ngOnInit(){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
addingTextSelected(){
|
|
||||||
//here will be moved addText in projectMapComponent
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
|
||||||
import { Subscription } from 'rxjs';
|
|
||||||
import { TextEditingTool } from '../../tools/text-editing-tool';
|
|
||||||
import { DrawingsEventSource } from '../../events/drawings-event-source';
|
|
||||||
import { TextEditedDataEvent } from '../../events/event-source';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-text-editing',
|
|
||||||
template: `<ng-content></ng-content>`,
|
|
||||||
styleUrls: ['./text-editing.component.scss']
|
|
||||||
})
|
|
||||||
export class TextEditingComponent implements OnInit, OnDestroy{
|
|
||||||
textEditingFinished: Subscription;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private textEditingTool: TextEditingTool,
|
|
||||||
private drawingEventSource: DrawingsEventSource
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
this.textEditingFinished = this.textEditingTool.editingFinished.subscribe((evt: TextEditedDataEvent) => {
|
|
||||||
this.drawingEventSource.textEdited.emit(evt);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onTextAddingChosen() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy() {
|
|
||||||
this.textEditingFinished.unsubscribe();
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,6 @@
|
|||||||
|
<div #temporaryTextElement id="temporaryElement" class="temporaryElement" contenteditable="true"
|
||||||
|
[style.top]=topPosition
|
||||||
|
[style.left]=leftPosition
|
||||||
|
[style.display]=display>
|
||||||
|
{{innerText}}
|
||||||
|
</div>
|
@ -0,0 +1,10 @@
|
|||||||
|
.temporaryElement{
|
||||||
|
padding-left: 4px;
|
||||||
|
width: fit-content;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 99;
|
||||||
|
font-family: 'Noto Sans';
|
||||||
|
font-size: 11pt;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #000000;
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
import { TextEditorComponent } from "./text-editor.component";
|
||||||
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { DrawingsEventSource } from '../../events/drawings-event-source';
|
||||||
|
import { ToolsService } from '../../../services/tools.service';
|
||||||
|
import { Context } from '../../models/context';
|
||||||
|
import { Renderer2 } from '@angular/core';
|
||||||
|
|
||||||
|
describe('TemporaryTextElementComponent', () => {
|
||||||
|
let component: TextEditorComponent;
|
||||||
|
let fixture: ComponentFixture<TextEditorComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
NoopAnimationsModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: DrawingsEventSource, useClass: DrawingsEventSource },
|
||||||
|
{ provide: ToolsService, useClass: ToolsService },
|
||||||
|
{ provide: Context, useClass: Context },
|
||||||
|
{ provide: Renderer2, useClass: Renderer2 }
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
TextEditorComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(TextEditorComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,119 @@
|
|||||||
|
import { Component, ViewChild, ElementRef, OnInit, Input, EventEmitter, OnDestroy, Renderer2 } from "@angular/core";
|
||||||
|
import { DrawingsEventSource } from '../../events/drawings-event-source';
|
||||||
|
import { TextAddedDataEvent, TextEditedDataEvent } from '../../events/event-source';
|
||||||
|
import { ToolsService } from '../../../services/tools.service';
|
||||||
|
import { select } from 'd3-selection';
|
||||||
|
import { TextElement } from '../../models/drawings/text-element';
|
||||||
|
import { Context } from '../../models/context';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-text-editor',
|
||||||
|
templateUrl: './text-editor.component.html',
|
||||||
|
styleUrls: ['./text-editor.component.scss']
|
||||||
|
})
|
||||||
|
export class TextEditorComponent implements OnInit, OnDestroy {
|
||||||
|
@ViewChild('temporaryTextElement') temporaryTextElement: ElementRef;
|
||||||
|
@Input('svg') svg: SVGSVGElement;
|
||||||
|
|
||||||
|
private leftPosition: string = '0px';
|
||||||
|
private topPosition: string = '0px';
|
||||||
|
private innerText: string = '';
|
||||||
|
|
||||||
|
private editingDrawingId: string;
|
||||||
|
private editedElement: any;
|
||||||
|
|
||||||
|
private mapListener: Function;
|
||||||
|
private textListener: Function;
|
||||||
|
private textAddingSubscription: Subscription;
|
||||||
|
public addingFinished = new EventEmitter<any>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private drawingsEventSource: DrawingsEventSource,
|
||||||
|
private toolsService: ToolsService,
|
||||||
|
private context: Context,
|
||||||
|
private renderer: Renderer2
|
||||||
|
){}
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
this.textAddingSubscription = this.toolsService.isTextAddingToolActivated.subscribe((isActive: boolean) => {
|
||||||
|
isActive ? this.activateTextAdding() : this.deactivateTextAdding()
|
||||||
|
});
|
||||||
|
|
||||||
|
this.activateTextEditing();
|
||||||
|
}
|
||||||
|
|
||||||
|
activateTextAdding(){
|
||||||
|
let addTextListener = (event: MouseEvent) => {
|
||||||
|
this.leftPosition = event.clientX.toString() + 'px';
|
||||||
|
this.topPosition = (event.clientY + window.pageYOffset).toString() + 'px';
|
||||||
|
this.renderer.setStyle(this.temporaryTextElement.nativeElement, 'display', 'initial');
|
||||||
|
this.temporaryTextElement.nativeElement.focus();
|
||||||
|
|
||||||
|
let textListener = () => {
|
||||||
|
this.drawingsEventSource.textAdded.emit(new TextAddedDataEvent(this.temporaryTextElement.nativeElement.innerText.replace(/\n$/, ""), event.clientX, event.clientY));
|
||||||
|
this.deactivateTextAdding();
|
||||||
|
this.innerText = '';
|
||||||
|
this.temporaryTextElement.nativeElement.removeEventListener("focusout", this.textListener);
|
||||||
|
this.renderer.setStyle(this.temporaryTextElement.nativeElement, 'display', 'none');
|
||||||
|
}
|
||||||
|
this.textListener = textListener;
|
||||||
|
this.temporaryTextElement.nativeElement.addEventListener('focusout', this.textListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.deactivateTextAdding();
|
||||||
|
this.mapListener = addTextListener;
|
||||||
|
this.svg.addEventListener('click', this.mapListener as EventListenerOrEventListenerObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
deactivateTextAdding(){
|
||||||
|
this.svg.removeEventListener('click', this.mapListener as EventListenerOrEventListenerObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
activateTextEditing(){
|
||||||
|
const rootElement = select(this.svg);
|
||||||
|
|
||||||
|
rootElement.selectAll<SVGTextElement, TextElement>('text.text_element')
|
||||||
|
.on("dblclick", (elem, index, textElements) => {
|
||||||
|
this.renderer.setStyle(this.temporaryTextElement.nativeElement, 'display', 'initial');
|
||||||
|
this.editedElement = elem;
|
||||||
|
|
||||||
|
select(textElements[index])
|
||||||
|
.attr("visibility", "hidden");
|
||||||
|
|
||||||
|
select(textElements[index])
|
||||||
|
.classed("editingMode", true);
|
||||||
|
|
||||||
|
this.editingDrawingId = textElements[index].parentElement.parentElement.getAttribute("drawing_id");
|
||||||
|
var transformData = textElements[index].parentElement.getAttribute("transform").split(/\(|\)/);
|
||||||
|
var x = Number(transformData[1].split(/,/)[0]) + this.context.getZeroZeroTransformationPoint().x;
|
||||||
|
var y = Number(transformData[1].split(/,/)[1]) + this.context.getZeroZeroTransformationPoint().y;
|
||||||
|
this.leftPosition = x.toString() + 'px';
|
||||||
|
this.topPosition = y.toString() + 'px';
|
||||||
|
this.temporaryTextElement.nativeElement.innerText = elem.text;
|
||||||
|
|
||||||
|
let listener = () => {
|
||||||
|
let innerText = this.temporaryTextElement.nativeElement.innerText;
|
||||||
|
this.drawingsEventSource.textEdited.emit(new TextEditedDataEvent(this.editingDrawingId, innerText.replace(/\n$/, ""), this.editedElement));
|
||||||
|
|
||||||
|
rootElement.selectAll<SVGTextElement, TextElement>('text.editingMode')
|
||||||
|
.attr("visibility", "visible")
|
||||||
|
.classed("editingMode", false);
|
||||||
|
|
||||||
|
this.innerText = '';
|
||||||
|
this.temporaryTextElement.nativeElement.innerText = '';
|
||||||
|
this.temporaryTextElement.nativeElement.removeEventListener("focusout", this.textListener);
|
||||||
|
|
||||||
|
this.renderer.setStyle(this.temporaryTextElement.nativeElement, 'display', 'none');
|
||||||
|
};
|
||||||
|
this.textListener = listener;
|
||||||
|
this.temporaryTextElement.nativeElement.addEventListener("focusout", this.textListener);
|
||||||
|
this.temporaryTextElement.nativeElement.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(){
|
||||||
|
this.textAddingSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ export class MapDrawingToSvgConverter implements Converter<MapDrawing, string> {
|
|||||||
} else if (mapDrawing.element instanceof LineElement) {
|
} else if (mapDrawing.element instanceof LineElement) {
|
||||||
elem = `<line stroke=\"${mapDrawing.element.stroke}\" stroke-width=\"${mapDrawing.element.stroke_width}\" x1=\"${mapDrawing.element.x1}\" x2=\"${mapDrawing.element.x2}\" y1=\"${mapDrawing.element.y1}\" y2=\"${mapDrawing.element.y2}\" />`
|
elem = `<line stroke=\"${mapDrawing.element.stroke}\" stroke-width=\"${mapDrawing.element.stroke_width}\" x1=\"${mapDrawing.element.x1}\" x2=\"${mapDrawing.element.x2}\" y1=\"${mapDrawing.element.y1}\" y2=\"${mapDrawing.element.y2}\" />`
|
||||||
} else if (mapDrawing.element instanceof TextElement) {
|
} else if (mapDrawing.element instanceof TextElement) {
|
||||||
elem = `<text fill=\"${mapDrawing.element.fill}\" fill-opacity=\"${mapDrawing.element.fill_opacity}\" font-family=\"${mapDrawing.element.font_family}\" font-size=\"${mapDrawing.element.font_size}\" font-weight=\"${mapDrawing.element.font_weight}\">${mapDrawing.element.text}</text>`;
|
elem = `<text fill=\"${mapDrawing.element.fill}\" fill-opacity=\"1.0\" font-family=\"${mapDrawing.element.font_family}\" font-size=\"${mapDrawing.element.font_size}\" font-weight=\"${mapDrawing.element.font_weight}\">${mapDrawing.element.text}</text>`;
|
||||||
} else return "";
|
} else return "";
|
||||||
|
|
||||||
return `<svg height=\"${mapDrawing.element.height}\" width=\"${mapDrawing.element.width}\">${elem}</svg>`;
|
return `<svg height=\"${mapDrawing.element.height}\" width=\"${mapDrawing.element.width}\">${elem}</svg>`;
|
||||||
|
@ -5,7 +5,6 @@ import { DrawingsWidget } from './widgets/drawings';
|
|||||||
import { DrawingLineWidget } from './widgets/drawing-line';
|
import { DrawingLineWidget } from './widgets/drawing-line';
|
||||||
import { SelectionTool } from './tools/selection-tool';
|
import { SelectionTool } from './tools/selection-tool';
|
||||||
import { MovingTool } from './tools/moving-tool';
|
import { MovingTool } from './tools/moving-tool';
|
||||||
import {TextEditingTool} from './tools/text-editing-tool';
|
|
||||||
import { LayersWidget } from './widgets/layers';
|
import { LayersWidget } from './widgets/layers';
|
||||||
import { LinkWidget } from './widgets/link';
|
import { LinkWidget } from './widgets/link';
|
||||||
import { InterfaceStatusWidget } from './widgets/interface-status';
|
import { InterfaceStatusWidget } from './widgets/interface-status';
|
||||||
@ -29,7 +28,6 @@ export const D3_MAP_IMPORTS = [
|
|||||||
DrawingLineWidget,
|
DrawingLineWidget,
|
||||||
SelectionTool,
|
SelectionTool,
|
||||||
MovingTool,
|
MovingTool,
|
||||||
TextEditingTool,
|
|
||||||
LayersWidget,
|
LayersWidget,
|
||||||
LinkWidget,
|
LinkWidget,
|
||||||
InterfaceStatusWidget,
|
InterfaceStatusWidget,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Injectable, EventEmitter } from "@angular/core";
|
import { Injectable, EventEmitter } from "@angular/core";
|
||||||
import { DraggedDataEvent, ResizedDataEvent } from "./event-source";
|
import { DraggedDataEvent, ResizedDataEvent, TextAddedDataEvent, TextEditedDataEvent, AddedDataEvent } from "./event-source";
|
||||||
import { MapDrawing } from "../models/map/map-drawing";
|
import { MapDrawing } from "../models/map/map-drawing";
|
||||||
|
|
||||||
|
|
||||||
@ -7,5 +7,11 @@ import { MapDrawing } from "../models/map/map-drawing";
|
|||||||
export class DrawingsEventSource {
|
export class DrawingsEventSource {
|
||||||
public dragged = new EventEmitter<DraggedDataEvent<MapDrawing>>();
|
public dragged = new EventEmitter<DraggedDataEvent<MapDrawing>>();
|
||||||
public resized = new EventEmitter<ResizedDataEvent<MapDrawing>>();
|
public resized = new EventEmitter<ResizedDataEvent<MapDrawing>>();
|
||||||
public textEdited = new EventEmitter<any>();
|
public selected = new EventEmitter<string>();
|
||||||
|
public pointToAddSelected = new EventEmitter<AddedDataEvent>();
|
||||||
|
public saved = new EventEmitter<any>();
|
||||||
|
|
||||||
|
public textAdded = new EventEmitter<TextAddedDataEvent>();
|
||||||
|
public textEdited = new EventEmitter<TextEditedDataEvent>();
|
||||||
|
public textSaved = new EventEmitter<any>();
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,14 @@ export class ResizedDataEvent<T> {
|
|||||||
public y: number,
|
public y: number,
|
||||||
public width: number,
|
public width: number,
|
||||||
public height: number
|
public height: number
|
||||||
) {}
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AddedDataEvent {
|
||||||
|
constructor(
|
||||||
|
public x: number,
|
||||||
|
public y: number
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ClickedDataEvent<T> {
|
export class ClickedDataEvent<T> {
|
||||||
@ -28,6 +35,14 @@ export class ClickedDataEvent<T> {
|
|||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TextAddedDataEvent {
|
||||||
|
constructor(
|
||||||
|
public savedText: string,
|
||||||
|
public x: number,
|
||||||
|
public y: number
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
export class TextEditedDataEvent {
|
export class TextEditedDataEvent {
|
||||||
constructor(
|
constructor(
|
||||||
public textDrawingId: string,
|
public textDrawingId: string,
|
||||||
|
36
src/app/cartography/helpers/default-drawings-factory.ts
Normal file
36
src/app/cartography/helpers/default-drawings-factory.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { TextElementFactory } from './drawings-factory/text-element-factory';
|
||||||
|
import { EllipseElementFactory } from './drawings-factory/ellipse-element-factory';
|
||||||
|
import { RectangleElementFactory } from './drawings-factory/rectangle-element-factory';
|
||||||
|
import { LineElementFactory } from './drawings-factory/line-element-factory';
|
||||||
|
import { DrawingElementFactory } from './drawings-factory/drawing-element-factory';
|
||||||
|
import { MapDrawing } from '../models/map/map-drawing';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DefaultDrawingsFactory {
|
||||||
|
private factory: DrawingElementFactory;
|
||||||
|
private availableFactories: {[ key: string]: DrawingElementFactory};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private textElementFactory: TextElementFactory,
|
||||||
|
private ellipseElementFactory: EllipseElementFactory,
|
||||||
|
private rectangleElementFactory: RectangleElementFactory,
|
||||||
|
private lineElementFactory: LineElementFactory
|
||||||
|
){
|
||||||
|
this.availableFactories = {
|
||||||
|
'text': this.textElementFactory,
|
||||||
|
'ellipse': this.ellipseElementFactory,
|
||||||
|
'rectangle': this.rectangleElementFactory,
|
||||||
|
'line': this.lineElementFactory
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getDrawingMock(drawingType: string){
|
||||||
|
this.factory = this.availableFactories[drawingType];
|
||||||
|
|
||||||
|
let mapDrawing = new MapDrawing();
|
||||||
|
mapDrawing.element = this.factory.getDrawingElement();
|
||||||
|
|
||||||
|
return mapDrawing;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
import { DrawingElement } from '../../models/drawings/drawing-element';
|
||||||
|
|
||||||
|
export interface DrawingElementFactory{
|
||||||
|
getDrawingElement(): DrawingElement;
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
import { DrawingElementFactory } from './drawing-element-factory';
|
||||||
|
import { DrawingElement } from '../../models/drawings/drawing-element';
|
||||||
|
import { EllipseElement } from '../../models/drawings/ellipse-element';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EllipseElementFactory implements DrawingElementFactory{
|
||||||
|
|
||||||
|
getDrawingElement(): DrawingElement{
|
||||||
|
let ellipseElement = new EllipseElement();
|
||||||
|
ellipseElement.fill = "#ffffff";
|
||||||
|
ellipseElement.fill_opacity = 1.0;
|
||||||
|
ellipseElement.stroke = "#000000";
|
||||||
|
ellipseElement.stroke_width = 2;
|
||||||
|
ellipseElement.cx = 100;
|
||||||
|
ellipseElement.cy = 100;
|
||||||
|
ellipseElement.rx = 100;
|
||||||
|
ellipseElement.ry = 100;
|
||||||
|
ellipseElement.width = 200;
|
||||||
|
ellipseElement.height = 200;
|
||||||
|
return ellipseElement;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
import { DrawingElementFactory } from './drawing-element-factory';
|
||||||
|
import { DrawingElement } from '../../models/drawings/drawing-element';
|
||||||
|
import { LineElement } from '../../models/drawings/line-element';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LineElementFactory implements DrawingElementFactory{
|
||||||
|
|
||||||
|
getDrawingElement(): DrawingElement{
|
||||||
|
let lineElement = new LineElement();
|
||||||
|
lineElement.stroke = "#000000";
|
||||||
|
lineElement.stroke_width = 2;
|
||||||
|
lineElement.x1 = 0;
|
||||||
|
lineElement.x2 = 200;
|
||||||
|
lineElement.y1 = 0;
|
||||||
|
lineElement.y2 = 0;
|
||||||
|
lineElement.width = 100;
|
||||||
|
lineElement.height = 0;
|
||||||
|
return lineElement;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
import { DrawingElementFactory } from './drawing-element-factory';
|
||||||
|
import { DrawingElement } from '../../models/drawings/drawing-element';
|
||||||
|
import { RectElement } from '../../models/drawings/rect-element';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RectangleElementFactory implements DrawingElementFactory{
|
||||||
|
|
||||||
|
getDrawingElement() : DrawingElement {
|
||||||
|
let rectElement = new RectElement();
|
||||||
|
rectElement.fill = "#ffffff";
|
||||||
|
rectElement.fill_opacity = 1.0;
|
||||||
|
rectElement.stroke = "#000000";
|
||||||
|
rectElement.stroke_width = 2;
|
||||||
|
rectElement.width = 200;
|
||||||
|
rectElement.height = 100;
|
||||||
|
return rectElement;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
import { DrawingElementFactory } from './drawing-element-factory';
|
||||||
|
import { DrawingElement } from '../../models/drawings/drawing-element';
|
||||||
|
import { TextElement } from '../../models/drawings/text-element';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TextElementFactory implements DrawingElementFactory{
|
||||||
|
|
||||||
|
getDrawingElement(): DrawingElement {
|
||||||
|
let textElement = new TextElement();
|
||||||
|
textElement.height = 100;
|
||||||
|
textElement.width = 100;
|
||||||
|
textElement.fill = "#000000";
|
||||||
|
textElement.fill_opacity = 0;
|
||||||
|
textElement.font_family = "Noto Sans";
|
||||||
|
textElement.font_size = 11;
|
||||||
|
textElement.font_weight = "bold";
|
||||||
|
return textElement;
|
||||||
|
}
|
||||||
|
}
|
@ -1,78 +0,0 @@
|
|||||||
import { Injectable, EventEmitter } from "@angular/core";
|
|
||||||
import { SVGSelection } from '../models/types';
|
|
||||||
import { select } from 'd3-selection';
|
|
||||||
import { TextElement } from '../models/drawings/text-element';
|
|
||||||
import { TextEditedDataEvent } from '../events/event-source';
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class TextEditingTool {
|
|
||||||
private enabled = true;
|
|
||||||
private editingDrawingId: string;
|
|
||||||
private editedElement: any;
|
|
||||||
public editingFinished = new EventEmitter<any>();
|
|
||||||
|
|
||||||
public setEnabled(enabled) {
|
|
||||||
this.enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
public draw(selection: SVGSelection){
|
|
||||||
if (!this.enabled){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
selection.selectAll<SVGTextElement, TextElement>('text.text_element')
|
|
||||||
.on("dblclick", (elem, index, textElements) => {
|
|
||||||
|
|
||||||
this.editedElement = elem;
|
|
||||||
|
|
||||||
select(textElements[index])
|
|
||||||
.attr("visibility", "hidden");
|
|
||||||
|
|
||||||
select(textElements[index])
|
|
||||||
.classed("editingMode", true);
|
|
||||||
|
|
||||||
this.editingDrawingId = textElements[index].parentElement.parentElement.getAttribute("drawing_id");
|
|
||||||
|
|
||||||
select(textElements[index].parentElement.parentElement.parentElement)
|
|
||||||
.append("foreignObject")
|
|
||||||
.attr("width", '1000px')
|
|
||||||
.attr("min-width", 'fit-content')
|
|
||||||
.attr("height", '100px')
|
|
||||||
.attr("id", "temporaryText")
|
|
||||||
.attr("transform", textElements[index].parentElement.getAttribute("transform"))
|
|
||||||
.append("xhtml:span")
|
|
||||||
.attr("width", "fit-content")
|
|
||||||
.attr("height", "fit-content")
|
|
||||||
.attr("class", "temporaryTextInside")
|
|
||||||
.attr('style', () => {
|
|
||||||
const styles: string[] = [];
|
|
||||||
styles.push(`white-space: pre-line`)
|
|
||||||
styles.push(`outline: 0px solid transparent`)
|
|
||||||
styles.push(`font-family: ${elem.font_family}`)
|
|
||||||
styles.push(`font-size: ${elem.font_size}pt!important`);
|
|
||||||
styles.push(`font-weight: ${elem.font_weight}`)
|
|
||||||
styles.push(`color: ${elem.fill}`);
|
|
||||||
return styles.join("; ");
|
|
||||||
})
|
|
||||||
.attr('text-decoration', elem.text_decoration)
|
|
||||||
.attr('contenteditable', 'true')
|
|
||||||
.text(elem.text)
|
|
||||||
.on("focusout", () => {
|
|
||||||
let temporaryText = document.getElementsByClassName("temporaryTextInside")[0] as HTMLElement;
|
|
||||||
let savedText = temporaryText.innerText;
|
|
||||||
this.editingFinished.emit(new TextEditedDataEvent(this.editingDrawingId, savedText, this.editedElement));
|
|
||||||
|
|
||||||
var temporaryElement = document.getElementById("temporaryText") as HTMLElement;
|
|
||||||
temporaryElement.remove();
|
|
||||||
|
|
||||||
selection.selectAll<SVGTextElement, TextElement>('text.editingMode')
|
|
||||||
.attr("visibility", "visible")
|
|
||||||
.classed("editingMode", false);
|
|
||||||
});
|
|
||||||
|
|
||||||
var txtInside = document.getElementsByClassName("temporaryTextInside")[0] as HTMLElement;
|
|
||||||
txtInside.focus();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,7 +8,6 @@ import { MovingTool } from "../tools/moving-tool";
|
|||||||
import { LayersWidget } from "./layers";
|
import { LayersWidget } from "./layers";
|
||||||
import { LayersManager } from "../managers/layers-manager";
|
import { LayersManager } from "../managers/layers-manager";
|
||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
import { TextEditingTool } from '../tools/text-editing-tool';
|
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -18,7 +17,6 @@ export class GraphLayout implements Widget {
|
|||||||
private drawingLineTool: DrawingLineWidget,
|
private drawingLineTool: DrawingLineWidget,
|
||||||
private selectionTool: SelectionTool,
|
private selectionTool: SelectionTool,
|
||||||
private movingTool: MovingTool,
|
private movingTool: MovingTool,
|
||||||
private textEditingTool: TextEditingTool,
|
|
||||||
private layersWidget: LayersWidget,
|
private layersWidget: LayersWidget,
|
||||||
private layersManager: LayersManager
|
private layersManager: LayersManager
|
||||||
) {
|
) {
|
||||||
@ -69,7 +67,6 @@ export class GraphLayout implements Widget {
|
|||||||
this.drawingLineTool.draw(view, context);
|
this.drawingLineTool.draw(view, context);
|
||||||
this.selectionTool.draw(view, context);
|
this.selectionTool.draw(view, context);
|
||||||
this.movingTool.draw(view, context);
|
this.movingTool.draw(view, context);
|
||||||
this.textEditingTool.draw(view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect(view: SVGSelection) {
|
disconnect(view: SVGSelection) {
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
import { DrawingAddedComponent } from "./drawing-added.component";
|
||||||
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
import { DrawingsDataSource } from '../../../cartography/datasources/drawings-datasource';
|
||||||
|
import { DrawingsEventSource } from '../../../cartography/events/drawings-event-source';
|
||||||
|
import { DefaultDrawingsFactory } from '../../../cartography/helpers/default-drawings-factory';
|
||||||
|
import { MapDrawingToSvgConverter } from '../../../cartography/converters/map/map-drawing-to-svg-converter';
|
||||||
|
import { MockedDrawingService, MockedDrawingsDataSource } from '../../project-map/project-map.component.spec';
|
||||||
|
import { TextElementFactory } from '../../../cartography/helpers/drawings-factory/text-element-factory';
|
||||||
|
import { EllipseElementFactory } from '../../../cartography/helpers/drawings-factory/ellipse-element-factory';
|
||||||
|
import { RectangleElementFactory } from '../../../cartography/helpers/drawings-factory/rectangle-element-factory';
|
||||||
|
import { LineElementFactory } from '../../../cartography/helpers/drawings-factory/line-element-factory';
|
||||||
|
import { Project } from '../../../models/project';
|
||||||
|
import { AddedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
describe('DrawingAddedComponent', () => {
|
||||||
|
let component: DrawingAddedComponent;
|
||||||
|
let fixture: ComponentFixture<DrawingAddedComponent>;
|
||||||
|
let mockedDrawingService = new MockedDrawingService;
|
||||||
|
let mockedDrawingsDataSource = new MockedDrawingsDataSource;
|
||||||
|
let mockedDrawingsEventSource = new DrawingsEventSource;
|
||||||
|
let mockedDrawingsFactory = new DefaultDrawingsFactory(new TextElementFactory,
|
||||||
|
new EllipseElementFactory, new RectangleElementFactory, new LineElementFactory);
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{ provide: DrawingService, useValue: mockedDrawingService },
|
||||||
|
{ provide: DrawingsDataSource, useValue: mockedDrawingsDataSource },
|
||||||
|
{ provide: DrawingsEventSource, useValue: mockedDrawingsEventSource },
|
||||||
|
{ provide: DefaultDrawingsFactory, useValue: mockedDrawingsFactory },
|
||||||
|
{ provide: MapDrawingToSvgConverter, useClass: MapDrawingToSvgConverter }
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
DrawingAddedComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DrawingAddedComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call drawing service when point to add drawing selected', () => {
|
||||||
|
component.project = { project_id: "sampleId" } as Project;
|
||||||
|
component.selectedDrawing = "rectangle";
|
||||||
|
const pointToAddSelectedDataEvent = new AddedDataEvent(0, 0);
|
||||||
|
spyOn(mockedDrawingService, 'add').and.returnValue( Observable.of({}));
|
||||||
|
|
||||||
|
mockedDrawingsEventSource.pointToAddSelected.emit(pointToAddSelectedDataEvent);
|
||||||
|
|
||||||
|
expect(mockedDrawingService.add).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,63 @@
|
|||||||
|
import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, SimpleChange } from "@angular/core";
|
||||||
|
import { DrawingsDataSource } from '../../../cartography/datasources/drawings-datasource';
|
||||||
|
import { DrawingsEventSource } from '../../../cartography/events/drawings-event-source';
|
||||||
|
import { DefaultDrawingsFactory } from '../../../cartography/helpers/default-drawings-factory';
|
||||||
|
import { Server } from '../../../models/server';
|
||||||
|
import { Project } from '../../../models/project';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
import { Drawing } from '../../../cartography/models/drawing';
|
||||||
|
import { MapDrawingToSvgConverter } from '../../../cartography/converters/map/map-drawing-to-svg-converter';
|
||||||
|
import { AddedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-drawing-added',
|
||||||
|
templateUrl: './drawing-added.component.html',
|
||||||
|
styleUrls: ['./drawing-added.component.css']
|
||||||
|
})
|
||||||
|
export class DrawingAddedComponent implements OnInit, OnDestroy{
|
||||||
|
@Input() server: Server;
|
||||||
|
@Input() project: Project;
|
||||||
|
@Input() selectedDrawing: string;
|
||||||
|
@Output() drawingSaved = new EventEmitter<boolean>();
|
||||||
|
private pointToAddSelected: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private drawingService: DrawingService,
|
||||||
|
private drawingsDataSource: DrawingsDataSource,
|
||||||
|
private drawingsEventSource: DrawingsEventSource,
|
||||||
|
private drawingsFactory: DefaultDrawingsFactory,
|
||||||
|
private mapDrawingToSvgConverter: MapDrawingToSvgConverter
|
||||||
|
){}
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
this.pointToAddSelected = this.drawingsEventSource.pointToAddSelected.subscribe((evt) => this.onDrawingSaved(evt));
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: { [propKey: string]: SimpleChange }) {
|
||||||
|
if(changes['selectedDrawing'] && !changes['selectedDrawing'].isFirstChange()){
|
||||||
|
this.selectedDrawing = changes['selectedDrawing'].currentValue;
|
||||||
|
|
||||||
|
if(this.selectedDrawing!=="text"){
|
||||||
|
this.drawingsEventSource.selected.emit(this.selectedDrawing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDrawingSaved(evt: AddedDataEvent){
|
||||||
|
let drawing = this.drawingsFactory.getDrawingMock(this.selectedDrawing);
|
||||||
|
let svgText = this.mapDrawingToSvgConverter.convert(drawing);
|
||||||
|
|
||||||
|
this.drawingService
|
||||||
|
.add(this.server, this.project.project_id, evt.x, evt.y, svgText)
|
||||||
|
.subscribe((serverDrawing: Drawing) => {
|
||||||
|
this.drawingsDataSource.add(serverDrawing);
|
||||||
|
this.drawingSaved.emit(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(){
|
||||||
|
this.pointToAddSelected.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
import { DrawingDraggedComponent } from "./drawing-dragged.component";
|
||||||
|
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
||||||
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
import { MockedDrawingService, MockedDrawingsDataSource } from '../../project-map/project-map.component.spec';
|
||||||
|
import { DrawingsDataSource } from '../../../cartography/datasources/drawings-datasource';
|
||||||
|
import { DrawingsEventSource } from '../../../cartography/events/drawings-event-source';
|
||||||
|
import { MapDrawing } from '../../../cartography/models/map/map-drawing';
|
||||||
|
import { DraggedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { DrawingElement } from '../../../cartography/models/drawings/drawing-element';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
describe('DrawingDraggedComponent', () => {
|
||||||
|
let component: DrawingDraggedComponent;
|
||||||
|
let fixture: ComponentFixture<DrawingDraggedComponent>;
|
||||||
|
let mockedDrawingService = new MockedDrawingService;
|
||||||
|
let mockedDrawingsDataSource = new MockedDrawingsDataSource;
|
||||||
|
let mockedDrawingsEventSource = new DrawingsEventSource;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{ provide: DrawingService, useValue: mockedDrawingService },
|
||||||
|
{ provide: DrawingsDataSource, useValue: mockedDrawingsDataSource },
|
||||||
|
{ provide: DrawingsEventSource, useValue: mockedDrawingsEventSource }
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
DrawingDraggedComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DrawingDraggedComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call drawing service when drawing is dragged', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
const mapDrawingElement: DrawingElement = {
|
||||||
|
width: 100,
|
||||||
|
height: 100
|
||||||
|
};
|
||||||
|
const mapDrawing: MapDrawing = {
|
||||||
|
id: "sampleId",
|
||||||
|
projectId: "sampleprojectId",
|
||||||
|
rotation: 0,
|
||||||
|
svg: "sampleSvg",
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0,
|
||||||
|
element: mapDrawingElement
|
||||||
|
};
|
||||||
|
const drawingDraggedDataEvent = new DraggedDataEvent<MapDrawing>(mapDrawing, 0, 0);
|
||||||
|
spyOn(mockedDrawingService, 'updatePosition').and.returnValue( Observable.of({}));
|
||||||
|
|
||||||
|
mockedDrawingsEventSource.dragged.emit(drawingDraggedDataEvent);
|
||||||
|
|
||||||
|
expect(mockedDrawingService.updatePosition).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,46 @@
|
|||||||
|
import { Component, Input, OnInit, OnDestroy } from "@angular/core";
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { DrawingsDataSource } from '../../../cartography/datasources/drawings-datasource';
|
||||||
|
import { Server } from '../../../models/server';
|
||||||
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
import { DraggedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { MapDrawing } from '../../../cartography/models/map/map-drawing';
|
||||||
|
import { Drawing } from '../../../cartography/models/drawing';
|
||||||
|
import { DrawingsEventSource } from '../../../cartography/events/drawings-event-source';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-drawing-dragged',
|
||||||
|
templateUrl: './drawing-dragged.component.html',
|
||||||
|
styleUrls: ['./drawing-dragged.component.css']
|
||||||
|
})
|
||||||
|
export class DrawingDraggedComponent implements OnInit, OnDestroy{
|
||||||
|
@Input() server: Server;
|
||||||
|
private drawingDragged: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private drawingService: DrawingService,
|
||||||
|
private drawingsDataSource: DrawingsDataSource,
|
||||||
|
private drawingsEventSource: DrawingsEventSource
|
||||||
|
){}
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
this.drawingDragged = this.drawingsEventSource.dragged.subscribe((evt) => this.onDrawingDragged(evt));
|
||||||
|
}
|
||||||
|
|
||||||
|
onDrawingDragged(draggedEvent: DraggedDataEvent<MapDrawing>) {
|
||||||
|
const drawing = this.drawingsDataSource.get(draggedEvent.datum.id);
|
||||||
|
drawing.x += draggedEvent.dx;
|
||||||
|
drawing.y += draggedEvent.dy;
|
||||||
|
|
||||||
|
this.drawingService
|
||||||
|
.updatePosition(this.server, drawing, drawing.x, drawing.y)
|
||||||
|
.subscribe((serverDrawing: Drawing) => {
|
||||||
|
this.drawingsDataSource.update(serverDrawing);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(){
|
||||||
|
this.drawingDragged.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
import { DrawingResizedComponent } from "./drawing-resized.component";
|
||||||
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
import { MockedDrawingService, MockedDrawingsDataSource } from '../../project-map/project-map.component.spec';
|
||||||
|
import { DrawingsDataSource } from '../../../cartography/datasources/drawings-datasource';
|
||||||
|
import { DrawingsEventSource } from '../../../cartography/events/drawings-event-source';
|
||||||
|
import { MapDrawingToSvgConverter } from '../../../cartography/converters/map/map-drawing-to-svg-converter';
|
||||||
|
import { ResizedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { MapDrawing } from '../../../cartography/models/map/map-drawing';
|
||||||
|
import { DrawingElement } from '../../../cartography/models/drawings/drawing-element';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
describe('DrawingResizedComponent', () => {
|
||||||
|
let component: DrawingResizedComponent;
|
||||||
|
let fixture: ComponentFixture<DrawingResizedComponent>;
|
||||||
|
let mockedDrawingService = new MockedDrawingService;
|
||||||
|
let mockedDrawingsDataSource = new MockedDrawingsDataSource;
|
||||||
|
let mockedDrawingsEventSource = new DrawingsEventSource;
|
||||||
|
let mockedMapDrawingToSvgConverter = new MapDrawingToSvgConverter;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{ provide: DrawingService, useValue: mockedDrawingService },
|
||||||
|
{ provide: DrawingsDataSource, useValue: mockedDrawingsDataSource },
|
||||||
|
{ provide: DrawingsEventSource, useValue: mockedDrawingsEventSource },
|
||||||
|
{ provide: MapDrawingToSvgConverter, useValue: mockedMapDrawingToSvgConverter }
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
DrawingResizedComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DrawingResizedComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call drawing service when drawing is resized', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
const mapDrawingElement: DrawingElement = {
|
||||||
|
width: 100,
|
||||||
|
height: 100
|
||||||
|
};
|
||||||
|
const mapDrawing: MapDrawing = {
|
||||||
|
id: "sampleId",
|
||||||
|
projectId: "sampleprojectId",
|
||||||
|
rotation: 0,
|
||||||
|
svg: "sampleSvg",
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0,
|
||||||
|
element: mapDrawingElement
|
||||||
|
};
|
||||||
|
const drawingResizedDataEvent = new ResizedDataEvent<MapDrawing>(mapDrawing, 0, 0, 100, 100);
|
||||||
|
spyOn(mockedDrawingService, 'updateSizeAndPosition').and.returnValue( Observable.of({}));
|
||||||
|
|
||||||
|
mockedDrawingsEventSource.resized.emit(drawingResizedDataEvent);
|
||||||
|
|
||||||
|
expect(mockedDrawingService.updateSizeAndPosition).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,47 @@
|
|||||||
|
import { Component, Input, OnInit, OnDestroy } from "@angular/core";
|
||||||
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
import { DrawingsDataSource } from '../../../cartography/datasources/drawings-datasource';
|
||||||
|
import { DrawingsEventSource } from '../../../cartography/events/drawings-event-source';
|
||||||
|
import { MapDrawingToSvgConverter } from '../../../cartography/converters/map/map-drawing-to-svg-converter';
|
||||||
|
import { Server } from '../../../models/server';
|
||||||
|
import { ResizedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { MapDrawing } from '../../../cartography/models/map/map-drawing';
|
||||||
|
import { Drawing } from '../../../cartography/models/drawing';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-drawing-resized',
|
||||||
|
templateUrl: './drawing-resized.component.html',
|
||||||
|
styleUrls: ['./drawing-resized.component.css']
|
||||||
|
})
|
||||||
|
export class DrawingResizedComponent implements OnInit, OnDestroy{
|
||||||
|
@Input() server: Server;
|
||||||
|
private drawingResized: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private drawingService: DrawingService,
|
||||||
|
private drawingsDataSource: DrawingsDataSource,
|
||||||
|
private drawingsEventSource: DrawingsEventSource,
|
||||||
|
private mapDrawingToSvgConverter: MapDrawingToSvgConverter
|
||||||
|
){}
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
this.drawingResized = this.drawingsEventSource.resized.subscribe((evt) => this.onDrawingResized(evt))
|
||||||
|
}
|
||||||
|
|
||||||
|
onDrawingResized(resizedEvent: ResizedDataEvent<MapDrawing>) {
|
||||||
|
const drawing = this.drawingsDataSource.get(resizedEvent.datum.id);
|
||||||
|
let svgString = this.mapDrawingToSvgConverter.convert(resizedEvent.datum);
|
||||||
|
|
||||||
|
this.drawingService
|
||||||
|
.updateSizeAndPosition(this.server, drawing, resizedEvent.x, resizedEvent.y, svgString)
|
||||||
|
.subscribe((serverDrawing: Drawing) => {
|
||||||
|
this.drawingsDataSource.update(serverDrawing);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(){
|
||||||
|
this.drawingResized.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
import { InterfaceLabelDraggedComponent } from "./interface-label-dragged.component";
|
||||||
|
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
||||||
|
import { MockedLinkService } from '../../project-map/project-map.component.spec';
|
||||||
|
import { LinksEventSource } from '../../../cartography/events/links-event-source';
|
||||||
|
import { LinkService } from '../../../services/link.service';
|
||||||
|
import { LinksDataSource } from '../../../cartography/datasources/links-datasource';
|
||||||
|
import { DraggedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { MapLinkNode } from '../../../cartography/models/map/map-link-node';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { MapLabel } from '../../../cartography/models/map/map-label';
|
||||||
|
import { Link } from '../../../models/link';
|
||||||
|
import { Label } from '../../../cartography/models/label';
|
||||||
|
|
||||||
|
describe('InterfaceLabelDraggedComponent', () => {
|
||||||
|
let component: InterfaceLabelDraggedComponent;
|
||||||
|
let fixture: ComponentFixture<InterfaceLabelDraggedComponent>;
|
||||||
|
let mockedLinkService = new MockedLinkService;
|
||||||
|
let mockedLinksEventSource = new LinksEventSource;
|
||||||
|
let mockedLinksDataSource = new LinksDataSource;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{ provide: LinkService, useValue: mockedLinkService },
|
||||||
|
{ provide: LinksDataSource, useValue: mockedLinksDataSource },
|
||||||
|
{ provide: LinksEventSource, useValue: mockedLinksEventSource }
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
InterfaceLabelDraggedComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(InterfaceLabelDraggedComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call link service when interface label dragged', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
const mapLinkNode: MapLinkNode = {
|
||||||
|
id: 'sampleId',
|
||||||
|
nodeId: 'sampleNodeId',
|
||||||
|
linkId: 'sampleLinkId',
|
||||||
|
adapterNumber: 0,
|
||||||
|
portNumber: 0,
|
||||||
|
label: {} as MapLabel
|
||||||
|
};
|
||||||
|
const interfaceLabelDraggedDataEvent = new DraggedDataEvent<MapLinkNode>(mapLinkNode, 0, 0);
|
||||||
|
|
||||||
|
let link: Link = {} as Link;
|
||||||
|
link.nodes = [
|
||||||
|
{
|
||||||
|
node_id: "1",
|
||||||
|
adapter_number: 0,
|
||||||
|
port_number: 0,
|
||||||
|
label: {} as Label
|
||||||
|
},
|
||||||
|
{
|
||||||
|
node_id: "2",
|
||||||
|
adapter_number: 0,
|
||||||
|
port_number: 0,
|
||||||
|
label: {} as Label
|
||||||
|
}
|
||||||
|
];
|
||||||
|
mockedLinksDataSource = TestBed.get(LinksDataSource);
|
||||||
|
|
||||||
|
spyOn(mockedLinksDataSource, 'get').and.returnValue(link);
|
||||||
|
spyOn(mockedLinkService, 'updateNodes').and.returnValue( Observable.of({}));
|
||||||
|
|
||||||
|
mockedLinksEventSource.interfaceDragged.emit(interfaceLabelDraggedDataEvent);
|
||||||
|
|
||||||
|
expect(mockedLinkService.updateNodes).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,52 @@
|
|||||||
|
import { Component, Input } from "@angular/core";
|
||||||
|
import { Server } from '../../../models/server';
|
||||||
|
import { LinksDataSource } from '../../../cartography/datasources/links-datasource';
|
||||||
|
import { LinkService } from '../../../services/link.service';
|
||||||
|
import { DraggedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { MapLinkNode } from '../../../cartography/models/map/map-link-node';
|
||||||
|
import { Link } from '../../../models/link';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { LinksEventSource } from '../../../cartography/events/links-event-source';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-interface-label-dragged',
|
||||||
|
templateUrl: './interface-label-dragged.component.html',
|
||||||
|
styleUrls: ['./interface-label-dragged.component.css']
|
||||||
|
})
|
||||||
|
export class InterfaceLabelDraggedComponent{
|
||||||
|
@Input() server: Server;
|
||||||
|
private interfaceDragged: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private linkService: LinkService,
|
||||||
|
private linksDataSource: LinksDataSource,
|
||||||
|
private linksEventSource: LinksEventSource
|
||||||
|
){}
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
this.interfaceDragged = this.linksEventSource.interfaceDragged.subscribe((evt) => this.onInterfaceLabelDragged(evt));
|
||||||
|
}
|
||||||
|
|
||||||
|
onInterfaceLabelDragged(draggedEvent: DraggedDataEvent<MapLinkNode>) {
|
||||||
|
const link = this.linksDataSource.get(draggedEvent.datum.linkId);
|
||||||
|
if (link.nodes[0].node_id === draggedEvent.datum.nodeId) {
|
||||||
|
link.nodes[0].label.x += draggedEvent.dx;
|
||||||
|
link.nodes[0].label.y += draggedEvent.dy;
|
||||||
|
}
|
||||||
|
if (link.nodes[1].node_id === draggedEvent.datum.nodeId) {
|
||||||
|
link.nodes[1].label.x += draggedEvent.dx;
|
||||||
|
link.nodes[1].label.y += draggedEvent.dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.linkService
|
||||||
|
.updateNodes(this.server, link, link.nodes)
|
||||||
|
.subscribe((serverLink: Link) => {
|
||||||
|
this.linksDataSource.update(serverLink);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(){
|
||||||
|
this.interfaceDragged.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
import { LinkCreatedComponent } from "./link-created.component";
|
||||||
|
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
||||||
|
import { ProjectService } from '../../../services/project.service';
|
||||||
|
import { MockedProjectService } from '../../../services/project.service.spec';
|
||||||
|
import { LinkService } from '../../../services/link.service';
|
||||||
|
import { LinksDataSource } from '../../../cartography/datasources/links-datasource';
|
||||||
|
import { LinksEventSource } from '../../../cartography/events/links-event-source';
|
||||||
|
import { MapNodeToNodeConverter } from '../../../cartography/converters/map/map-node-to-node-converter';
|
||||||
|
import { MapPortToPortConverter } from '../../../cartography/converters/map/map-port-to-port-converter';
|
||||||
|
import { MockedLinkService } from '../../project-map/project-map.component.spec';
|
||||||
|
import { MapLabelToLabelConverter } from '../../../cartography/converters/map/map-label-to-label-converter';
|
||||||
|
import { FontBBoxCalculator } from '../../../cartography/helpers/font-bbox-calculator';
|
||||||
|
import { CssFixer } from '../../../cartography/helpers/css-fixer';
|
||||||
|
import { FontFixer } from '../../../cartography/helpers/font-fixer';
|
||||||
|
import { MapLinkCreated } from '../../../cartography/events/links';
|
||||||
|
import { MapNode } from '../../../cartography/models/map/map-node';
|
||||||
|
import { MapLabel } from '../../../cartography/models/map/map-label';
|
||||||
|
import { MapPort } from '../../../cartography/models/map/map-port';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { Project } from '../../../models/project';
|
||||||
|
|
||||||
|
describe('LinkCreatedComponent', () => {
|
||||||
|
let component: LinkCreatedComponent;
|
||||||
|
let fixture: ComponentFixture<LinkCreatedComponent>;
|
||||||
|
let mockedLinkService = new MockedLinkService;
|
||||||
|
let mockedMapNodeToNodeConverter: MapNodeToNodeConverter =
|
||||||
|
new MapNodeToNodeConverter(new MapLabelToLabelConverter(new FontBBoxCalculator, new CssFixer, new FontFixer), new MapPortToPortConverter);
|
||||||
|
let mockedLinksEventSource = new LinksEventSource;
|
||||||
|
let project = new Project();
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{ provide: ProjectService, useClass: MockedProjectService },
|
||||||
|
{ provide: LinkService, useValue: mockedLinkService },
|
||||||
|
{ provide: LinksDataSource, useClass: LinksDataSource },
|
||||||
|
{ provide: LinksEventSource, useValue: mockedLinksEventSource },
|
||||||
|
{ provide: MapNodeToNodeConverter, useValue: mockedMapNodeToNodeConverter },
|
||||||
|
{ provide: MapPortToPortConverter, useClass: MapPortToPortConverter }
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
LinkCreatedComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LinkCreatedComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
|
project.project_id = "sampleId";
|
||||||
|
component.project = project;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call link service when link created', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
const mapNode: MapNode = {
|
||||||
|
id: 'sampleId',
|
||||||
|
commandLine: 'sampleCommandLine',
|
||||||
|
computeId: 'sampleComputeId',
|
||||||
|
console: 0,
|
||||||
|
consoleHost: 'sampleConsoleHost',
|
||||||
|
consoleType: 'sampleConsoleType',
|
||||||
|
firstPortName: 'sampleFirstPortName',
|
||||||
|
height: 0,
|
||||||
|
label: {} as MapLabel,
|
||||||
|
name: "sampleName",
|
||||||
|
nodeDirectory: "sampleNodeDirectory",
|
||||||
|
nodeType: "sampleNodeType",
|
||||||
|
portNameFormat: "samplePortNameFormat",
|
||||||
|
portSegmentSize: 0,
|
||||||
|
ports: [],
|
||||||
|
projectId: "sampleProjectId",
|
||||||
|
status: "sampleStatus",
|
||||||
|
symbol: "sampleSymbol",
|
||||||
|
symbolUrl: "sampleUrl",
|
||||||
|
width: 0,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
};
|
||||||
|
const mapPort: MapPort = {
|
||||||
|
adapterNumber: 1,
|
||||||
|
linkType: 'sampleLinkType',
|
||||||
|
name: 'sampleName',
|
||||||
|
portNumber: 1,
|
||||||
|
shortName: 'sampleShortName'
|
||||||
|
};
|
||||||
|
const linkCreatedDataEvent = new MapLinkCreated(mapNode, mapPort, mapNode, mapPort);
|
||||||
|
spyOn(mockedLinkService, 'createLink').and.returnValue( Observable.of({}));
|
||||||
|
|
||||||
|
mockedLinksEventSource.created.emit(linkCreatedDataEvent);
|
||||||
|
|
||||||
|
expect(mockedLinkService.createLink).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,56 @@
|
|||||||
|
import { Component, OnDestroy, Input, OnInit } from "@angular/core";
|
||||||
|
import { Server } from '../../../models/server';
|
||||||
|
import { LinkService } from '../../../services/link.service';
|
||||||
|
import { ProjectService } from '../../../services/project.service';
|
||||||
|
import { MapNodeToNodeConverter } from '../../../cartography/converters/map/map-node-to-node-converter';
|
||||||
|
import { MapPortToPortConverter } from '../../../cartography/converters/map/map-port-to-port-converter';
|
||||||
|
import { LinksDataSource } from '../../../cartography/datasources/links-datasource';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { Project } from '../../../models/project';
|
||||||
|
import { MapLinkCreated } from '../../../cartography/events/links';
|
||||||
|
import { Link } from "../../../models/link";
|
||||||
|
import { LinksEventSource } from '../../../cartography/events/links-event-source';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-link-created',
|
||||||
|
templateUrl: './link-created.component.html',
|
||||||
|
styleUrls: ['./link-created.component.css']
|
||||||
|
})
|
||||||
|
export class LinkCreatedComponent implements OnInit, OnDestroy{
|
||||||
|
@Input() server: Server;
|
||||||
|
@Input() project: Project;
|
||||||
|
private linkCreated: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private projectService: ProjectService,
|
||||||
|
private linkService: LinkService,
|
||||||
|
private linksDataSource: LinksDataSource,
|
||||||
|
private linksEventSource: LinksEventSource,
|
||||||
|
private mapNodeToNode: MapNodeToNodeConverter,
|
||||||
|
private mapPortToPort: MapPortToPortConverter
|
||||||
|
){}
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
this.linkCreated = this.linksEventSource.created.subscribe((evt) => this.onLinkCreated(evt));
|
||||||
|
}
|
||||||
|
|
||||||
|
onLinkCreated(linkCreated: MapLinkCreated) {
|
||||||
|
const sourceNode = this.mapNodeToNode.convert(linkCreated.sourceNode);
|
||||||
|
const sourcePort = this.mapPortToPort.convert(linkCreated.sourcePort);
|
||||||
|
const targetNode = this.mapNodeToNode.convert(linkCreated.targetNode);
|
||||||
|
const targetPort = this.mapPortToPort.convert(linkCreated.targetPort);
|
||||||
|
|
||||||
|
this.linkService
|
||||||
|
.createLink(this.server, sourceNode, sourcePort, targetNode, targetPort)
|
||||||
|
.subscribe(() => {
|
||||||
|
this.projectService.links(this.server, this.project.project_id).subscribe((links: Link[]) => {
|
||||||
|
this.linksDataSource.set(links);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(){
|
||||||
|
this.linkCreated.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
import { NodeDraggedComponent } from "./node-dragged.component";
|
||||||
|
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
||||||
|
import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource';
|
||||||
|
import { NodeService } from '../../../services/node.service';
|
||||||
|
import { NodesEventSource } from '../../../cartography/events/nodes-event-source';
|
||||||
|
import { MockedNodeService, MockedNodesDataSource } from '../../project-map/project-map.component.spec';
|
||||||
|
import { DraggedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { MapNode } from '../../../cartography/models/map/map-node';
|
||||||
|
import { MapLabel } from '../../../cartography/models/map/map-label';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
describe('NodeDraggedComponent', () => {
|
||||||
|
let component: NodeDraggedComponent;
|
||||||
|
let fixture: ComponentFixture<NodeDraggedComponent>;
|
||||||
|
let mockedNodesDataSource = new MockedNodesDataSource;
|
||||||
|
let mockedNodeService = new MockedNodeService;
|
||||||
|
let mockedNodesEventSource = new NodesEventSource;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{ provide: NodesDataSource, useValue: mockedNodesDataSource },
|
||||||
|
{ provide: NodeService, useValue: mockedNodeService },
|
||||||
|
{ provide: NodesEventSource, useValue: mockedNodesEventSource }
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
NodeDraggedComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(NodeDraggedComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call node service when node dragged', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
const mapNode: MapNode = {
|
||||||
|
id: 'sampleId',
|
||||||
|
commandLine: 'sampleCommandLine',
|
||||||
|
computeId: 'sampleComputeId',
|
||||||
|
console: 0,
|
||||||
|
consoleHost: 'sampleConsoleHost',
|
||||||
|
consoleType: 'sampleConsoleType',
|
||||||
|
firstPortName: 'sampleFirstPortName',
|
||||||
|
height: 0,
|
||||||
|
label: {} as MapLabel,
|
||||||
|
name: "sampleName",
|
||||||
|
nodeDirectory: "sampleNodeDirectory",
|
||||||
|
nodeType: "sampleNodeType",
|
||||||
|
portNameFormat: "samplePortNameFormat",
|
||||||
|
portSegmentSize: 0,
|
||||||
|
ports: [],
|
||||||
|
projectId: "sampleProjectId",
|
||||||
|
status: "sampleStatus",
|
||||||
|
symbol: "sampleSymbol",
|
||||||
|
symbolUrl: "sampleUrl",
|
||||||
|
width: 0,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0
|
||||||
|
};
|
||||||
|
const draggedDataEvent = new DraggedDataEvent<MapNode>(mapNode, 0, 0);
|
||||||
|
spyOn(mockedNodeService, 'updatePosition').and.returnValue( Observable.of({}));
|
||||||
|
|
||||||
|
mockedNodesEventSource.dragged.emit(draggedDataEvent);
|
||||||
|
|
||||||
|
expect(mockedNodeService.updatePosition).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,46 @@
|
|||||||
|
import { Component, Input, OnInit, OnDestroy } from "@angular/core";
|
||||||
|
import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource';
|
||||||
|
import { NodeService } from '../../../services/node.service';
|
||||||
|
import { DraggedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { Node } from '../../../cartography/models/node';
|
||||||
|
import { Server } from '../../../models/server';
|
||||||
|
import { NodesEventSource } from '../../../cartography/events/nodes-event-source';
|
||||||
|
import { MapNode } from '../../../cartography/models/map/map-node';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-node-dragged',
|
||||||
|
templateUrl: './node-dragged.component.html',
|
||||||
|
styleUrls: ['./node-dragged.component.css']
|
||||||
|
})
|
||||||
|
export class NodeDraggedComponent implements OnInit, OnDestroy{
|
||||||
|
@Input() server: Server;
|
||||||
|
private nodeDragged: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private nodesDataSource: NodesDataSource,
|
||||||
|
private nodeService: NodeService,
|
||||||
|
private nodesEventSource: NodesEventSource
|
||||||
|
){}
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
this.nodeDragged = this.nodesEventSource.dragged.subscribe((evt) => this.onNodeDragged(evt));
|
||||||
|
}
|
||||||
|
|
||||||
|
onNodeDragged(draggedEvent: DraggedDataEvent<MapNode>) {
|
||||||
|
const node = this.nodesDataSource.get(draggedEvent.datum.id);
|
||||||
|
node.x += draggedEvent.dx;
|
||||||
|
node.y += draggedEvent.dy;
|
||||||
|
|
||||||
|
this.nodeService
|
||||||
|
.updatePosition(this.server, node, node.x, node.y)
|
||||||
|
.subscribe((serverNode: Node) => {
|
||||||
|
this.nodesDataSource.update(serverNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(){
|
||||||
|
this.nodeDragged.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
import { NodeLabelDraggedComponent } from "./node-label-dragged.component";
|
||||||
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
|
import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource';
|
||||||
|
import { NodeService } from '../../../services/node.service';
|
||||||
|
import { MapLabelToLabelConverter } from '../../../cartography/converters/map/map-label-to-label-converter';
|
||||||
|
import { NodesEventSource } from '../../../cartography/events/nodes-event-source';
|
||||||
|
import { CssFixer } from '../../../cartography/helpers/css-fixer';
|
||||||
|
import { FontFixer } from '../../../cartography/helpers/font-fixer';
|
||||||
|
import { FontBBoxCalculator } from '../../../cartography/helpers/font-bbox-calculator';
|
||||||
|
import { MockedNodeService, MockedNodesDataSource } from '../../project-map/project-map.component.spec';
|
||||||
|
import { DraggedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { MapLabel } from '../../../cartography/models/map/map-label';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
describe('NodeLabelDraggedComponent', () => {
|
||||||
|
let component: NodeLabelDraggedComponent;
|
||||||
|
let fixture: ComponentFixture<NodeLabelDraggedComponent>;
|
||||||
|
let mockedNodesDataSource = new MockedNodesDataSource;
|
||||||
|
let mockedNodeService = new MockedNodeService;
|
||||||
|
let mockedNodesEventSource = new NodesEventSource;
|
||||||
|
let mapLabelToLabelConverter = new MapLabelToLabelConverter(
|
||||||
|
new FontBBoxCalculator,
|
||||||
|
new CssFixer,
|
||||||
|
new FontFixer
|
||||||
|
);
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{ provide: NodesDataSource, useValue: mockedNodesDataSource },
|
||||||
|
{ provide: NodeService, useValue: mockedNodeService },
|
||||||
|
{ provide: NodesEventSource, useValue: mockedNodesEventSource },
|
||||||
|
{ provide: MapLabelToLabelConverter, useValue: mapLabelToLabelConverter }
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
NodeLabelDraggedComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(NodeLabelDraggedComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call node service when node label dragged', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
const mapLabel: MapLabel = {
|
||||||
|
id: "sample id",
|
||||||
|
rotation: 0,
|
||||||
|
style: "sample style",
|
||||||
|
text: "sample text",
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
originalX: 0,
|
||||||
|
originalY: 0,
|
||||||
|
nodeId: "node id"
|
||||||
|
};
|
||||||
|
const nodeDraggedDataEvent = new DraggedDataEvent<MapLabel>(mapLabel, 0, 0);
|
||||||
|
spyOn(mockedNodeService, 'updateLabel').and.returnValue( Observable.of({}));
|
||||||
|
|
||||||
|
mockedNodesEventSource.labelDragged.emit(nodeDraggedDataEvent);
|
||||||
|
|
||||||
|
expect(mockedNodeService.updateLabel).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,52 @@
|
|||||||
|
import { Component, Input, OnInit, OnDestroy } from "@angular/core";
|
||||||
|
import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource';
|
||||||
|
import { NodesEventSource } from '../../../cartography/events/nodes-event-source';
|
||||||
|
import { NodeService } from '../../../services/node.service';
|
||||||
|
import { MapLabelToLabelConverter } from '../../../cartography/converters/map/map-label-to-label-converter';
|
||||||
|
import { Node } from '../../../cartography/models/node';
|
||||||
|
import { Server } from '../../../models/server';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { DraggedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { MapLabel } from '../../../cartography/models/map/map-label';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-node-label-dragged',
|
||||||
|
templateUrl: './node-label-dragged.component.html',
|
||||||
|
styleUrls: ['./node-label-dragged.component.css']
|
||||||
|
})
|
||||||
|
export class NodeLabelDraggedComponent implements OnInit, OnDestroy{
|
||||||
|
@Input() server: Server;
|
||||||
|
private nodeLabelDragged: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private nodesDataSource: NodesDataSource,
|
||||||
|
private nodeService: NodeService,
|
||||||
|
private nodesEventSource: NodesEventSource,
|
||||||
|
private mapLabelToLabel: MapLabelToLabelConverter
|
||||||
|
){}
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
this.nodeLabelDragged = this.nodesEventSource.labelDragged.subscribe((evt) => this.onNodeLabelDragged(evt));
|
||||||
|
}
|
||||||
|
|
||||||
|
onNodeLabelDragged(draggedEvent: DraggedDataEvent<MapLabel>) {
|
||||||
|
const node = this.nodesDataSource.get(draggedEvent.datum.nodeId);
|
||||||
|
const mapLabel = draggedEvent.datum;
|
||||||
|
mapLabel.x += draggedEvent.dx;
|
||||||
|
mapLabel.y += draggedEvent.dy;
|
||||||
|
|
||||||
|
const label = this.mapLabelToLabel.convert(mapLabel);
|
||||||
|
node.label = label;
|
||||||
|
|
||||||
|
this.nodeService
|
||||||
|
.updateLabel(this.server, node, node.label)
|
||||||
|
.subscribe((serverNode: Node) => {
|
||||||
|
this.nodesDataSource.update(serverNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(){
|
||||||
|
this.nodeLabelDragged.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
import { TextAddedComponent } from "./text-added.component";
|
||||||
|
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
||||||
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
import { DrawingsDataSource } from '../../../cartography/datasources/drawings-datasource';
|
||||||
|
import { DrawingsEventSource } from '../../../cartography/events/drawings-event-source';
|
||||||
|
import { MapDrawingToSvgConverter } from '../../../cartography/converters/map/map-drawing-to-svg-converter';
|
||||||
|
import { MockedDrawingService, MockedDrawingsDataSource } from '../../project-map/project-map.component.spec';
|
||||||
|
import { DefaultDrawingsFactory } from '../../../cartography/helpers/default-drawings-factory';
|
||||||
|
import { Context } from '../../../cartography/models/context';
|
||||||
|
import { TextAddedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { TextElementFactory } from '../../../cartography/helpers/drawings-factory/text-element-factory';
|
||||||
|
import { EllipseElementFactory } from '../../../cartography/helpers/drawings-factory/ellipse-element-factory';
|
||||||
|
import { RectangleElementFactory } from '../../../cartography/helpers/drawings-factory/rectangle-element-factory';
|
||||||
|
import { LineElementFactory } from '../../../cartography/helpers/drawings-factory/line-element-factory';
|
||||||
|
import { Project } from '../../../models/project';
|
||||||
|
|
||||||
|
describe('TextAddedComponent', () => {
|
||||||
|
let component: TextAddedComponent;
|
||||||
|
let fixture: ComponentFixture<TextAddedComponent>;
|
||||||
|
let mockedDrawingService = new MockedDrawingService;
|
||||||
|
let mockedDrawingsDataSource = new MockedDrawingsDataSource;
|
||||||
|
let mockedDrawingsEventSource = new DrawingsEventSource;
|
||||||
|
let mockedDrawingsFactory = new DefaultDrawingsFactory(new TextElementFactory,
|
||||||
|
new EllipseElementFactory, new RectangleElementFactory, new LineElementFactory);
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{ provide: DrawingService, useValue: mockedDrawingService },
|
||||||
|
{ provide: DrawingsDataSource, useValue: mockedDrawingsDataSource },
|
||||||
|
{ provide: DrawingsEventSource, useValue: mockedDrawingsEventSource },
|
||||||
|
{ provide: DefaultDrawingsFactory, useValue: mockedDrawingsFactory },
|
||||||
|
{ provide: MapDrawingToSvgConverter, useClass: MapDrawingToSvgConverter },
|
||||||
|
{ provide: Context, useClass: Context}
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
TextAddedComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(TextAddedComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call drawing service when text added', () => {
|
||||||
|
component.project = { project_id: "sampleId" } as Project;
|
||||||
|
const textAddedDataEvent = new TextAddedDataEvent("savedText", 0 ,0);
|
||||||
|
spyOn(mockedDrawingService, 'add').and.returnValue( Observable.of({}));
|
||||||
|
|
||||||
|
mockedDrawingsEventSource.textAdded.emit(textAddedDataEvent);
|
||||||
|
|
||||||
|
expect(mockedDrawingService.add).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,56 @@
|
|||||||
|
import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from "@angular/core";
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
import { DrawingsDataSource } from '../../../cartography/datasources/drawings-datasource';
|
||||||
|
import { DrawingsEventSource } from '../../../cartography/events/drawings-event-source';
|
||||||
|
import { MapDrawingToSvgConverter } from '../../../cartography/converters/map/map-drawing-to-svg-converter';
|
||||||
|
import { TextAddedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { DefaultDrawingsFactory } from '../../../cartography/helpers/default-drawings-factory';
|
||||||
|
import { TextElement } from '../../../cartography/models/drawings/text-element';
|
||||||
|
import { Server } from '../../../models/server';
|
||||||
|
import { Project } from '../../../models/project';
|
||||||
|
import { Drawing } from '../../../cartography/models/drawing';
|
||||||
|
import { Context } from '../../../cartography/models/context';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-text-added',
|
||||||
|
templateUrl: './text-added.component.html',
|
||||||
|
styleUrls: ['./text-added.component.css']
|
||||||
|
})
|
||||||
|
export class TextAddedComponent implements OnInit, OnDestroy{
|
||||||
|
@Input() server: Server;
|
||||||
|
@Input() project: Project;
|
||||||
|
@Output() drawingSaved = new EventEmitter<boolean>();
|
||||||
|
private textAdded: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private drawingService: DrawingService,
|
||||||
|
private drawingsDataSource: DrawingsDataSource,
|
||||||
|
private drawingsEventSource: DrawingsEventSource,
|
||||||
|
private drawingsFactory: DefaultDrawingsFactory,
|
||||||
|
private mapDrawingToSvgConverter: MapDrawingToSvgConverter,
|
||||||
|
private context: Context
|
||||||
|
){}
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
this.textAdded = this.drawingsEventSource.textAdded.subscribe((evt) => this.onTextAdded(evt));
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextAdded(evt: TextAddedDataEvent){
|
||||||
|
let drawing = this.drawingsFactory.getDrawingMock("text");
|
||||||
|
(drawing.element as TextElement).text = evt.savedText;
|
||||||
|
let svgText = this.mapDrawingToSvgConverter.convert(drawing);
|
||||||
|
|
||||||
|
this.drawingService
|
||||||
|
.add(this.server, this.project.project_id, evt.x - this.context.getZeroZeroTransformationPoint().x, evt.y - this.context.getZeroZeroTransformationPoint().y, svgText)
|
||||||
|
.subscribe((serverDrawing: Drawing) => {
|
||||||
|
this.drawingsDataSource.add(serverDrawing);
|
||||||
|
this.drawingSaved.emit(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(){
|
||||||
|
this.textAdded.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
import { TextEditedComponent } from "./text-edited.component";
|
||||||
|
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||||
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
import { DrawingsDataSource } from '../../../cartography/datasources/drawings-datasource';
|
||||||
|
import { DrawingsEventSource } from '../../../cartography/events/drawings-event-source';
|
||||||
|
import { MapDrawingToSvgConverter } from '../../../cartography/converters/map/map-drawing-to-svg-converter';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { TextEditedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { TextElement } from '../../../cartography/models/drawings/text-element';
|
||||||
|
import { MockedDrawingService, MockedDrawingsDataSource } from '../../project-map/project-map.component.spec';
|
||||||
|
|
||||||
|
describe('TextEditedComponent', () => {
|
||||||
|
let component: TextEditedComponent;
|
||||||
|
let fixture: ComponentFixture<TextEditedComponent>;
|
||||||
|
let mockedDrawingService = new MockedDrawingService;
|
||||||
|
let mockedDrawingsDataSource = new MockedDrawingsDataSource;
|
||||||
|
let mockedDrawingsEventSource = new DrawingsEventSource;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
{ provide: DrawingService, useValue: mockedDrawingService },
|
||||||
|
{ provide: DrawingsDataSource, useValue: mockedDrawingsDataSource },
|
||||||
|
{ provide: DrawingsEventSource, useValue: mockedDrawingsEventSource },
|
||||||
|
{ provide: MapDrawingToSvgConverter, useClass: MapDrawingToSvgConverter }
|
||||||
|
],
|
||||||
|
declarations: [
|
||||||
|
TextEditedComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(TextEditedComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call drawing service when text edited', () => {
|
||||||
|
const textElement: TextElement = {
|
||||||
|
height: 100,
|
||||||
|
width: 100,
|
||||||
|
text: "sample text",
|
||||||
|
fill: "fill",
|
||||||
|
fill_opacity: 100,
|
||||||
|
font_family: "font",
|
||||||
|
font_size: 100,
|
||||||
|
font_weight: "bold",
|
||||||
|
text_decoration: "sample decoration"
|
||||||
|
};
|
||||||
|
const textEditedDataEvent = new TextEditedDataEvent("id", "edited text", textElement);
|
||||||
|
spyOn(mockedDrawingService, 'updateText').and.returnValue( Observable.of({}));
|
||||||
|
|
||||||
|
mockedDrawingsEventSource.textEdited.emit(textEditedDataEvent);
|
||||||
|
|
||||||
|
expect(mockedDrawingService.updateText).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,53 @@
|
|||||||
|
import { Component, OnInit, OnDestroy, Input } from "@angular/core";
|
||||||
|
import { TextEditedDataEvent } from '../../../cartography/events/event-source';
|
||||||
|
import { MapDrawing } from '../../../cartography/models/map/map-drawing';
|
||||||
|
import { TextElement } from '../../../cartography/models/drawings/text-element';
|
||||||
|
import { Drawing } from '../../../cartography/models/drawing';
|
||||||
|
import { Server } from '../../../models/server';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { DrawingService } from '../../../services/drawing.service';
|
||||||
|
import { DrawingsDataSource } from '../../../cartography/datasources/drawings-datasource';
|
||||||
|
import { DrawingsEventSource } from '../../../cartography/events/drawings-event-source';
|
||||||
|
import { MapDrawingToSvgConverter } from '../../../cartography/converters/map/map-drawing-to-svg-converter';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-text-edited',
|
||||||
|
templateUrl: './text-edited.component.html',
|
||||||
|
styleUrls: ['./text-edited.component.css']
|
||||||
|
})
|
||||||
|
export class TextEditedComponent implements OnInit, OnDestroy{
|
||||||
|
@Input() server: Server;
|
||||||
|
private textEdited: Subscription;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private drawingService: DrawingService,
|
||||||
|
private drawingsDataSource: DrawingsDataSource,
|
||||||
|
private drawingsEventSource: DrawingsEventSource,
|
||||||
|
private mapDrawingToSvgConverter: MapDrawingToSvgConverter
|
||||||
|
){}
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
this.textEdited = this.drawingsEventSource.textEdited.subscribe((evt) => this.onTextEdited(evt));
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextEdited(evt: TextEditedDataEvent){
|
||||||
|
let mapDrawing: MapDrawing = new MapDrawing();
|
||||||
|
mapDrawing.element = evt.textElement;
|
||||||
|
(mapDrawing.element as TextElement).text = evt.editedText;
|
||||||
|
let svgString = this.mapDrawingToSvgConverter.convert(mapDrawing);
|
||||||
|
|
||||||
|
let drawing = this.drawingsDataSource.get(evt.textDrawingId);
|
||||||
|
|
||||||
|
this.drawingService
|
||||||
|
.updateText(this.server, drawing, svgString)
|
||||||
|
.subscribe((serverDrawing: Drawing) => {
|
||||||
|
this.drawingsDataSource.update(serverDrawing);
|
||||||
|
this.drawingsEventSource.textSaved.emit(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(){
|
||||||
|
this.textEdited.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -8,10 +8,6 @@
|
|||||||
[width]="project.scene_width"
|
[width]="project.scene_width"
|
||||||
[height]="project.scene_height"
|
[height]="project.scene_height"
|
||||||
[show-interface-labels]="project.show_interface_labels"
|
[show-interface-labels]="project.show_interface_labels"
|
||||||
[selection-tool]="tools.selection"
|
|
||||||
[moving-tool]="tools.moving"
|
|
||||||
[text-editing-tool]="tools.text_editing"
|
|
||||||
[draw-link-tool]="tools.draw_link"
|
|
||||||
[readonly]="inReadOnlyMode"
|
[readonly]="inReadOnlyMode"
|
||||||
(nodeDragged)="onNodeDragged($event)"
|
(nodeDragged)="onNodeDragged($event)"
|
||||||
(drawingDragged)="onDrawingDragged($event)"
|
(drawingDragged)="onDrawingDragged($event)"
|
||||||
@ -99,39 +95,30 @@
|
|||||||
</mat-toolbar>
|
</mat-toolbar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="draw-menu" *ngIf="!inReadOnlyMode">
|
<div id="show-menu-wrapper" [ngClass]="{shadowed: !drawTools.visibility}">
|
||||||
<mat-drawer-container [ngClass]="{shadow: drawTools.visibility}" class="drawer-container">
|
<button class="arrow-button" mat-icon-button (click)="showMenu()">
|
||||||
<mat-drawer #drawer class="drawer">
|
<mat-icon>keyboard_arrow_right</mat-icon>
|
||||||
<div class="drawer-buttons">
|
</button>
|
||||||
<button matTooltip="Add a note" mat-icon-button class="drawer-button" [color]="drawTools.isAddingTextChosen ? 'primary': 'basic'" (click)="addText()">
|
</div>
|
||||||
<mat-icon>create</mat-icon>
|
|
||||||
</button>
|
<div id="menu-wrapper" [ngClass]="{extended: drawTools.visibility}">
|
||||||
<button matTooltip="Draw a rectangle" mat-icon-button class="drawer-button" [color]="drawTools.isRectangleChosen ? 'primary': 'basic'" (click)="addDrawing('rectangle')">
|
<button matTooltip="Add a note" mat-icon-button class="menu-button" [color]="drawTools.isTextChosen ? 'primary': 'basic'" (click)="addDrawing('text')">
|
||||||
<mat-icon>crop_3_2</mat-icon>
|
<mat-icon>create</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button matTooltip="Draw an ellipse" mat-icon-button class="drawer-button" [color]="drawTools.isEllipseChosen ? 'primary': 'basic'" (click)="addDrawing('ellipse')">
|
<button matTooltip="Draw a rectangle" mat-icon-button class="menu-button" [color]="drawTools.isRectangleChosen ? 'primary': 'basic'" (click)="addDrawing('rectangle')">
|
||||||
<mat-icon>panorama_fish_eye</mat-icon>
|
<mat-icon>crop_3_2</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<button matTooltip="Draw line" mat-icon-button class="drawer-button" (click)="addDrawing('line')">
|
<button matTooltip="Draw an ellipse" mat-icon-button class="menu-button" [color]="drawTools.isEllipseChosen ? 'primary': 'basic'" (click)="addDrawing('ellipse')">
|
||||||
<svg height="40" width="40">
|
<mat-icon>panorama_fish_eye</mat-icon>
|
||||||
<line [ngClass]="{selected: drawTools.isLineChosen}" x1="30" y1="10" x2="10" y2="30" style="stroke:white;stroke-width:2" />
|
</button>
|
||||||
</svg>
|
<button matTooltip="Draw line" mat-icon-button class="menu-button" (click)="addDrawing('line')">
|
||||||
</button>
|
<svg height="40" width="40">
|
||||||
<div class="drawer-arrow-button-left">
|
<line [ngClass]="{selected: drawTools.isLineChosen}" x1="30" y1="10" x2="10" y2="30" style="stroke:white;stroke-width:2" />
|
||||||
<button mat-icon-button (click)="drawer.toggle(); hideMenu()">
|
</svg>
|
||||||
<mat-icon>keyboard_arrow_left</mat-icon>
|
</button>
|
||||||
</button>
|
<button class="arrow-button" mat-icon-button (click)="hideMenu()">
|
||||||
</div>
|
<mat-icon>keyboard_arrow_left</mat-icon>
|
||||||
</div>
|
</button>
|
||||||
</mat-drawer>
|
|
||||||
<mat-drawer-content class="drawer-content">
|
|
||||||
<div [ngClass]="{shadow: !drawTools.visibility}" class="drawer-arrow-button-right">
|
|
||||||
<button mat-icon-button (click)="drawer.toggle(); showMenu()">
|
|
||||||
<mat-icon>keyboard_arrow_right</mat-icon>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</mat-drawer-content>
|
|
||||||
</mat-drawer-container>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-node-context-menu [project]="project" [server]="server"></app-node-context-menu>
|
<app-node-context-menu [project]="project" [server]="server"></app-node-context-menu>
|
||||||
@ -145,4 +132,23 @@
|
|||||||
[server]="server">
|
[server]="server">
|
||||||
</app-project-map-shortcuts>
|
</app-project-map-shortcuts>
|
||||||
|
|
||||||
<app-draw-link-tool *ngIf="tools.draw_link"></app-draw-link-tool>
|
<app-draw-link-tool *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>
|
||||||
|
<app-link-created [server]="server" [project]="project"></app-link-created>
|
||||||
|
<app-node-dragged [server]="server"></app-node-dragged>
|
||||||
|
<app-node-label-dragged [server]="server"></app-node-label-dragged>
|
||||||
|
<app-text-added
|
||||||
|
[server]="server"
|
||||||
|
[project]="project"
|
||||||
|
(drawingSaved)="onDrawingSaved()">
|
||||||
|
</app-text-added>
|
||||||
|
<app-text-edited [server]="server"></app-text-edited>
|
||||||
|
@ -18,17 +18,63 @@ g.node:hover {
|
|||||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||||
}
|
}
|
||||||
|
|
||||||
.draw-menu {
|
#show-menu-wrapper {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
top: 20px;
|
top: 20px;
|
||||||
left: 92px;
|
left: 92px;
|
||||||
width: 296px !important;
|
background:#263238;
|
||||||
height: 72px !important;
|
height: 72px;
|
||||||
|
padding-top: 16px;
|
||||||
|
|
||||||
|
.arrow-button {
|
||||||
|
outline: 0!important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.draw-menu button {
|
.shadowed {
|
||||||
outline: none;
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||||
|
}
|
||||||
|
|
||||||
|
#menu-wrapper {
|
||||||
|
position: fixed;
|
||||||
|
background: transparent;
|
||||||
|
top: 20px;
|
||||||
|
left: 92px;
|
||||||
|
background:#263238;
|
||||||
|
height: 72px;
|
||||||
|
padding-top: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
transition: 35s;
|
||||||
|
width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: 0.15s;
|
||||||
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-button {
|
||||||
|
outline: 0!important;
|
||||||
|
transition: 0.5s;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.extended {
|
||||||
|
width: 296px!important;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@-moz-document url-prefix() {
|
@-moz-document url-prefix() {
|
||||||
@ -38,64 +84,10 @@ g.node:hover {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mat-drawer-content {
|
|
||||||
height: auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.drawer-container {
|
|
||||||
height: 72px !important;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shadow {
|
.shadow {
|
||||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||||
}
|
}
|
||||||
|
|
||||||
.drawer {
|
|
||||||
width: 296px !important;
|
|
||||||
height: 72px !important;
|
|
||||||
background:#263238;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-content {
|
|
||||||
background: #F0F0F0!important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-button {
|
|
||||||
width: 40px;
|
|
||||||
margin-right: 12px!important;
|
|
||||||
margin-left: 12px!important;
|
|
||||||
background: #263238;
|
|
||||||
padding: 0;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-arrow-button-right {
|
|
||||||
width: 40px;
|
|
||||||
height: 72px;
|
|
||||||
padding-top: 16px;
|
|
||||||
background:#263238;
|
|
||||||
position: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-arrow-button-left {
|
|
||||||
width: 40px;
|
|
||||||
margin-left: 256px;
|
|
||||||
background:#263238;
|
|
||||||
position: fixed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-buttons {
|
|
||||||
background:#263238;
|
|
||||||
padding-top: 16px;
|
|
||||||
height: 72px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mat-drawer-backdrop.mat-drawer-shown {
|
.mat-drawer-backdrop.mat-drawer-shown {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
@ -113,6 +105,10 @@ g.node:hover {
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
line.selected {
|
||||||
|
stroke: #0097a7!important;
|
||||||
|
}
|
||||||
|
|
||||||
svg.map image:hover, svg.map image.chosen, g.selected {
|
svg.map image:hover, svg.map image.chosen, g.selected {
|
||||||
-webkit-filter: grayscale(100%);
|
-webkit-filter: grayscale(100%);
|
||||||
-moz-filter: grayscale(100%);
|
-moz-filter: grayscale(100%);
|
@ -14,39 +14,41 @@ import { ProjectWebServiceHandler } from '../../handlers/project-web-service-han
|
|||||||
import { MapChangeDetectorRef } from '../../cartography/services/map-change-detector-ref';
|
import { MapChangeDetectorRef } from '../../cartography/services/map-change-detector-ref';
|
||||||
import { NodeWidget } from '../../cartography/widgets/node';
|
import { NodeWidget } from '../../cartography/widgets/node';
|
||||||
import { MapNodeToNodeConverter } from '../../cartography/converters/map/map-node-to-node-converter';
|
import { MapNodeToNodeConverter } from '../../cartography/converters/map/map-node-to-node-converter';
|
||||||
import { MapPortToPortConverter } from '../../cartography/converters/map/map-port-to-port-converter';
|
|
||||||
import { NodesDataSource } from '../../cartography/datasources/nodes-datasource';
|
import { NodesDataSource } from '../../cartography/datasources/nodes-datasource';
|
||||||
import { LinksDataSource } from '../../cartography/datasources/links-datasource';
|
import { LinksDataSource } from '../../cartography/datasources/links-datasource';
|
||||||
import { NodesEventSource } from '../../cartography/events/nodes-event-source';
|
|
||||||
import { DrawingsEventSource } from '../../cartography/events/drawings-event-source';
|
|
||||||
import { LinksEventSource } from '../../cartography/events/links-event-source';
|
|
||||||
import { MapDrawingToSvgConverter } from '../../cartography/converters/map/map-drawing-to-svg-converter';
|
|
||||||
import { DrawingsDataSource } from '../../cartography/datasources/drawings-datasource';
|
import { DrawingsDataSource } from '../../cartography/datasources/drawings-datasource';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { ANGULAR_MAP_DECLARATIONS } from '../../cartography/angular-map.imports';
|
import { ANGULAR_MAP_DECLARATIONS } from '../../cartography/angular-map.imports';
|
||||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { SymbolService } from '../../services/symbol.service';
|
|
||||||
import { MockedSettingsService } from '../../services/settings.service.spec';
|
import { MockedSettingsService } from '../../services/settings.service.spec';
|
||||||
import { MockedServerService } from '../../services/server.service.spec';
|
import { MockedServerService } from '../../services/server.service.spec';
|
||||||
import { MockedProjectService } from '../../services/project.service.spec';
|
import { MockedProjectService } from '../../services/project.service.spec';
|
||||||
import { Observable } from 'rxjs/Rx';
|
import { Observable } from 'rxjs/Rx';
|
||||||
import { Drawing } from '../../cartography/models/drawing';
|
import { Drawing } from '../../cartography/models/drawing';
|
||||||
import { D3MapComponent } from '../../cartography/components/d3-map/d3-map.component';
|
import { D3MapComponent } from '../../cartography/components/d3-map/d3-map.component';
|
||||||
import { Project } from '../../models/project';
|
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { DrawingElement } from '../../cartography/models/drawings/drawing-element';
|
|
||||||
import { RectElement } from '../../cartography/models/drawings/rect-element';
|
|
||||||
import { MapDrawing } from '../../cartography/models/map/map-drawing';
|
|
||||||
import { HttpServer } from '../../services/http-server.service';
|
|
||||||
import { Server } from '../../models/server';
|
import { Server } from '../../models/server';
|
||||||
import { ResizedDataEvent } from '../../cartography/events/event-source';
|
import { Node } from '../../cartography/models/node';
|
||||||
import { MapLabelToLabelConverter } from '../../cartography/converters/map/map-label-to-label-converter';
|
import { ToolsService } from '../../services/tools.service';
|
||||||
|
|
||||||
export class MockedProgressService {
|
export class MockedProgressService {
|
||||||
public activate() {}
|
public activate() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MockedNodeService {
|
||||||
|
public node = { label: {} } as Node;
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
updateLabel(): Observable<Node> {
|
||||||
|
return of(this.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePosition(): Observable<Node> {
|
||||||
|
return of(this.node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class MockedDrawingService {
|
export class MockedDrawingService {
|
||||||
public drawing = {} as Drawing;
|
public drawing = {} as Drawing;
|
||||||
constructor() {}
|
constructor() {}
|
||||||
@ -70,6 +72,22 @@ export class MockedDrawingService {
|
|||||||
delete(_server: Server, _drawing: Drawing){
|
delete(_server: Server, _drawing: Drawing){
|
||||||
return of(this.drawing);
|
return of(this.drawing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateText(_server: Server, _drawing: Drawing, _svg: string): Observable<Drawing> {
|
||||||
|
return of(this.drawing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MockedLinkService {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
createLink() {
|
||||||
|
return of({});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNodes(){
|
||||||
|
return of({});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MockedDrawingsDataSource {
|
export class MockedDrawingsDataSource {
|
||||||
@ -80,6 +98,14 @@ export class MockedDrawingsDataSource {
|
|||||||
update() { return of({})}
|
update() { return of({})}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MockedNodesDataSource {
|
||||||
|
add() {}
|
||||||
|
|
||||||
|
get() { return of({})}
|
||||||
|
|
||||||
|
update() { return of({})}
|
||||||
|
}
|
||||||
|
|
||||||
describe('ProjectMapComponent', () => {
|
describe('ProjectMapComponent', () => {
|
||||||
let component: ProjectMapComponent;
|
let component: ProjectMapComponent;
|
||||||
let fixture: ComponentFixture<ProjectMapComponent>;
|
let fixture: ComponentFixture<ProjectMapComponent>;
|
||||||
@ -100,7 +126,6 @@ describe('ProjectMapComponent', () => {
|
|||||||
{ provide: ActivatedRoute },
|
{ provide: ActivatedRoute },
|
||||||
{ provide: ServerService, useClass: MockedServerService },
|
{ provide: ServerService, useClass: MockedServerService },
|
||||||
{ provide: ProjectService, useClass: MockedProjectService },
|
{ provide: ProjectService, useClass: MockedProjectService },
|
||||||
{ provide: SymbolService },
|
|
||||||
{ provide: NodeService },
|
{ provide: NodeService },
|
||||||
{ provide: LinkService },
|
{ provide: LinkService },
|
||||||
{ provide: DrawingService, useValue: drawingService},
|
{ provide: DrawingService, useValue: drawingService},
|
||||||
@ -109,18 +134,11 @@ describe('ProjectMapComponent', () => {
|
|||||||
{ provide: MapChangeDetectorRef },
|
{ provide: MapChangeDetectorRef },
|
||||||
{ provide: NodeWidget },
|
{ provide: NodeWidget },
|
||||||
{ provide: MapNodeToNodeConverter },
|
{ provide: MapNodeToNodeConverter },
|
||||||
{ provide: MapPortToPortConverter },
|
|
||||||
{ provide: NodesDataSource },
|
{ provide: NodesDataSource },
|
||||||
{ provide: LinksDataSource },
|
{ provide: LinksDataSource },
|
||||||
{ provide: DrawingsDataSource, useValue: drawingsDataSource},
|
{ provide: DrawingsDataSource, useValue: drawingsDataSource},
|
||||||
{ provide: NodesEventSource },
|
|
||||||
{ provide: DrawingsEventSource },
|
|
||||||
{ provide: LinksEventSource },
|
|
||||||
{ provide: MapDrawingToSvgConverter, useValue: {
|
|
||||||
convert: () => { return ''}
|
|
||||||
} },
|
|
||||||
{ provide: SettingsService, useClass: MockedSettingsService },
|
{ provide: SettingsService, useClass: MockedSettingsService },
|
||||||
{ provide: MapLabelToLabelConverter}
|
{ provide: ToolsService }
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
ProjectMapComponent,
|
ProjectMapComponent,
|
||||||
@ -143,47 +161,6 @@ describe('ProjectMapComponent', () => {
|
|||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update position on resizing event', () => {
|
|
||||||
let drawingElement: DrawingElement;
|
|
||||||
let rect = new RectElement();
|
|
||||||
rect.fill = "#ffffff";
|
|
||||||
rect.fill_opacity = 1.0;
|
|
||||||
rect.stroke = "#000000";
|
|
||||||
rect.stroke_width = 2;
|
|
||||||
rect.width = 200;
|
|
||||||
rect.height = 100;
|
|
||||||
drawingElement = rect;
|
|
||||||
let mapDrawing = new MapDrawing;
|
|
||||||
mapDrawing.id = '1';
|
|
||||||
mapDrawing.projectId = '1';
|
|
||||||
mapDrawing.rotation = 1;
|
|
||||||
mapDrawing.svg = '';
|
|
||||||
mapDrawing.x = 0;
|
|
||||||
mapDrawing.y = 0;
|
|
||||||
mapDrawing.z = 0;
|
|
||||||
mapDrawing.element = drawingElement;
|
|
||||||
let event = new ResizedDataEvent<MapDrawing>(mapDrawing,0,0,100,100);
|
|
||||||
spyOn(drawingService, 'updateSizeAndPosition').and.returnValue( Observable.of({}));
|
|
||||||
|
|
||||||
component.onDrawingResized(event);
|
|
||||||
|
|
||||||
expect(drawingService.updateSizeAndPosition).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should add selected drawing', () => {
|
|
||||||
component.mapChild = { context: {} } as D3MapComponent;
|
|
||||||
component.project = { project_id: "1" } as Project;
|
|
||||||
component.mapChild.context.getZeroZeroTransformationPoint = jasmine.createSpy('HTML element').and.callFake(() => { return {x: 0, y: 0}});
|
|
||||||
var dummyElement = document.createElement('map');
|
|
||||||
document.getElementsByClassName = jasmine.createSpy('HTML element').and.callFake(() => { return ([dummyElement])});
|
|
||||||
spyOn(drawingsDataSource, 'add');
|
|
||||||
|
|
||||||
component.addDrawing("rectangle");
|
|
||||||
dummyElement.click();
|
|
||||||
|
|
||||||
expect(drawingsDataSource.add).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should hide draw tools when hide menu is called', () => {
|
it('should hide draw tools when hide menu is called', () => {
|
||||||
var dummyElement = document.createElement('map');
|
var dummyElement = document.createElement('map');
|
||||||
document.getElementsByClassName = jasmine.createSpy('HTML element').and.callFake(() => { return ([dummyElement])});
|
document.getElementsByClassName = jasmine.createSpy('HTML element').and.callFake(() => { return ([dummyElement])});
|
||||||
@ -194,9 +171,11 @@ describe('ProjectMapComponent', () => {
|
|||||||
expect(component.resetDrawToolChoice).toHaveBeenCalled();
|
expect(component.resetDrawToolChoice).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return drawing mock of correct type'), () => {
|
it('should reset choice on draw menu after saving drawing', () => {
|
||||||
let mock = component.getDrawingMock('rectangle');
|
spyOn(component, 'resetDrawToolChoice');
|
||||||
|
|
||||||
expect(mock instanceof RectElement).toBe(true);
|
component.onDrawingSaved();
|
||||||
}
|
|
||||||
|
expect(component.resetDrawToolChoice).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -16,7 +16,6 @@ import { NodeContextMenuComponent } from "./node-context-menu/node-context-menu.
|
|||||||
import { Template } from "../../models/template";
|
import { Template } from "../../models/template";
|
||||||
import { NodeService } from "../../services/node.service";
|
import { NodeService } from "../../services/node.service";
|
||||||
import { Symbol } from "../../models/symbol";
|
import { Symbol } from "../../models/symbol";
|
||||||
import { LinkService } from "../../services/link.service";
|
|
||||||
import { NodesDataSource } from "../../cartography/datasources/nodes-datasource";
|
import { NodesDataSource } from "../../cartography/datasources/nodes-datasource";
|
||||||
import { LinksDataSource } from "../../cartography/datasources/links-datasource";
|
import { LinksDataSource } from "../../cartography/datasources/links-datasource";
|
||||||
import { ProjectWebServiceHandler } from "../../handlers/project-web-service-handler";
|
import { ProjectWebServiceHandler } from "../../handlers/project-web-service-handler";
|
||||||
@ -24,36 +23,19 @@ import { DrawingsDataSource } from "../../cartography/datasources/drawings-datas
|
|||||||
import { ProgressService } from "../../common/progress/progress.service";
|
import { ProgressService } from "../../common/progress/progress.service";
|
||||||
import { MapChangeDetectorRef } from '../../cartography/services/map-change-detector-ref';
|
import { MapChangeDetectorRef } from '../../cartography/services/map-change-detector-ref';
|
||||||
import { NodeContextMenu } from '../../cartography/events/nodes';
|
import { NodeContextMenu } from '../../cartography/events/nodes';
|
||||||
import { MapLinkCreated } from '../../cartography/events/links';
|
|
||||||
import { NodeWidget } from '../../cartography/widgets/node';
|
import { NodeWidget } from '../../cartography/widgets/node';
|
||||||
import { DraggedDataEvent, ResizedDataEvent, TextEditedDataEvent } from '../../cartography/events/event-source';
|
|
||||||
import { DrawingService } from '../../services/drawing.service';
|
import { DrawingService } from '../../services/drawing.service';
|
||||||
import { MapNodeToNodeConverter } from '../../cartography/converters/map/map-node-to-node-converter';
|
import { MapNodeToNodeConverter } from '../../cartography/converters/map/map-node-to-node-converter';
|
||||||
import { NodesEventSource } from '../../cartography/events/nodes-event-source';
|
|
||||||
import { DrawingsEventSource } from '../../cartography/events/drawings-event-source';
|
|
||||||
import { MapNode } from '../../cartography/models/map/map-node';
|
|
||||||
import { LinksEventSource } from '../../cartography/events/links-event-source';
|
|
||||||
import { MapDrawing } from '../../cartography/models/map/map-drawing';
|
|
||||||
import { MapPortToPortConverter } from '../../cartography/converters/map/map-port-to-port-converter';
|
|
||||||
import { MapDrawingToSvgConverter } from '../../cartography/converters/map/map-drawing-to-svg-converter';
|
|
||||||
import { DrawingElement } from '../../cartography/models/drawings/drawing-element';
|
|
||||||
import { RectElement } from '../../cartography/models/drawings/rect-element';
|
|
||||||
import { EllipseElement } from '../../cartography/models/drawings/ellipse-element';
|
|
||||||
import { LineElement } from '../../cartography/models/drawings/line-element';
|
|
||||||
import { SettingsService, Settings } from '../../services/settings.service';
|
import { SettingsService, Settings } from '../../services/settings.service';
|
||||||
import { MapLabel } from '../../cartography/models/map/map-label';
|
|
||||||
import { D3MapComponent } from '../../cartography/components/d3-map/d3-map.component';
|
import { D3MapComponent } from '../../cartography/components/d3-map/d3-map.component';
|
||||||
import { MapLinkNode } from '../../cartography/models/map/map-link-node';
|
import { ToolsService } from '../../services/tools.service';
|
||||||
import { TextElement } from '../../cartography/models/drawings/text-element';
|
|
||||||
import { MapLabelToLabelConverter } from '../../cartography/converters/map/map-label-to-label-converter';
|
|
||||||
import { select } from 'd3-selection';
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-project-map',
|
selector: 'app-project-map',
|
||||||
encapsulation: ViewEncapsulation.None,
|
encapsulation: ViewEncapsulation.None,
|
||||||
templateUrl: './project-map.component.html',
|
templateUrl: './project-map.component.html',
|
||||||
styleUrls: ['./project-map.component.css'],
|
styleUrls: ['./project-map.component.scss'],
|
||||||
})
|
})
|
||||||
export class ProjectMapComponent implements OnInit, OnDestroy {
|
export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||||
public nodes: Node[] = [];
|
public nodes: Node[] = [];
|
||||||
@ -62,7 +44,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
public symbols: Symbol[] = [];
|
public symbols: Symbol[] = [];
|
||||||
public project: Project;
|
public project: Project;
|
||||||
public server: Server;
|
public server: Server;
|
||||||
private drawListener: Function;
|
public selectedDrawing: string;
|
||||||
private ws: Subject<any>;
|
private ws: Subject<any>;
|
||||||
|
|
||||||
tools = {
|
tools = {
|
||||||
@ -78,12 +60,10 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
'isRectangleChosen': false,
|
'isRectangleChosen': false,
|
||||||
'isEllipseChosen': false,
|
'isEllipseChosen': false,
|
||||||
'isLineChosen': false,
|
'isLineChosen': false,
|
||||||
'visibility': false,
|
'isTextChosen': false,
|
||||||
'isAddingTextChosen': false
|
'visibility': false
|
||||||
};
|
};
|
||||||
|
|
||||||
protected selectedDrawing: string;
|
|
||||||
|
|
||||||
private inReadOnlyMode = false;
|
private inReadOnlyMode = false;
|
||||||
|
|
||||||
@ViewChild(NodeContextMenuComponent) nodeContextMenu: NodeContextMenuComponent;
|
@ViewChild(NodeContextMenuComponent) nodeContextMenu: NodeContextMenuComponent;
|
||||||
@ -96,23 +76,17 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
private serverService: ServerService,
|
private serverService: ServerService,
|
||||||
private projectService: ProjectService,
|
private projectService: ProjectService,
|
||||||
private nodeService: NodeService,
|
private nodeService: NodeService,
|
||||||
private linkService: LinkService,
|
|
||||||
public drawingService: DrawingService,
|
public drawingService: DrawingService,
|
||||||
private progressService: ProgressService,
|
private progressService: ProgressService,
|
||||||
private projectWebServiceHandler: ProjectWebServiceHandler,
|
private projectWebServiceHandler: ProjectWebServiceHandler,
|
||||||
private mapChangeDetectorRef: MapChangeDetectorRef,
|
private mapChangeDetectorRef: MapChangeDetectorRef,
|
||||||
private nodeWidget: NodeWidget,
|
private nodeWidget: NodeWidget,
|
||||||
private mapNodeToNode: MapNodeToNodeConverter,
|
private mapNodeToNode: MapNodeToNodeConverter,
|
||||||
private mapPortToPort: MapPortToPortConverter,
|
|
||||||
private nodesDataSource: NodesDataSource,
|
private nodesDataSource: NodesDataSource,
|
||||||
private linksDataSource: LinksDataSource,
|
private linksDataSource: LinksDataSource,
|
||||||
private drawingsDataSource: DrawingsDataSource,
|
private drawingsDataSource: DrawingsDataSource,
|
||||||
private nodesEventSource: NodesEventSource,
|
|
||||||
private drawingsEventSource: DrawingsEventSource,
|
|
||||||
private linksEventSource: LinksEventSource,
|
|
||||||
private mapDrawingToSvgConverter: MapDrawingToSvgConverter,
|
|
||||||
private settingsService: SettingsService,
|
private settingsService: SettingsService,
|
||||||
private mapLabelToLabel: MapLabelToLabelConverter
|
private toolsService: ToolsService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@ -177,34 +151,6 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
this.mapChangeDetectorRef.detectChanges();
|
this.mapChangeDetectorRef.detectChanges();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
this.subscriptions.push(
|
|
||||||
this.nodesEventSource.dragged.subscribe((evt) => this.onNodeDragged(evt))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.subscriptions.push(
|
|
||||||
this.nodesEventSource.labelDragged.subscribe((evt) => this.onNodeLabelDragged(evt))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.subscriptions.push(
|
|
||||||
this.drawingsEventSource.dragged.subscribe((evt) => this.onDrawingDragged(evt))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.subscriptions.push(
|
|
||||||
this.drawingsEventSource.resized.subscribe((evt) => this.onDrawingResized(evt))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.subscriptions.push(
|
|
||||||
this.drawingsEventSource.textEdited.subscribe((evt) => this.onTextEdited(evt))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.subscriptions.push(
|
|
||||||
this.linksEventSource.created.subscribe((evt) => this.onLinkCreated(evt))
|
|
||||||
);
|
|
||||||
|
|
||||||
this.subscriptions.push(
|
|
||||||
this.linksEventSource.interfaceDragged.subscribe((evt) => this.onInterfaceLabelDragged(evt))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onProjectLoad(project: Project) {
|
onProjectLoad(project: Project) {
|
||||||
@ -225,7 +171,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
.subscribe((drawings: Drawing[]) => {
|
.subscribe((drawings: Drawing[]) => {
|
||||||
this.drawingsDataSource.set(drawings);
|
this.drawingsDataSource.set(drawings);
|
||||||
|
|
||||||
this.setUpMapCallbacks(project);
|
this.setUpMapCallbacks();
|
||||||
this.setUpWS(project);
|
this.setUpWS(project);
|
||||||
|
|
||||||
this.progressService.deactivate();
|
this.progressService.deactivate();
|
||||||
@ -242,7 +188,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setUpMapCallbacks(project: Project) {
|
setUpMapCallbacks() {
|
||||||
const onContextMenu = this.nodeWidget.onContextMenu.subscribe((eventNode: NodeContextMenu) => {
|
const onContextMenu = this.nodeWidget.onContextMenu.subscribe((eventNode: NodeContextMenu) => {
|
||||||
const node = this.mapNodeToNode.convert(eventNode.node);
|
const node = this.mapNodeToNode.convert(eventNode.node);
|
||||||
this.nodeContextMenu.open(
|
this.nodeContextMenu.open(
|
||||||
@ -259,120 +205,27 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
onNodeCreation(template: Template) {
|
onNodeCreation(template: Template) {
|
||||||
this.nodeService
|
this.nodeService
|
||||||
.createFromTemplate(this.server, this.project, template, 0, 0, 'local')
|
.createFromTemplate(this.server, this.project, template, 0, 0, 'local')
|
||||||
.subscribe((createdNode: Node) => {
|
|
||||||
this.projectService
|
|
||||||
.nodes(this.server, this.project.project_id)
|
|
||||||
.subscribe((nodes: Node[]) => {
|
|
||||||
this.nodesDataSource.set(nodes);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private onNodeDragged(draggedEvent: DraggedDataEvent<MapNode>) {
|
|
||||||
const node = this.nodesDataSource.get(draggedEvent.datum.id);
|
|
||||||
node.x += draggedEvent.dx;
|
|
||||||
node.y += draggedEvent.dy;
|
|
||||||
|
|
||||||
this.nodeService
|
|
||||||
.updatePosition(this.server, node, node.x, node.y)
|
|
||||||
.subscribe((serverNode: Node) => {
|
|
||||||
this.nodesDataSource.update(serverNode);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private onNodeLabelDragged(draggedEvent: DraggedDataEvent<MapLabel>) {
|
|
||||||
const node = this.nodesDataSource.get(draggedEvent.datum.nodeId);
|
|
||||||
const mapLabel = draggedEvent.datum;
|
|
||||||
mapLabel.x += draggedEvent.dx;
|
|
||||||
mapLabel.y += draggedEvent.dy;
|
|
||||||
|
|
||||||
const label = this.mapLabelToLabel.convert(mapLabel);
|
|
||||||
node.label = label;
|
|
||||||
|
|
||||||
this.nodeService
|
|
||||||
.updateLabel(this.server, node, node.label)
|
|
||||||
.subscribe((serverNode: Node) => {
|
|
||||||
this.nodesDataSource.update(serverNode);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private onDrawingDragged(draggedEvent: DraggedDataEvent<MapDrawing>) {
|
|
||||||
const drawing = this.drawingsDataSource.get(draggedEvent.datum.id);
|
|
||||||
drawing.x += draggedEvent.dx;
|
|
||||||
drawing.y += draggedEvent.dy;
|
|
||||||
|
|
||||||
this.drawingService
|
|
||||||
.updatePosition(this.server, drawing, drawing.x, drawing.y)
|
|
||||||
.subscribe((serverDrawing: Drawing) => {
|
|
||||||
this.drawingsDataSource.update(serverDrawing);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private onInterfaceLabelDragged(draggedEvent: DraggedDataEvent<MapLinkNode>) {
|
|
||||||
const link = this.linksDataSource.get(draggedEvent.datum.linkId);
|
|
||||||
if (link.nodes[0].node_id === draggedEvent.datum.nodeId) {
|
|
||||||
link.nodes[0].label.x += draggedEvent.dx;
|
|
||||||
link.nodes[0].label.y += draggedEvent.dy;
|
|
||||||
}
|
|
||||||
if (link.nodes[1].node_id === draggedEvent.datum.nodeId) {
|
|
||||||
link.nodes[1].label.x += draggedEvent.dx;
|
|
||||||
link.nodes[1].label.y += draggedEvent.dy;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.linkService
|
|
||||||
.updateNodes(this.server, link, link.nodes)
|
|
||||||
.subscribe((serverLink: Link) => {
|
|
||||||
this.linksDataSource.update(serverLink);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private onLinkCreated(linkCreated: MapLinkCreated) {
|
|
||||||
const sourceNode = this.mapNodeToNode.convert(linkCreated.sourceNode);
|
|
||||||
const sourcePort = this.mapPortToPort.convert(linkCreated.sourcePort);
|
|
||||||
const targetNode = this.mapNodeToNode.convert(linkCreated.targetNode);
|
|
||||||
const targetPort = this.mapPortToPort.convert(linkCreated.targetPort);
|
|
||||||
|
|
||||||
this.linkService
|
|
||||||
.createLink(this.server, sourceNode, sourcePort, targetNode, targetPort)
|
|
||||||
.subscribe(() => {
|
.subscribe(() => {
|
||||||
this.projectService.links(this.server, this.project.project_id).subscribe((links: Link[]) => {
|
this.projectService
|
||||||
this.linksDataSource.set(links);
|
.nodes(this.server, this.project.project_id)
|
||||||
|
.subscribe((nodes: Node[]) => {
|
||||||
|
this.nodesDataSource.set(nodes);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public onDrawingResized(resizedEvent: ResizedDataEvent<MapDrawing>) {
|
public onDrawingSaved(){
|
||||||
const drawing = this.drawingsDataSource.get(resizedEvent.datum.id);
|
this.resetDrawToolChoice();
|
||||||
let svgString = this.mapDrawingToSvgConverter.convert(resizedEvent.datum);
|
|
||||||
|
|
||||||
this.drawingService
|
|
||||||
.updateSizeAndPosition(this.server, drawing, resizedEvent.x, resizedEvent.y, svgString)
|
|
||||||
.subscribe((serverDrawing: Drawing) => {
|
|
||||||
this.drawingsDataSource.update(serverDrawing);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public onTextEdited(evt: TextEditedDataEvent){
|
|
||||||
let mapDrawing: MapDrawing = new MapDrawing();
|
|
||||||
mapDrawing.element = evt.textElement;
|
|
||||||
(mapDrawing.element as TextElement).text = evt.editedText;
|
|
||||||
let svgString = this.mapDrawingToSvgConverter.convert(mapDrawing);
|
|
||||||
|
|
||||||
let drawing = this.drawingsDataSource.get(evt.textDrawingId);
|
|
||||||
|
|
||||||
this.drawingService
|
|
||||||
.updateText(this.server, drawing, svgString)
|
|
||||||
.subscribe((serverDrawing: Drawing) => {
|
|
||||||
this.drawingsDataSource.update(serverDrawing);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public set readonly(value) {
|
public set readonly(value) {
|
||||||
this.inReadOnlyMode = value;
|
this.inReadOnlyMode = value;
|
||||||
if (value) {
|
if (value) {
|
||||||
this.tools.selection = false;
|
this.tools.selection = false;
|
||||||
|
this.toolsService.selectionToolActivation(false);
|
||||||
} else {
|
} else {
|
||||||
this.tools.selection = true;
|
this.tools.selection = true;
|
||||||
|
this.toolsService.selectionToolActivation(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,13 +235,16 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
public toggleMovingMode() {
|
public toggleMovingMode() {
|
||||||
this.tools.moving = !this.tools.moving;
|
this.tools.moving = !this.tools.moving;
|
||||||
|
this.toolsService.movingToolActivation(this.tools.moving);
|
||||||
if (!this.readonly) {
|
if (!this.readonly) {
|
||||||
this.tools.selection = !this.tools.moving;
|
this.tools.selection = !this.tools.moving;
|
||||||
|
this.toolsService.selectionToolActivation(this.tools.selection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleDrawLineMode() {
|
public toggleDrawLineMode() {
|
||||||
this.tools.draw_link = !this.tools.draw_link;
|
this.tools.draw_link = !this.tools.draw_link;
|
||||||
|
this.toolsService.drawLinkToolActivation(this.tools.draw_link);
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleShowInterfaceLabels(enabled: boolean) {
|
public toggleShowInterfaceLabels(enabled: boolean) {
|
||||||
@ -396,193 +252,58 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public addDrawing(selectedObject: string) {
|
public addDrawing(selectedObject: string) {
|
||||||
if (selectedObject === this.selectedDrawing){
|
|
||||||
var map = document.getElementsByClassName('map')[0];
|
|
||||||
map.removeEventListener('click', this.drawListener as EventListenerOrEventListenerObject);
|
|
||||||
this.resetDrawToolChoice();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (selectedObject) {
|
switch (selectedObject) {
|
||||||
case "rectangle":
|
case "rectangle":
|
||||||
this.drawTools.isAddingTextChosen = false;
|
this.drawTools.isTextChosen = false;
|
||||||
this.drawTools.isEllipseChosen = false;
|
this.drawTools.isEllipseChosen = false;
|
||||||
this.drawTools.isRectangleChosen = !this.drawTools.isRectangleChosen;
|
this.drawTools.isRectangleChosen = !this.drawTools.isRectangleChosen;
|
||||||
this.drawTools.isLineChosen = false;
|
this.drawTools.isLineChosen = false;
|
||||||
break;
|
break;
|
||||||
case "ellipse":
|
case "ellipse":
|
||||||
this.drawTools.isAddingTextChosen = false;
|
this.drawTools.isTextChosen = false;
|
||||||
this.drawTools.isEllipseChosen = !this.drawTools.isEllipseChosen;
|
this.drawTools.isEllipseChosen = !this.drawTools.isEllipseChosen;
|
||||||
this.drawTools.isRectangleChosen = false;
|
this.drawTools.isRectangleChosen = false;
|
||||||
this.drawTools.isLineChosen = false;
|
this.drawTools.isLineChosen = false;
|
||||||
break;
|
break;
|
||||||
case "line":
|
case "line":
|
||||||
this.drawTools.isAddingTextChosen = false;
|
this.drawTools.isTextChosen = false;
|
||||||
this.drawTools.isEllipseChosen = false;
|
this.drawTools.isEllipseChosen = false;
|
||||||
this.drawTools.isRectangleChosen = false;
|
this.drawTools.isRectangleChosen = false;
|
||||||
this.drawTools.isLineChosen = !this.drawTools.isLineChosen;
|
this.drawTools.isLineChosen = !this.drawTools.isLineChosen;
|
||||||
break;
|
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 = selectedObject;
|
this.selectedDrawing = this.selectedDrawing === selectedObject ? "" : selectedObject;
|
||||||
var map = document.getElementsByClassName('map')[0];
|
|
||||||
let mapDrawing: MapDrawing = this.getDrawingMock(selectedObject);
|
|
||||||
|
|
||||||
let listener = (event: MouseEvent) => {
|
|
||||||
let x = event.clientX - this.mapChild.context.getZeroZeroTransformationPoint().x;
|
|
||||||
let y = event.clientY - this.mapChild.context.getZeroZeroTransformationPoint().y;
|
|
||||||
let svg = this.mapDrawingToSvgConverter.convert(mapDrawing);
|
|
||||||
|
|
||||||
this.drawingService
|
|
||||||
.add(this.server, this.project.project_id, x, y, svg)
|
|
||||||
.subscribe((serverDrawing: Drawing) => {
|
|
||||||
this.drawingsDataSource.add(serverDrawing);
|
|
||||||
});
|
|
||||||
this.resetDrawToolChoice();
|
|
||||||
}
|
|
||||||
|
|
||||||
map.removeEventListener('click', this.drawListener as EventListenerOrEventListenerObject);
|
|
||||||
this.drawListener = listener;
|
|
||||||
map.addEventListener('click', this.drawListener as EventListenerOrEventListenerObject, {once : true});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public resetDrawToolChoice(){
|
public resetDrawToolChoice() {
|
||||||
this.drawTools.isRectangleChosen = false;
|
this.drawTools.isRectangleChosen = false;
|
||||||
this.drawTools.isEllipseChosen = false;
|
this.drawTools.isEllipseChosen = false;
|
||||||
this.drawTools.isLineChosen = false;
|
this.drawTools.isLineChosen = false;
|
||||||
this.drawTools.isAddingTextChosen = false;
|
this.drawTools.isTextChosen = false;
|
||||||
this.selectedDrawing = "";
|
this.selectedDrawing = "";
|
||||||
|
this.toolsService.textAddingToolActivation(this.drawTools.isTextChosen);
|
||||||
}
|
}
|
||||||
|
|
||||||
public hideMenu(){
|
public hideMenu(){
|
||||||
var map = document.getElementsByClassName('map')[0];
|
|
||||||
map.removeEventListener('click', this.drawListener as EventListenerOrEventListenerObject);
|
|
||||||
this.resetDrawToolChoice();
|
this.resetDrawToolChoice();
|
||||||
this.drawTools.visibility = false;
|
this.drawTools.visibility = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public showMenu(){
|
public showMenu() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.drawTools.visibility = true;
|
this.drawTools.visibility = true;
|
||||||
},
|
},
|
||||||
200);
|
200);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDrawingMock(objectType: string, text?: string): MapDrawing {
|
|
||||||
let drawingElement: DrawingElement;
|
|
||||||
|
|
||||||
switch (objectType) {
|
|
||||||
case "rectangle":
|
|
||||||
let rectElement = new RectElement();
|
|
||||||
rectElement.fill = "#ffffff";
|
|
||||||
rectElement.fill_opacity = 1.0;
|
|
||||||
rectElement.stroke = "#000000";
|
|
||||||
rectElement.stroke_width = 2;
|
|
||||||
rectElement.width = 200;
|
|
||||||
rectElement.height = 100;
|
|
||||||
drawingElement = rectElement;
|
|
||||||
break;
|
|
||||||
case "ellipse":
|
|
||||||
let ellipseElement = new EllipseElement();
|
|
||||||
ellipseElement.fill = "#ffffff";
|
|
||||||
ellipseElement.fill_opacity = 1.0;
|
|
||||||
ellipseElement.stroke = "#000000";
|
|
||||||
ellipseElement.stroke_width = 2;
|
|
||||||
ellipseElement.cx = 100;
|
|
||||||
ellipseElement.cy = 100;
|
|
||||||
ellipseElement.rx = 100;
|
|
||||||
ellipseElement.ry = 100;
|
|
||||||
ellipseElement.width = 200;
|
|
||||||
ellipseElement.height = 200;
|
|
||||||
drawingElement = ellipseElement;
|
|
||||||
break;
|
|
||||||
case "line":
|
|
||||||
let lineElement = new LineElement();
|
|
||||||
lineElement.stroke = "#000000";
|
|
||||||
lineElement.stroke_width = 2;
|
|
||||||
lineElement.x1 = 0;
|
|
||||||
lineElement.x2 = 200;
|
|
||||||
lineElement.y1 = 0;
|
|
||||||
lineElement.y2 = 0;
|
|
||||||
lineElement.width = 100;
|
|
||||||
lineElement.height = 0;
|
|
||||||
drawingElement = lineElement;
|
|
||||||
break;
|
|
||||||
case "text":
|
|
||||||
let textElement = new TextElement();
|
|
||||||
textElement.height = 100; //should be calculated
|
|
||||||
textElement.width = 100;
|
|
||||||
textElement.text = text;
|
|
||||||
textElement.fill = "#000000";
|
|
||||||
textElement.fill_opacity = 0;
|
|
||||||
textElement.font_family = "Noto Sans";
|
|
||||||
textElement.font_size = 11;
|
|
||||||
textElement.font_weight = "bold";
|
|
||||||
drawingElement = textElement;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mapDrawing = new MapDrawing();
|
|
||||||
mapDrawing.element = drawingElement;
|
|
||||||
return mapDrawing;
|
|
||||||
}
|
|
||||||
|
|
||||||
public addText(){
|
|
||||||
if (!this.drawTools.isAddingTextChosen){
|
|
||||||
this.resetDrawToolChoice();
|
|
||||||
this.drawTools.isAddingTextChosen = true;
|
|
||||||
var map = document.getElementsByClassName('map')[0];
|
|
||||||
|
|
||||||
let addTextListener = (event: MouseEvent) => {
|
|
||||||
|
|
||||||
var div = document.createElement('div');
|
|
||||||
div.style.width = "fit-content";
|
|
||||||
div.style.left = event.clientX.toString() + 'px';
|
|
||||||
div.style.top = (event.clientY - 10).toString() + 'px';
|
|
||||||
div.style.position = "absolute";
|
|
||||||
div.style.zIndex = "99";
|
|
||||||
|
|
||||||
div.style.fontFamily = "Noto Sans";
|
|
||||||
div.style.fontSize = "11pt";
|
|
||||||
div.style.fontWeight = "bold";
|
|
||||||
div.style.color = "#000000";
|
|
||||||
|
|
||||||
div.setAttribute("contenteditable", "true");
|
|
||||||
|
|
||||||
document.body.appendChild(div);
|
|
||||||
div.innerText = "";
|
|
||||||
div.focus();
|
|
||||||
document.body.style.cursor = "text";
|
|
||||||
|
|
||||||
div.addEventListener("focusout", () => {
|
|
||||||
let savedText = div.innerText;
|
|
||||||
|
|
||||||
let drawing = this.getDrawingMock("text", savedText);
|
|
||||||
(drawing.element as TextElement).text = savedText;
|
|
||||||
let svgText = this.mapDrawingToSvgConverter.convert(drawing);
|
|
||||||
|
|
||||||
this.drawingService
|
|
||||||
.add(this.server, this.project.project_id, event.clientX - this.mapChild.context.getZeroZeroTransformationPoint().x, event.clientY - this.mapChild.context.getZeroZeroTransformationPoint().y, svgText)
|
|
||||||
.subscribe((serverDrawing: Drawing) => {
|
|
||||||
document.body.style.cursor = "default";
|
|
||||||
div.remove();
|
|
||||||
this.drawingsDataSource.add(serverDrawing);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.resetDrawToolChoice();
|
|
||||||
}
|
|
||||||
|
|
||||||
map.removeEventListener('click', this.drawListener as EventListenerOrEventListenerObject);
|
|
||||||
this.drawListener = addTextListener;
|
|
||||||
map.addEventListener('click', this.drawListener as EventListenerOrEventListenerObject, {once : true});
|
|
||||||
} else {
|
|
||||||
this.resetDrawToolChoice();
|
|
||||||
var map = document.getElementsByClassName('map')[0];
|
|
||||||
map.removeEventListener('click', this.drawListener as EventListenerOrEventListenerObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
this.drawingsDataSource.clear();
|
this.drawingsDataSource.clear();
|
||||||
this.nodesDataSource.clear();
|
this.nodesDataSource.clear();
|
||||||
|
@ -17,7 +17,8 @@ export class DrawingService {
|
|||||||
.post<Drawing>(server, `/projects/${project_id}/drawings`, {
|
.post<Drawing>(server, `/projects/${project_id}/drawings`, {
|
||||||
'svg': svg,
|
'svg': svg,
|
||||||
'x': Math.round(x),
|
'x': Math.round(x),
|
||||||
'y': Math.round(y)
|
'y': Math.round(y),
|
||||||
|
'z': 1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,10 @@ export class MockedProjectService {
|
|||||||
isReadOnly(project) {
|
isReadOnly(project) {
|
||||||
return project.readonly;
|
return project.readonly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
links(server: Server, project_id: string) {
|
||||||
|
return of([]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
0
src/app/services/tools.service.spec.ts
Normal file
0
src/app/services/tools.service.spec.ts
Normal file
33
src/app/services/tools.service.ts
Normal file
33
src/app/services/tools.service.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ToolsService {
|
||||||
|
public isSelectionToolActivated = new Subject<boolean>();
|
||||||
|
public isMovingToolActivated = new Subject<boolean>();
|
||||||
|
public isTextEditingToolActivated = new Subject<boolean>();
|
||||||
|
public isTextAddingToolActivated = new Subject<boolean>();
|
||||||
|
public isDrawLinkToolActivated = new Subject<boolean>();
|
||||||
|
|
||||||
|
constructor(){}
|
||||||
|
|
||||||
|
selectionToolActivation(value: boolean){
|
||||||
|
this.isSelectionToolActivated.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
movingToolActivation(value: boolean){
|
||||||
|
this.isMovingToolActivated.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
textEditingToolActivation(value: boolean){
|
||||||
|
this.isTextEditingToolActivated.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
textAddingToolActivation(value: boolean){
|
||||||
|
this.isTextAddingToolActivated.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawLinkToolActivation(value: boolean){
|
||||||
|
this.isDrawLinkToolActivated.next(value);
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,12 @@ a.table-link {
|
|||||||
color: white!important;
|
color: white!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@-moz-document url-prefix() {
|
||||||
|
.temporaryElement{
|
||||||
|
line-height: 1.4em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
app-root {
|
app-root {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user